use anyhow::bail; use anyhow::Context; use anyhow::Error; use orgize::elements::Timestamp; use orgize::{ export::{DefaultHtmlHandler, HtmlHandler}, Element, Org, }; use std::io::Write; use syntect::{ highlighting::{Theme, ThemeSet}, html::highlighted_html_for_string, parsing::SyntaxSet, }; use time::macros::format_description; use time::Date; pub trait Parser<T> { fn start(&mut self, _: &Element) -> anyhow::Result<()>; fn end(&mut self, _: &Element) -> anyhow::Result<()>; fn render(self) -> anyhow::Result<T>; } pub fn parse<T, P: Parser<T>>(mut p: P, org: &str) -> anyhow::Result<T> { for e in Org::parse(org).iter() { match e { orgize::Event::Start(ele) => p.start(ele)?, orgize::Event::End(ele) => p.end(ele)?, } } p.render() } pub struct CustomHtmlHandler { fallback: DefaultHtmlHandler, ps: SyntaxSet, theme: Theme, } impl Default for CustomHtmlHandler { fn default() -> Self { let mut ts = ThemeSet::load_defaults(); Self { fallback: Default::default(), ps: SyntaxSet::load_defaults_newlines(), theme: ts .themes .remove("base16-eighties.dark") .expect("Could not load theme"), } } } impl HtmlHandler<Error> for CustomHtmlHandler { fn start<W: Write>(&mut self, mut w: W, element: &Element) -> Result<(), Error> { match element { Element::SourceBlock(block) => { let syntax = self .ps .find_syntax_by_token(&block.language) .with_context(|| format!("Unknown language {}", block.language))?; let html = highlighted_html_for_string(&block.contents, &self.ps, syntax, &self.theme)?; write!(w, "{html}")?; } Element::SpecialBlock(block) => match block.name.as_ref() { "JUSTIFYRIGHT" => { write!(w, r#"<div class="justify-right">"#)?; } _ => bail!("Unrecognized special block name {}", block.name), }, Element::Timestamp(Timestamp::Active { start, .. }) => { let date = Date::from_calendar_date( start.year.into(), start.month.try_into()?, start.day, )?; date.format_into( &mut w, format_description!("[day] [month repr:short] [year]"), )?; } // fallback to default handler _ => self.fallback.start(w, element)?, } Ok(()) } fn end<W: Write>(&mut self, mut w: W, element: &Element) -> Result<(), Error> { match element { Element::SpecialBlock(block) => match block.name.as_ref() { "JUSTIFYRIGHT" => { write!(w, "</div>")?; } _ => bail!("Unrecognized special block name {}", block.name), }, // fallback to default handler _ => self.fallback.end(w, element)?, } Ok(()) } }