#[macro_use] extern crate rocket; use chrono::{DateTime, Utc}; use rocket::fs::NamedFile; use rocket_dyn_templates::{context, Template}; use scraper::{Html, Selector}; use serde::Serialize; use std::collections::HashMap; use std::path::Path; #[get("/")] fn serve_main() -> Template { Template::render( "index", context! { content: std::fs::read_to_string("data/pages/about.html").unwrap_or_else(|_| "Error loading about page.".to_string()) }, ) } #[get("/posts/")] async fn serve_post(post_name: &str) -> Template { let file_path = Path::new("data/posts").join(format!("{}.html", post_name)); let file_maybe = std::fs::read_to_string(file_path); let file = match file_maybe.ok() { Some(s) => s, None => "404: File not found.".to_string(), }; Template::render( "index", context! { content: file }, ) } #[get("/style/")] async fn serve_css(file: &str) -> Option { let file_path = Path::new("data/style").join(format!("{}", file)); NamedFile::open(file_path).await.ok() } #[get("/recent_posts")] fn list_recent_posts() -> Template { #[derive(Serialize)] struct Post { title: String, path: String, date: String, date_utc: DateTime, } let posts_dir = Path::new("data/posts"); let mut posts = Vec::new(); if let Ok(entries) = std::fs::read_dir(posts_dir) { for entry in entries { if let Ok(entry) = entry { if let Some(file_name) = entry.file_name().to_str() { if file_name.ends_with(".html") { let file = match std::fs::read_to_string(posts_dir.join(file_name)) { Ok(s) => s, Err(_) => continue, }; let document = Html::parse_document(file.as_str()); let mut tags: HashMap<&str, &str> = HashMap::new(); for meta_tag in document.select(&Selector::parse("meta").unwrap()) { let name = meta_tag.value().attr("name").unwrap_or(""); let content = meta_tag.value().attr("content").unwrap_or(""); tags.insert(name, content); } let title = tags.get("title").unwrap_or(&"Untitled Post").to_string(); let date_utc = tags .get("date") .and_then(|s_date| DateTime::parse_from_rfc3339(s_date).ok()) .map(|dt| dt.with_timezone(&Utc)) .unwrap_or_else(|| DateTime::from_timestamp(0, 0).unwrap()); let date = date_utc.format("%d-%m-%Y").to_string(); posts.push(Post { title, date_utc, date, path: format!("/posts/{}", file_name.trim_end_matches(".html")), }); } } } } } posts.sort_by_key(|post| std::cmp::Reverse(post.date_utc)); Template::render("posts_list", context! { posts: posts }) } #[launch] fn rocket() -> _ { rocket::build() .mount("/", routes![serve_post, serve_main, serve_css, list_recent_posts]) .attach(Template::fairing()) }