diff options
Diffstat (limited to '')
-rw-r--r-- | src/blog.rs | 146 |
1 files changed, 30 insertions, 116 deletions
diff --git a/src/blog.rs b/src/blog.rs index 22cee05..0e757d5 100644 --- a/src/blog.rs +++ b/src/blog.rs @@ -7,12 +7,13 @@ use tokio::{fs, io::AsyncReadExt}; use chrono::{DateTime, Utc}; use crate::{ + article::Article, error::BlossomError, posts::{Post, PostType}, Result, }; -static ARTICLES_DIR: &str = "./articles"; +static DIRECTORY: &str = "./articles"; #[derive(Clone)] pub struct Blogpost { @@ -32,6 +33,32 @@ pub struct BlogpostMetadata { tags: Vec<String>, } +impl Article for Blogpost { + type Metadata = BlogpostMetadata; + + type Article = Blogpost; + + fn directory() -> &'static str { + DIRECTORY + } + + fn new(file_name: String, metadata: Self::Metadata, content: String) -> Result<Self::Article> { + let updated_at = if let Some(updated_at) = metadata.updated_at { + Some(updated_at.parse::<DateTime<Utc>>()?) + } else { + None + }; + Ok(Blogpost { + file_name, + title: metadata.title, + published_at: metadata.published_at.parse::<DateTime<Utc>>()?, + updated_at, + tags: metadata.tags, + content, + }) + } +} + impl Post for Blogpost { fn id(&self) -> &str { &self.file_name @@ -45,12 +72,8 @@ impl Post for Blogpost { &self.published_at } - fn updated_at(&self) -> &DateTime<Utc> { - if let Some(updated_at) = &self.updated_at { - updated_at - } else { - &self.published_at - } + fn updated_at(&self) -> Option<&DateTime<Utc>> { + self.updated_at.as_ref() } fn tags(&self) -> &Vec<String> { @@ -74,113 +97,4 @@ impl Blogpost { pub fn file_name(&self) -> &str { &self.file_name } - - async fn from_path(path: impl AsRef<Path>) -> Result<Self> { - let mut file = fs::File::open(&path).await?; - let mut buf = String::new(); - file.read_to_string(&mut buf).await?; - let parse_options = ParseOptions { - constructs: Constructs { - frontmatter: true, - ..Constructs::gfm() - }, - ..ParseOptions::default() - }; - let metadata: BlogpostMetadata = markdown::to_mdast(&buf, &parse_options) - .unwrap() - .try_into()?; - let file_name = path - .as_ref() - .file_name() - .ok_or(BlossomError::NotAFile)? - .to_string_lossy(); - let file_name = file_name[..file_name.len() - 3].to_owned(); - let updated_at = if let Some(updated_at) = metadata.updated_at { - Some(updated_at.parse::<DateTime<Utc>>()?) - } else { - None - }; - // TODO: cache render only when needed - let options = Options { - parse: parse_options, - compile: CompileOptions { - gfm_task_list_item_checkable: true, - allow_dangerous_html: true, - ..CompileOptions::default() - }, - }; - let content = markdown::to_html_with_options(&buf, &options).unwrap(); - Ok(Self { - file_name, - title: metadata.title, - published_at: metadata.published_at.parse::<DateTime<Utc>>()?, - updated_at, - tags: metadata.tags, - content, - }) - } - - // async fn render(&mut self) -> Result<String> { - // let path = Path::new(ARTICLES_DIR) - // .join(&self.file_name) - // .with_extension("md"); - // // TODO: remove unwraps when file read failure - // let mut file = fs::File::open(&path).await.unwrap(); - // let mut buf = String::new(); - // file.read_to_string(&mut buf).await.unwrap(); - // let parse_options = ParseOptions { - // constructs: Constructs { - // frontmatter: true, - // ..Constructs::gfm() - // }, - // ..ParseOptions::default() - // }; - // let options = Options { - // parse: parse_options, - // compile: CompileOptions { - // gfm_task_list_item_checkable: true, - // allow_dangerous_html: true, - // ..CompileOptions::default() - // }, - // }; - // let content = markdown::to_html_with_options(&buf, &options).unwrap(); - // Ok(content) - // } -} - -impl TryFrom<Node> for BlogpostMetadata { - type Error = BlossomError; - - fn try_from(tree: Node) -> std::prelude::v1::Result<Self, Self::Error> { - let children = tree.children(); - if let Some(children) = children { - if let Some(toml) = children.into_iter().find_map(|el| match el { - Node::Toml(toml) => Some(&toml.value), - _ => None, - }) { - return Ok(toml::from_str(toml)?); - }; - } - Err(BlossomError::NoMetadata) - } -} - -pub async fn get_blogposts() -> Result<Vec<Blogpost>> { - let mut blogposts: Vec<Blogpost> = Vec::new(); - let mut articles_dir = fs::read_dir(ARTICLES_DIR).await?; - while let Some(entry) = articles_dir.next_entry().await? { - let blogpost = Blogpost::from_path(entry.path()).await?; - blogposts.push(blogpost); - } - blogposts.sort_by_key(|post| post.published_at); - blogposts.reverse(); - Ok(blogposts) -} - -pub async fn get_blogpost(file_name: &str) -> Result<Blogpost> { - let path = Path::new(ARTICLES_DIR) - .join(Path::new(file_name)) - .with_extension("md"); - println!("{:?}", path); - Blogpost::from_path(path).await } |