use serde::Deserialize; use chrono::NaiveDate; use std::path::PathBuf; use std::fs; #[derive(Deserialize, Debug)] pub struct Frontmatter { pub title: String, pub date: NaiveDate, #[serde(default)] pub draft: bool, } #[derive(Debug)] pub struct Page { pub meta: Frontmatter, pub source_path: PathBuf, pub output_path: PathBuf, pub body: String, pub html: String, } pub fn parse_frontmatter(content: &str) -> Option { if !content.starts_with("+++\n") { return None; } let rest = &content[4..]; let end = rest.find("\n+++\n")?; let toml_str = &rest[..end]; let meta: Frontmatter = toml::from_str(toml_str) .expect("frontmatter failed parsed"); Some(meta) } pub fn parse_page(source_path: &PathBuf) -> anyhow::Result { let content = fs::read_to_string(source_path)?; let maybe_meta = parse_frontmatter(&content); let meta: Frontmatter; let body: String; if let Some(fm) = maybe_meta { let end_post = content.find("\n+++\n").unwrap(); println!("end_post: {}", end_post); meta = fm; body = content[(end_post + 5)..].to_string(); } else { let fallback_title = source_path .file_stem() .and_then(|s| s.to_str()) .unwrap_or("untitled") .to_string(); meta = Frontmatter { title: fallback_title, date: chrono::Utc::now().date_naive(), draft: true, }; body = content; } let html = render_markdown(&body); let slug = meta.title.to_lowercase().replace(' ', "-"); let output_path = PathBuf::from("public").join(&slug).join("index.html"); Ok (Page { meta, source_path: source_path.clone(), output_path, body, html, }) } pub fn render_markdown(input: &str) -> String { use pulldown_cmark::{Parser, Options, html}; let mut options = Options::empty(); options.insert(Options::ENABLE_STRIKETHROUGH); options.insert(Options::ENABLE_TABLES); let parser = Parser::new_ext(input, options); let mut out = String::new(); html::push_html(&mut out, parser); out }