diff options
Diffstat (limited to 'src/blog.rs')
| -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  }  | 
