aboutsummaryrefslogtreecommitdiffstats
path: root/src/posts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/posts/mod.rs73
1 files changed, 61 insertions, 12 deletions
diff --git a/src/posts/mod.rs b/src/posts/mod.rs
index b188ee0..be1e1a9 100644
--- a/src/posts/mod.rs
+++ b/src/posts/mod.rs
@@ -1,6 +1,8 @@
+use std::collections::HashSet;
+
use async_trait::async_trait;
use chrono::{DateTime, Utc};
-use markdown::{mdast::Node, Constructs, ParseOptions};
+use markdown::{mdast::Node, Constructs, Options, ParseOptions};
use rocket::http::Status;
use serde::{ser::SerializeStruct, Deserialize, Serialize};
use tokio::fs;
@@ -28,15 +30,23 @@ pub struct Post<D: Content> {
updated_at: Option<DateTime<Utc>>,
tags: Vec<String>,
post_type: PostType,
+ render: Option<String>,
data: D,
}
-impl<D: Content> Serialize for Post<D> {
+impl<D: Content + Serialize> Post<D> {
+ pub async fn render(&mut self) -> Result<()> {
+ self.render = Some(self.data.render().await?);
+ Ok(())
+ }
+}
+
+impl<D: Content + Serialize> Serialize for Post<D> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
- let mut state = serializer.serialize_struct("Post", 5)?;
+ let mut state = serializer.serialize_struct("Post", 7)?;
state.serialize_field("subject", &self.subject)?;
state.serialize_field("created_at", &self.created_at.to_string())?;
state.serialize_field(
@@ -45,6 +55,8 @@ impl<D: Content> Serialize for Post<D> {
)?;
state.serialize_field("tags", &self.tags)?;
state.serialize_field("post_type", &self.post_type)?;
+ state.serialize_field("render", &self.render)?;
+ state.serialize_field("data", &self.data)?;
state.end()
}
}
@@ -52,29 +64,55 @@ impl<D: Content> Serialize for Post<D> {
pub async fn get_blogposts() -> Result<Vec<Post<Article>>> {
let mut blogposts: Vec<Post<Article>> = Vec::new();
let mut articles_dir = fs::read_dir("./articles").await?;
- while let Some(dir) = articles_dir.next_entry().await? {
- let mut file_path = dir.path();
- file_path.push(dir.file_name());
- let file_path = file_path.with_extension("md");
- println!("{:?}", file_path);
+ while let Some(file) = articles_dir.next_entry().await? {
+ let name = file.file_name();
+ let name = name.to_str().unwrap_or_default()[..name.len() - 3].to_owned();
let blogpost: Article = Article {
- path: file_path.to_str().unwrap_or_default().to_owned(),
+ path: file.path().to_str().unwrap_or_default().to_owned(),
+ name,
};
let blogpost = Post::try_from(blogpost).await.unwrap();
- println!("{:?}", blogpost);
blogposts.push(blogpost);
}
Ok(blogposts)
}
+pub async fn get_blogpost(post_name: &str) -> Result<Post<Article>> {
+ let mut articles_dir = fs::read_dir("./articles").await?;
+ while let Some(file) = articles_dir.next_entry().await? {
+ let name = file.file_name();
+ let name = &name.to_str().unwrap_or_default()[..name.len() - 3];
+ if name == post_name {
+ let blogpost = Article {
+ path: file.path().to_str().unwrap_or_default().to_owned(),
+ name: name.to_owned(),
+ };
+ let blogpost = Post::try_from(blogpost).await.unwrap();
+ return Ok(blogpost);
+ }
+ }
+ Err(BlossomError::NotFound(Status::new(404)))
+}
+
+pub fn get_tags<D: Content>(posts: &Vec<Post<D>>) -> HashSet<String> {
+ posts.into_iter().fold(HashSet::new(), |mut acc, post| {
+ let tags = &post.tags;
+ for tag in tags {
+ acc.insert(tag.to_owned());
+ }
+ acc
+ })
+}
+
#[async_trait]
pub trait Content {
async fn render(&self) -> Result<String>;
}
-#[derive(Debug)]
+#[derive(Serialize, Debug)]
pub struct Article {
path: String,
+ name: String,
}
impl Article {
@@ -116,7 +154,17 @@ impl Content for Article {
let mut file = fs::File::open(&self.path).await?;
let mut buf = String::new();
file.read_to_string(&mut buf).await?;
- Ok(markdown::to_html(&buf))
+ let options = Options {
+ parse: ParseOptions {
+ constructs: Constructs {
+ frontmatter: true,
+ ..Constructs::default()
+ },
+ ..ParseOptions::default()
+ },
+ ..Options::default()
+ };
+ Ok(markdown::to_html_with_options(&buf, &options).unwrap())
}
}
@@ -144,6 +192,7 @@ impl Post<Article> {
updated_at,
tags: metadata.tags,
post_type: PostType::Article,
+ render: None,
data: article,
})
}