Skip to content
Snippets Groups Projects
post.rs 2.4 KiB
Newer Older
Stephen D's avatar
Stephen D committed
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 {
Stephen D's avatar
Stephen D committed
        format!(r#"<a href="{slug}">{}</a> [{}]"#, self.title, self.date_f)
Stephen D's avatar
Stephen D committed
    }
}

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,
        })
    }
}