diff --git a/src/main.rs b/src/main.rs index fb22a04..4af4e78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod config; mod entries; +mod render; fn main() { let config = config::Config::load("config.toml").expect("failed to load config.toml"); diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..da2e3c8 --- /dev/null +++ b/src/render.rs @@ -0,0 +1,119 @@ +use crate::entries::Entry; + +pub fn render_page(site_title: &str, site_url: &str, entries: &[Entry], form_html: &str) -> String { + let nav_url = site_url.trim_end_matches('/'); + let mut html = format!( + r#" + + + + + guestbook - {site_title} + + + + +

guestbook

+

If you visited my site, please sign my guestbook!

+{form_html} +"# + ); + + for entry in entries { + html.push_str(&render_entry(entry)); + } + + html.push_str("\n\n"); + html +} + +fn render_entry(entry: &Entry) -> String { + let mut header = format!("
\n

{} - {}", entry.meta.date, entry.meta.name); + if !entry.meta.website.is_empty() { + header.push_str(&format!( + " ({})", + entry.meta.website, entry.meta.website + )); + } + header.push_str("

\n"); + format!("{header} {}\n
\n", entry.body) +} + +pub const FORM_HTML: &str = r#"
+ + + + + +
"#; + +pub const STYLE_CSS: &str = "body { + max-width: 70ch; + line-height: 1.5; + margin: 0 auto; + padding: 1rem; +} +"; + +#[cfg(test)] +mod tests { + use super::*; + use crate::entries::{Entry, EntryMeta, Status}; + + fn make_entry(name: &str, date: &str, body: &str) -> Entry { + Entry { + id: "test".into(), + meta: EntryMeta { + name: name.into(), + date: date.into(), + website: String::new(), + status: Status::Approved, + }, + body: body.into(), + } + } + + #[test] + fn test_render_page_contains_nav() { + let html = render_page("ily.rs", "https://ily.rs", &[], FORM_HTML); + assert!(html.contains(r#"ily.rs"#)); + assert!(html.contains(r#"links"#)); + } + + #[test] + fn test_render_page_contains_form() { + let html = render_page("ily.rs", "https://ily.rs", &[], FORM_HTML); + assert!(html.contains(r#"action="/submit""#)); + assert!(html.contains(r#"style="display:none""#)); // honeypot + } + + #[test] + fn test_render_entry_no_website() { + let entry = make_entry("alice", "2026-04-09", "Hello!"); + let html = render_page("ily.rs", "https://ily.rs", &[entry], FORM_HTML); + assert!(html.contains("alice")); + assert!(html.contains("Hello!")); + assert!(!html.contains(""#)); + } + + #[test] + fn test_render_preserves_html_in_body() { + let entry = make_entry("carol", "2026-04-09", "Bold "); + let html = render_page("ily.rs", "https://ily.rs", &[entry], FORM_HTML); + assert!(html.contains("Bold")); + assert!(html.contains("")); + } +}