solid_pod_rs_forge/html/
mod.rs1use crate::request::esc;
9
10mod views;
11pub use views::*;
12
13const STYLE: &str = "\
16:root{color-scheme:light dark}\
17body{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;\
18max-width:60rem;margin:1.5rem auto;padding:0 1rem;line-height:1.5}\
19a{color:#3573b3;text-decoration:none}a:hover{text-decoration:underline}\
20header.forge{border-bottom:1px solid #8884;margin-bottom:1rem;padding-bottom:.5rem}\
21code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}\
22pre{background:#8881;padding:.75rem;border-radius:6px;overflow-x:auto}\
23table{border-collapse:collapse;width:100%}\
24td,th{text-align:left;padding:.35rem .5rem;border-bottom:1px solid #8883}\
25.muted{color:#8888}.badge{font-size:.8rem;padding:.1rem .45rem;border-radius:10px;background:#8882}\
26.state-open{background:#2c9e4b33;color:#2c9e4b}.state-closed{background:#b5433933;color:#b54339}\
27.state-merged{background:#8250df33;color:#8250df}\
28nav.crumbs{font-size:.95rem;margin-bottom:.75rem}\
29";
30
31#[must_use]
35pub fn page(title: &str, body_html: &str) -> String {
36 format!(
37 "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\">\
38<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\
39<title>{}</title><style>{}</style></head><body>\
40<header class=\"forge\"><strong>\u{2692} forge</strong></header>{}</body></html>",
41 esc(title),
42 STYLE,
43 body_html
44 )
45}
46
47#[must_use]
50pub fn crumbs(items: &[(&str, Option<String>)]) -> String {
51 let mut out = String::from("<nav class=\"crumbs\">");
52 for (i, (label, href)) in items.iter().enumerate() {
53 if i > 0 {
54 out.push_str(" / ");
55 }
56 match href {
57 Some(h) => out.push_str(&format!("<a href=\"{}\">{}</a>", esc(h), esc(label))),
58 None => out.push_str(&esc(label)),
59 }
60 }
61 out.push_str("</nav>");
62 out
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn page_escapes_title() {
71 let p = page("<x>", "<p>ok</p>");
72 assert!(p.contains("<title><x></title>"));
73 assert!(p.contains("<p>ok</p>"));
74 assert!(p.starts_with("<!doctype html>"));
75 }
76
77 #[test]
78 fn crumbs_last_is_plain_text() {
79 let c = crumbs(&[
80 ("home", Some("/forge".into())),
81 ("alice", Some("/forge/alice".into())),
82 ("repo", None),
83 ]);
84 assert!(c.contains("<a href=\"/forge\">home</a>"));
85 assert!(c.contains(" / repo</nav>"));
86 assert!(!c.contains(">repo</a>"));
88 }
89}