Registry / RepositoryTypeScriptRustPython
Rollpie
ReadmeFiles
Versions
Info
Download
0.1.280.1 KB2026-09-140.1.167.6 KB2026-09-140.1.064.9 KB2026-09-14
Version
0.1.0
Copyright
Rollpie, Irohabook
Publisher
math
Published
2026-09-14
Size
64.9 KB
Downloads
2
Checksum
3f5a7260ef28c5f7ca3540b87faa9176bdf8039ad720f809e49a0f7117b8528b
Dependencies
None

html.rs

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("&lt;"),
            '>' => out.push_str("&gt;"),
            '"' => out.push_str("&quot;"),
            '\'' => out.push_str("&#39;"),
            _ => 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">&'"#), "&lt;a href=&quot;x&quot;&gt;&amp;&#39;");
    }

    #[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>"));
    }
}