1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! HTML の組み立て。テンプレートエンジンは使わず、文字列を順に足していく。
//! 出力に改行を入れないので、返した HTML はそのまま 1 行になる。
/// ページに要る CSS
const PAGE_STYLE: &str = "body{margin:0;padding:56px 40px 96px;color:#1f1f1f;background:#fff;\
font-family:system-ui,-apple-system,sans-serif;font-size:14px;line-height:1.9}\
h1{font-size:19px;font-weight:500;margin:0 0 8px}\
p{max-width:44em;margin:0 0 24px;color:#767676}\
ul{list-style:none;margin:0;padding:0}\
li{position:relative;padding-left:16px;margin-bottom:4px}\
li::before{content:'';position:absolute;left:0;top:12px;width:2px;height:2px;\
background:#767676;border-radius:50%}\
li.done{color:#767676}\
.note{color:#767676;font-size:12px}";
/// HTML に入れて安全な文字列にする
pub fn escape(source: &str) -> String {
let mut out = String::with_capacity(source.len());
for character in source.chars() {
match character {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\'' => out.push_str("'"),
_ => out.push(character),
}
}
out
}
/// ページ全体の HTML。title と body はエスケープ済みのものを受け取る
pub fn document(title: &str, body: &str) -> String {
let mut out = String::new();
out.push_str("<!DOCTYPE html>");
out.push_str(r#"<html lang="ja">"#);
out.push_str("<head>");
out.push_str(r#"<meta charset="utf-8">"#);
out.push_str(r#"<meta name="viewport" content="width=device-width, initial-scale=1">"#);
out.push_str("<title>");
out.push_str(title);
out.push_str("</title>");
out.push_str("<style>");
out.push_str(PAGE_STYLE);
out.push_str("</style>");
out.push_str("</head>");
out.push_str("<body>");
out.push_str(body);
out.push_str("</body>");
out.push_str("</html>");
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn escapes_the_five_characters() {
assert_eq!(escape(r#"<a href="x">&'"#), "<a href="x">&'");
}
#[test]
fn the_document_has_no_line_break() {
let out = document("題", "<h1>本文</h1>");
assert!(!out.contains('\n'));
assert!(out.starts_with("<!DOCTYPE html>"));
assert!(out.ends_with("</html>"));
}
}