From 8b6bb67e39b59f68dc005550dd42f031b6f415e8 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Mon, 6 Jan 2025 17:19:06 +1300 Subject: Initial version --- src/generate_html.rs | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 197 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 src/generate_html.rs create mode 100644 src/main.rs (limited to 'src') diff --git a/src/generate_html.rs b/src/generate_html.rs new file mode 100644 index 0000000..3f22d3b --- /dev/null +++ b/src/generate_html.rs @@ -0,0 +1,244 @@ +use crate::*; + +use markdown::*; + + +pub fn generate_html(document: &MarkdownDocument, page: &SourceFile, website: &Website) -> String { + format!("\ + + +{} — {} + + +{} + + +
+{} +
+ + \ +", + page.name, website.name, + get_html_head(document), + document_to_html(document, page, website) + ) +} + + + +pub fn get_html_head(document: &MarkdownDocument) -> String { + if let Some(Block::Fragment { language, content }) = document.blocks.first() { + if language == "embed-html-head" { + return content.to_string(); + } + } + String::from("\ + + + \ + ") +} + + + +pub fn document_to_html(document: &MarkdownDocument, page: &SourceFile, website: &Website) -> String { + let mut html = String::new(); + + macro_rules! line_to_html { + ($l:expr) => {{ line_to_html(&$l, page, website) }}; } + macro_rules! html { + ($($arg:tt)*) => {{ html.push_str(&format!($($arg)*)); html.push('\n'); }}; } + macro_rules! tag { + ($t:expr,$l:expr) => { html!("<{}>{}", $t, line_to_html!($l), $t) }; } + macro_rules! wrap { + ($t:expr,$f:expr) => {{ html!("<{}>", $t); $f; html!("", $t); }}; + } + + for block in &document.blocks { + match block { + Block::Heading { level, line } => match level { + Level::Heading1 => tag!("h1", line), + Level::Heading2 => tag!("h2", line), + Level::Heading3 => tag!("h3", line), + } + Block::Paragraph(line) => tag!("p", line), + Block::List(lines) => wrap!("ul", for line in lines { + // Insert a
tag directly after the first untagged colon. + let mut depth = 0; + let mut prev = '\0'; + let mut output = String::new(); + for c in line_to_html!(line).chars() { + output.push(c); + if c == '<' { + depth += 1; + } else if c == '/' && prev == '<' { + depth -= 2; + } else if c == ':' && depth == 0 { + output.pop(); output.push_str("
"); depth += 99; + } + prev = c; + } + match output.contains("
") { + true => html!("
  • {output}
  • "), + false => html!("
  • {output}
  • "), + } + }), + Block::Note(lines) => wrap!("aside", for line in lines { tag!("p", line) }), + Block::Embedded { label, path } => match path.rsplit_once('.') { + Some((_, extension)) => match extension.to_lowercase().as_str() { + "jpg"|"jpeg"|"png"|"webp"|"gif"|"tiff" => html!( + "
    {}
    ", + path, path, label, label + ), + "mp3"|"wav"|"m4a" => html!("