diff options
| author | Gabriel A. Giovanini <mail@gabrielgio.me> | 2022-06-11 00:00:27 +0200 | 
|---|---|---|
| committer | Gabriel A. Giovanini <mail@gabrielgio.me> | 2022-06-11 00:00:27 +0200 | 
| commit | 4fb323f69c11557a51c7da0b2031029f63edf789 (patch) | |
| tree | 0d08ce52abbe0c5d123b4051f50ec5bc386652ae | |
| parent | 0e147a780e74b54afbd56ff7438077d855d5c1c2 (diff) | |
| download | macroblog.rs-4fb323f69c11557a51c7da0b2031029f63edf789.tar.gz macroblog.rs-4fb323f69c11557a51c7da0b2031029f63edf789.tar.bz2 macroblog.rs-4fb323f69c11557a51c7da0b2031029f63edf789.zip | |
feat: Handle 404 result
Now gracefully handle 404, so instead of just panic now it will return a
proper http 404 response.
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | src/assets.rs | 49 | ||||
| -rw-r--r-- | src/bin/actix.rs | 9 | ||||
| -rw-r--r-- | src/blog.rs | 63 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/router.rs | 14 | ||||
| -rw-r--r-- | tests/test_blog.rs | 3 | ||||
| -rw-r--r-- | tests/test_router.rs | 19 | 
8 files changed, 99 insertions, 61 deletions
| @@ -3,7 +3,7 @@  After reading [this  article](https://www.andreinc.net/2022/04/10/a-blog-that-is-a-single-executable-binary)  by Andrei Ciobanu it sparkled in me to do the same thing but with rust. -It is going to be a bit bigger than micro though ;) +It is going to be a bit bigger than micro ;)  To achieve that I'll be using the following: 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<date>[\d]{4}-[\d]{2}-[\d]{2})(?P<title>[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() @@ -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(), diff --git a/tests/test_blog.rs b/tests/test_blog.rs index b72f800..6cd3249 100644 --- a/tests/test_blog.rs +++ b/tests/test_blog.rs @@ -1,4 +1,5 @@  use macroblog::blog::*; +use macroblog::assets::*;  use chrono::NaiveDate; @@ -17,7 +18,7 @@ fn test_create_blog_entry() {  #[test]  fn test_read_assets() {      // This test meant to test if all files are parsed correctly -    let assets = BlogEntry::read_assets(); +    let assets = read_assets();      assert!(assets.iter().count() > 1)  } diff --git a/tests/test_router.rs b/tests/test_router.rs index 7ebe019..cfd4c32 100644 --- a/tests/test_router.rs +++ b/tests/test_router.rs @@ -1,11 +1,11 @@ -use macroblog::router::{Router}; +use macroblog::router::Router;  #[test]  fn test_router_new_posts() { -    match Router::new("/posts/k8s.html") { +    match Router::new("/posts/2021-12-26Enable_NFS_on_K3S.md") {          Router::NotFound => assert!(false, "Wrong type parse"),          Router::Index => assert!(false, "Wrong type parse"), -        Router::Post { page } => assert_eq!(page, "k8s.html".to_string()) +        Router::Post { page } => assert_eq!(page, "2021-12-26Enable_NFS_on_K3S.md".to_string()),      };  } @@ -14,7 +14,7 @@ fn test_router_new_index() {      match Router::new("/") {          Router::Index => assert!(true),          Router::NotFound => assert!(false, "Wrong type parse"), -        Router::Post { page: _ } => assert!(false, "Wrong type parse") +        Router::Post { page: _ } => assert!(false, "Wrong type parse"),      };  } @@ -23,6 +23,15 @@ fn test_router_new_not_found() {      match Router::new("/not_found") {          Router::NotFound => assert!(true),          Router::Index => assert!(false, "Wrong type parse"), -        Router::Post { page: _ } => assert!(false, "Wrong type parse") +        Router::Post { page: _ } => assert!(false, "Wrong type parse"), +    }; +} + +#[test] +fn test_router_new_not_found_matching_regex() { +    match Router::new("/posts/2021-12-03Enable_NFS_on_K3S.html") { +        Router::NotFound => assert!(true), +        Router::Index => assert!(false, "Wrong type parse"), +        Router::Post { page: _ } => assert!(false, "Wrong type parse"),      };  } | 
