use std::{ fs::{read_to_string, File}, io::Write, }; use clap::{Parser, Subcommand}; use laby::{html, internal::Buffer, raw, Render}; use markdown::{Block, Span}; #[derive(Parser)] struct Args { #[clap(short, long)] output: Option, #[clap(subcommand)] action: ArgAction, } #[derive(Subcommand)] enum ArgAction { RenderArticle { input: 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()); } } } fn write_output(t: &Option, 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!(body)) } 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) -> String { let mut out = String::new(); for s in ss { out += match s { Span::Break => format!("
"), Span::Text(t) => escape(&t), Span::Code(c) => format!("
{}
", escape(&c)), Span::Link(text, url, _) => { format!("{}", escape(&url), escape(&text)) } Span::Image(_, _, _) => todo!(), Span::Emphasis(c) => format!("{}", span_to_html(c)), Span::Strong(c) => format!("{}", span_to_html(c)), } .as_str() } out } fn blocks_to_html(blocks: Vec) -> String { let mut out = String::new(); for e in blocks { out += match e { markdown::Block::Header(text, level) => { format!("{}", span_to_html(text)) } markdown::Block::Paragraph(p) => span_to_html(p), markdown::Block::Blockquote(q) => format!("{}", blocks_to_html(q)), markdown::Block::CodeBlock(_syntax, content) => { format!("
{}
", escape(&content)) // TODO syntax highlighting } markdown::Block::OrderedList(_, _) => todo!(), markdown::Block::UnorderedList(_) => todo!(), markdown::Block::Raw(r) => r, markdown::Block::Hr => format!("
"), } .as_str(); } out } fn escape(text: &str) -> String { text.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("'", "’") .replace("\"", """) }