use actix_web::{get, web, HttpResponse, Responder}; use cached::proc_macro::once; use chrono::{DateTime, Datelike, Utc}; use comrak::{parse_document, Arena}; use ramhorns::Content; use crate::{ config::Config, misc::{ date::Date, markdown::{get_metadata, get_options, read_file, File, FileMetadata}, }, template::Infos, }; #[get("/blog")] pub async fn index(config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_index(config.get_ref().to_owned())) } #[derive(Content)] struct BlogIndexTemplate { posts: Vec, no_posts: bool, } //#[once(time = 120)] pub fn build_index(config: Config) -> String { let mut posts = get_posts("data/blog"); // Sort from newest to oldest posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day)); posts.reverse(); config.tmpl.render( "blog/index.html", BlogIndexTemplate { no_posts: posts.is_empty(), posts, }, Infos { page_title: Some("Blog".to_owned()), page_desc: Some("Liste des posts d'Anri".to_owned()), page_kw: Some(["blog", "blogging"].join(", ")), }, ) } #[derive(Content)] struct Post { title: String, date: Date, url: String, } fn get_posts(location: &str) -> Vec { let entries = match std::fs::read_dir(location) { Ok(res) => res .flatten() .filter(|f| f.path().extension().unwrap() == "md") .collect::>(), Err(_) => vec![], }; entries .iter() .map(|f| { let _filename = f.file_name(); let filename = _filename.to_string_lossy(); let file_without_ext = filename.split_at(filename.len() - 3).0; let file_metadata = match std::fs::read_to_string(format!("{location}/{filename}")) { Ok(text) => { let arena = Arena::new(); let options = get_options(); let root = parse_document(&arena, &text, &options); let mut metadata = get_metadata(root); metadata.title = match metadata.title { Some(title) => Some(title), None => Some(file_without_ext.to_owned()), }; metadata } Err(_) => FileMetadata { title: Some(file_without_ext.to_owned()), link: None, date: None, }, }; Post { url: file_without_ext.to_owned(), title: file_metadata.title.unwrap(), date: file_metadata.date.unwrap_or({ let m = f.metadata().unwrap(); let date = std::convert::Into::>::into( m.modified().unwrap_or(m.created().unwrap()), ) .date_naive(); Date { day: date.day(), month: date.month(), year: date.year(), } }), } }) .collect::>() } #[derive(Content)] struct BlogPostTemplate { post: Option, } #[get("/blog/{id}")] pub async fn page(path: web::Path<(String,)>, config: web::Data) -> impl Responder { HttpResponse::Ok().body(build_post(path.into_inner().0, config.get_ref().to_owned())) } pub fn build_post(file: String, config: Config) -> String { let mut post = None; let infos = get_post(&mut post, file); config .tmpl .render("blog/post.html", BlogPostTemplate { post }, infos) } fn get_post(post: &mut Option, filename: String) -> Infos { let blog_dir = "data/blog"; let ext = ".md"; *post = read_file(&format!("{blog_dir}/{filename}{ext}")); let title = match post { Some(data) => match &data.metadata.info.title { Some(text) => text, None => &filename, }, None => &filename, }; Infos { page_title: Some(format!("Post: {}", title)), page_desc: Some("Blog d'Anri".to_owned()), page_kw: Some(["blog", "blogging", "write", "writing"].join(", ")), } }