From 4fb323f69c11557a51c7da0b2031029f63edf789 Mon Sep 17 00:00:00 2001 From: "Gabriel A. Giovanini" Date: Sat, 11 Jun 2022 00:00:27 +0200 Subject: feat: Handle 404 result Now gracefully handle 404, so instead of just panic now it will return a proper http 404 response. --- src/assets.rs | 49 +++++++++++++++++++++++++++++++++++++++++++ src/bin/actix.rs | 9 +++++++- src/blog.rs | 63 +++++++++----------------------------------------------- src/lib.rs | 1 + src/router.rs | 14 +++++++++++++ 5 files changed, 82 insertions(+), 54 deletions(-) create mode 100644 src/assets.rs (limited to 'src') diff --git a/src/assets.rs b/src/assets.rs new file mode 100644 index 0000000..2c39d1b --- /dev/null +++ b/src/assets.rs @@ -0,0 +1,49 @@ +use chrono::NaiveDate; +use regex::Regex; +use rust_embed::RustEmbed; +use sailfish::TemplateOnce; +use std::cmp::{Eq, Ord, PartialEq, PartialOrd}; +use std::str; + +pub const BLOG_REGEX: &str = r"(?P[\d]{4}-[\d]{2}-[\d]{2})(?P[a-zA-Z0-9-_]*)"; + +#[derive(RustEmbed)] +#[folder = "content/posts/"] +pub struct PostAsset; + +#[derive(TemplateOnce)] +#[template(path = "index.html")] +pub struct IndexTemplate { + pub posts: Vec<BlogEntry>, +} + +#[derive(TemplateOnce)] +#[template(path = "post.html")] +pub struct PostTemplate { + pub content: String, + pub title: String, + pub date: String, +} + +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub struct BlogEntry { + pub title: String, + pub datetime: NaiveDate, + pub file: String, +} + +impl BlogEntry { + pub fn new(path: &String) -> BlogEntry { + let re = Regex::new(BLOG_REGEX).unwrap(); + let caps = re.captures(path).unwrap(); + let date = &caps["date"]; + let title = str::replace(&caps["title"], "_", " "); + + BlogEntry { + title: String::from(title), + file: String::from(path), + datetime: NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap(), + } + } + +} diff --git a/src/bin/actix.rs b/src/bin/actix.rs index 3f00f36..101fe2e 100644 --- a/src/bin/actix.rs +++ b/src/bin/actix.rs @@ -1,6 +1,7 @@ use actix_web::{get, web, middleware, App, HttpResponse, HttpServer, Responder, http::header::ContentType}; use macroblog::blog::{render_index_page, render_post_page}; -use std::{env}; +use macroblog::router::blog_post_exists; +use std::env; #[get("/")] async fn index() -> impl Responder { @@ -14,6 +15,12 @@ async fn index() -> impl Responder { #[get("/posts/{name}")] async fn posts(name: web::Path<String>) -> impl Responder { + + if !blog_post_exists(&name) { + return HttpResponse::NotFound() + .body("Not Found".to_string()); + } + let body = render_post_page(&name); HttpResponse::Ok() diff --git a/src/blog.rs b/src/blog.rs index c877303..eaa314a 100644 --- a/src/blog.rs +++ b/src/blog.rs @@ -1,62 +1,18 @@ -use chrono::NaiveDate; use pulldown_cmark::{html, Options, Parser}; -use regex::Regex; -use rust_embed::RustEmbed; use sailfish::TemplateOnce; -use std::cmp::{Eq, Ord, PartialEq, PartialOrd}; use std::str; +use crate::assets::{BlogEntry, PostAsset, IndexTemplate, PostTemplate}; -const BLOG_REGEX: &str = r"(?P<date>[\d]{4}-[\d]{2}-[\d]{2})(?P<title>[a-zA-Z0-9-_]*)"; -#[derive(RustEmbed)] -#[folder = "content/posts/"] -struct PostAsset; +pub fn read_assets() -> Vec<BlogEntry> { + let mut entries: Vec<BlogEntry> = PostAsset::iter() + .map(|e| format!("{}", e)) + .map(|e| BlogEntry::new(&e)) + .collect(); -#[derive(TemplateOnce)] -#[template(path = "index.html")] -struct IndexTemplate { - posts: Vec<BlogEntry>, -} - -#[derive(TemplateOnce)] -#[template(path = "post.html")] -struct PostTemplate { - content: String, - title: String, - date: String, -} - -#[derive(PartialEq, Eq, PartialOrd, Ord)] -pub struct BlogEntry { - pub title: String, - pub datetime: NaiveDate, - pub file: String, -} - -impl BlogEntry { - pub fn new(path: &String) -> BlogEntry { - let re = Regex::new(BLOG_REGEX).unwrap(); - let caps = re.captures(path).unwrap(); - let date = &caps["date"]; - let title = str::replace(&caps["title"], "_", " "); + entries.sort_by(|a, b| b.datetime.cmp(&a.datetime)); - BlogEntry { - title: String::from(title), - file: String::from(path), - datetime: NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap(), - } - } - - pub fn read_assets() -> Vec<BlogEntry> { - let mut entries: Vec<BlogEntry> = PostAsset::iter() - .map(|e| format!("{}", e)) - .map(|e| BlogEntry::new(&e)) - .collect(); - - entries.sort_by(|a, b| b.datetime.cmp(&a.datetime)); - - entries - } + entries } fn get_file_content(path: &str) -> String { @@ -68,6 +24,7 @@ fn get_file_content(path: &str) -> String { return html_output.to_string(); } + pub fn render_post_page(path: &String) -> String { let blog = BlogEntry::new(path); @@ -82,7 +39,7 @@ pub fn render_post_page(path: &String) -> String { pub fn render_index_page() -> String { IndexTemplate { - posts: BlogEntry::read_assets(), + posts: read_assets(), } .render_once() .unwrap() diff --git a/src/lib.rs b/src/lib.rs index 0c69888..90ddff2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod blog; pub mod router; +pub mod assets; diff --git a/src/router.rs b/src/router.rs index 35fdf3e..c196ab8 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,3 +1,6 @@ +use std::borrow::Borrow; + +use crate::assets::PostAsset; use regex::Regex; const ACTION_REGEX: &str = r"/{0,1}(?P<action>\w*)/(?P<id>.+)"; @@ -8,6 +11,10 @@ pub enum Router { Post { page: String }, } +pub fn blog_post_exists(name: &str) -> bool { + PostAsset::iter().any(|x| name.eq(&x.to_string())) +} + impl Router { pub fn new(path: &str) -> Router { let re = Regex::new(ACTION_REGEX).unwrap(); @@ -17,6 +24,13 @@ impl Router { None => "index", }; + + // this 7 means the "/posts/" from the full path + let trimmed_path: String = path.chars().skip(7).collect(); + if action.eq("posts") && !blog_post_exists(&trimmed_path) { + return Router::NotFound; + } + match action { "posts" => Router::Post { page: caps.unwrap()["id"].to_string(), -- cgit v1.2.3