use anyhow::{bail, Context}; use orgize::{elements::Keyword, export::HtmlHandler, Element}; use time::{macros::format_description, Date}; use crate::{ date::parse_org_date, parser::{CustomHtmlHandler, Parser}, }; pub struct Post { pub title: String, pub date_f: String, pub content: String, } impl Post { pub fn html(&self) -> anyhow::Result<String> { let title = &self.title; let content = &self.content; let date = &self.date_f; Ok(format!( r#"<div class="header-container"><h1>{title}</h1><div class="header-date">{date}</div></div>{content}"# )) } pub fn link(&self, slug: &str) -> String { format!(r#"<a href="{slug}">{}</a> [{}]"#, self.title, self.date_f) } } pub struct PostParser { title: Option<String>, date: Option<Date>, content: Vec<u8>, handler: CustomHtmlHandler, } impl Default for PostParser { fn default() -> Self { Self { title: None, date: None, content: vec![], handler: CustomHtmlHandler::default(), } } } impl Parser<Post> for PostParser { fn start(&mut self, e: &Element) -> anyhow::Result<()> { match e { Element::Keyword(Keyword { key, value, .. }) if key == "TITLE" => { if self.title.is_some() { bail!("Post has more than one title"); } self.title = Some(value.to_string()); } Element::Keyword(Keyword { key, value, .. }) if key == "DATE" => { if self.date.is_some() { bail!("Post has more than one date"); } self.date = Some(parse_org_date(value)?); } _ => { self.handler.start(&mut self.content, e)?; } } Ok(()) } fn end(&mut self, e: &Element) -> anyhow::Result<()> { self.handler.end(&mut self.content, e)?; Ok(()) } fn render(self) -> anyhow::Result<Post> { let title = self.title.context("Could not find post title")?; let date = self.date.context("Could not find post date")?; let date_f = date.format(format_description!("[day] [month repr:short] [year]"))?; let content = String::from_utf8(self.content)?; Ok(Post { title, date_f, content, }) } }