From 8b6bb67e39b59f68dc005550dd42f031b6f415e8 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Mon, 6 Jan 2025 17:19:06 +1300 Subject: Initial version --- .gitignore | 1 + Cargo.lock | 37 ++++++++ Cargo.toml | 16 ++++ src/generate_html.rs | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 197 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 495 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/generate_html.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f913ee4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,37 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "markdown" +version = "2.0.0" +source = "git+git://benbridle.com/markdown?tag=v2.0.0#a78feb46aefaf8e8950e9b029984e9ff98fe69b0" + +[[package]] +name = "md" +version = "0.1.0" +dependencies = [ + "markdown", + "vagabond", + "xflags", +] + +[[package]] +name = "vagabond" +version = "1.0.1" +source = "git+git://benbridle.com/vagabond?tag=v1.0.1#08f3153fea62ea81a42438347eeee058f5bec199" + +[[package]] +name = "xflags" +version = "0.4.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6a40f95e4e200baabdfe8b813e3ee754b58407a677141bd2890c28ef4a89c21" +dependencies = [ + "xflags-macros", +] + +[[package]] +name = "xflags-macros" +version = "0.4.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a6d9b56f406f5754a3808524166b6e6bdfe219c0526e490cfc39ecc0582a4e6" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..29837e8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "md" +version = "0.1.0" +edition = "2021" + +[dependencies] +vagabond = { git = "git://benbridle.com/vagabond", tag = "v1.0.1" } +markdown = { git = "git://benbridle.com/markdown", tag = "v2.0.0" } +xflags = "0.4.0-pre.1" + +[profile.release] +lto=true +opt-level="s" +debug=false +strip=true +codegen-units=1 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!("