diff options
Diffstat (limited to 'code/src/main.rs')
-rw-r--r-- | code/src/main.rs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/code/src/main.rs b/code/src/main.rs new file mode 100644 index 0000000..09ce3d8 --- /dev/null +++ b/code/src/main.rs @@ -0,0 +1,140 @@ +use std::{ + fs::{read_to_string, File}, + io::{BufRead, BufReader, Write}, + path::PathBuf, +}; + +use clap::{Parser, Subcommand}; +use laby::{html, internal::Buffer, iter, li, raw, ul, Render}; +use markdown::{Block, Span}; + +#[derive(Parser)] +struct Args { + #[clap(short, long)] + output: Option<String>, + #[clap(subcommand)] + action: ArgAction, +} + +#[derive(Subcommand)] +enum ArgAction { + RenderArticle { input: String }, + RenderIndex { root: String }, +} + +fn main() { + let args = Args::parse(); + match args.action { + ArgAction::RenderArticle { input } => { + let md_source = read_to_string(input).unwrap(); + let mut out = Buffer::new(); + article(md_source).render(&mut out); + write_output(&args.output, out.into_string()); + } + ArgAction::RenderIndex { root } => { + let mut out = Buffer::new(); + index(root).render(&mut out); + write_output(&args.output, out.into_string()); + } + } +} + +fn write_output(t: &Option<String>, o: String) { + if let Some(f) = t { + let mut f = File::create(f).unwrap(); + f.write_fmt(format_args!("{o}")).unwrap() + } else { + println!("{o}") + } +} + +fn scaffold(title: String, body: impl Render) -> impl Render { + html!( + head!(title!(title)), + body!( + nav!(h2!("metamuffin's blog"), a!(href = "./index.html", "index")), + article!(body) + ) + ) +} + +fn index(root: String) -> impl Render { + scaffold( + "index".to_string(), + ul!(iter!(std::fs::read_dir(root) + .unwrap() + .map(|e| e.unwrap()) + .map(|e| ( + e.file_name().to_str().unwrap().to_string(), + article_title(e.path()) + )) + .map(|(path, title)| li!( + path.as_str()[0..10].to_string(), + ": ", + a!(href = format!("./{}", path.replace(".md", ".html")), title) + )))), + ) +} + +fn article_title(path: PathBuf) -> String { + let f = File::open(path).unwrap(); + let mut f = BufReader::new(f); + let mut buf = String::new(); + f.read_line(&mut buf).unwrap(); // assume the 1st line has the title + String::from(&buf[2..]) +} + +fn article(md_source: String) -> impl Render { + scaffold( + "blub".to_string(), + raw!(blocks_to_html(markdown::tokenize(&md_source))), + ) +} + +fn span_to_html(ss: Vec<Span>) -> String { + let mut out = String::new(); + for s in ss { + out += match s { + Span::Break => format!("<br/>"), + Span::Text(t) => escape(&t), + Span::Code(c) => format!("<pre><code>{}</code></pre>", escape(&c)), + Span::Link(text, url, _) => { + format!("<a href=\"{}\">{}</a>", escape(&url), escape(&text)) + } + Span::Image(_, _, _) => todo!(), + Span::Emphasis(c) => format!("<i>{}</i>", span_to_html(c)), + Span::Strong(c) => format!("<b>{}</b>", span_to_html(c)), + } + .as_str() + } + out +} +fn blocks_to_html(blocks: Vec<Block>) -> String { + let mut out = String::new(); + for e in blocks { + out += match e { + markdown::Block::Header(text, level) => { + format!("<h{level}>{}</h{level}>", span_to_html(text)) + } + markdown::Block::Paragraph(p) => format!("<p>{}</p>", span_to_html(p)), + markdown::Block::Blockquote(q) => format!("<quote>{}</quote>", blocks_to_html(q)), + markdown::Block::CodeBlock(_syntax, content) => { + format!("<pre><code>{}</code></pre>", escape(&content)) // TODO syntax highlighting + } + markdown::Block::OrderedList(_, _) => todo!(), + markdown::Block::UnorderedList(_) => todo!(), + markdown::Block::Raw(r) => r, + markdown::Block::Hr => format!("<hr/>"), + } + .as_str(); + } + out +} + +fn escape(text: &str) -> String { + text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("'", "’") + .replace("\"", """) +} |