1diff --git a/README.md b/README.md
2index 59cf13b70688f24817edbebcbc7ad01a9706d9be..2a9d334be198dd7701555aba08efc1b1f009dec1 100644
3--- a/README.md
4+++ b/README.md
5@@ -3,7 +3,7 @@
6 After reading [this
7 article](https://www.andreinc.net/2022/04/10/a-blog-that-is-a-single-executable-binary)
8 by Andrei Ciobanu it sparkled in me to do the same thing but with rust.
9-It is going to be a bit bigger than micro though ;)
10+It is going to be a bit bigger than micro ;)
11
12 To achieve that I'll be using the following:
13
14diff --git a/src/assets.rs b/src/assets.rs
15new file mode 100644
16index 0000000000000000000000000000000000000000..2c39d1b71ae6e763aeb0c338f6e729173c2dee39
17--- /dev/null
18+++ b/src/assets.rs
19@@ -0,0 +1,49 @@
20+use chrono::NaiveDate;
21+use regex::Regex;
22+use rust_embed::RustEmbed;
23+use sailfish::TemplateOnce;
24+use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
25+use std::str;
26+
27+pub const BLOG_REGEX: &str = r"(?P<date>[\d]{4}-[\d]{2}-[\d]{2})(?P<title>[a-zA-Z0-9-_]*)";
28+
29+#[derive(RustEmbed)]
30+#[folder = "content/posts/"]
31+pub struct PostAsset;
32+
33+#[derive(TemplateOnce)]
34+#[template(path = "index.html")]
35+pub struct IndexTemplate {
36+ pub posts: Vec<BlogEntry>,
37+}
38+
39+#[derive(TemplateOnce)]
40+#[template(path = "post.html")]
41+pub struct PostTemplate {
42+ pub content: String,
43+ pub title: String,
44+ pub date: String,
45+}
46+
47+#[derive(PartialEq, Eq, PartialOrd, Ord)]
48+pub struct BlogEntry {
49+ pub title: String,
50+ pub datetime: NaiveDate,
51+ pub file: String,
52+}
53+
54+impl BlogEntry {
55+ pub fn new(path: &String) -> BlogEntry {
56+ let re = Regex::new(BLOG_REGEX).unwrap();
57+ let caps = re.captures(path).unwrap();
58+ let date = &caps["date"];
59+ let title = str::replace(&caps["title"], "_", " ");
60+
61+ BlogEntry {
62+ title: String::from(title),
63+ file: String::from(path),
64+ datetime: NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap(),
65+ }
66+ }
67+
68+}
69diff --git a/src/bin/actix.rs b/src/bin/actix.rs
70index 3f00f360f48bf245b9f02814685bf6a687e7d6f5..101fe2e4dc8116e33656ba2f00ad23c5dec0d138 100644
71--- a/src/bin/actix.rs
72+++ b/src/bin/actix.rs
73@@ -1,6 +1,7 @@
74 use actix_web::{get, web, middleware, App, HttpResponse, HttpServer, Responder, http::header::ContentType};
75 use macroblog::blog::{render_index_page, render_post_page};
76-use std::{env};
77+use macroblog::router::blog_post_exists;
78+use std::env;
79
80 #[get("/")]
81 async fn index() -> impl Responder {
82@@ -14,6 +15,12 @@
83
84 #[get("/posts/{name}")]
85 async fn posts(name: web::Path<String>) -> impl Responder {
86+
87+ if !blog_post_exists(&name) {
88+ return HttpResponse::NotFound()
89+ .body("Not Found".to_string());
90+ }
91+
92 let body = render_post_page(&name);
93
94 HttpResponse::Ok()
95diff --git a/src/blog.rs b/src/blog.rs
96index c87730374672bbcc6fc8158177b0229644dc5fd8..eaa314abab08acb9c96c1cf4b2e274c4da566819 100644
97--- a/src/blog.rs
98+++ b/src/blog.rs
99@@ -1,62 +1,18 @@
100-use chrono::NaiveDate;
101 use pulldown_cmark::{html, Options, Parser};
102-use regex::Regex;
103-use rust_embed::RustEmbed;
104 use sailfish::TemplateOnce;
105-use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
106 use std::str;
107+use crate::assets::{BlogEntry, PostAsset, IndexTemplate, PostTemplate};
108
109-const BLOG_REGEX: &str = r"(?P<date>[\d]{4}-[\d]{2}-[\d]{2})(?P<title>[a-zA-Z0-9-_]*)";
110
111-#[derive(RustEmbed)]
112-#[folder = "content/posts/"]
113-struct PostAsset;
114+pub fn read_assets() -> Vec<BlogEntry> {
115+ let mut entries: Vec<BlogEntry> = PostAsset::iter()
116+ .map(|e| format!("{}", e))
117+ .map(|e| BlogEntry::new(&e))
118+ .collect();
119
120-#[derive(TemplateOnce)]
121-#[template(path = "index.html")]
122-struct IndexTemplate {
123- posts: Vec<BlogEntry>,
124-}
125+ entries.sort_by(|a, b| b.datetime.cmp(&a.datetime));
126
127-#[derive(TemplateOnce)]
128-#[template(path = "post.html")]
129-struct PostTemplate {
130- content: String,
131- title: String,
132- date: String,
133-}
134-
135-#[derive(PartialEq, Eq, PartialOrd, Ord)]
136-pub struct BlogEntry {
137- pub title: String,
138- pub datetime: NaiveDate,
139- pub file: String,
140-}
141-
142-impl BlogEntry {
143- pub fn new(path: &String) -> BlogEntry {
144- let re = Regex::new(BLOG_REGEX).unwrap();
145- let caps = re.captures(path).unwrap();
146- let date = &caps["date"];
147- let title = str::replace(&caps["title"], "_", " ");
148-
149- BlogEntry {
150- title: String::from(title),
151- file: String::from(path),
152- datetime: NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap(),
153- }
154- }
155-
156- pub fn read_assets() -> Vec<BlogEntry> {
157- let mut entries: Vec<BlogEntry> = PostAsset::iter()
158- .map(|e| format!("{}", e))
159- .map(|e| BlogEntry::new(&e))
160- .collect();
161-
162- entries.sort_by(|a, b| b.datetime.cmp(&a.datetime));
163-
164- entries
165- }
166+ entries
167 }
168
169 fn get_file_content(path: &str) -> String {
170@@ -67,6 +23,7 @@ let mut html_output = &mut String::new();
171 html::push_html(&mut html_output, parser);
172 return html_output.to_string();
173 }
174+
175
176 pub fn render_post_page(path: &String) -> String {
177 let blog = BlogEntry::new(path);
178@@ -82,7 +39,7 @@ }
179
180 pub fn render_index_page() -> String {
181 IndexTemplate {
182- posts: BlogEntry::read_assets(),
183+ posts: read_assets(),
184 }
185 .render_once()
186 .unwrap()
187diff --git a/src/lib.rs b/src/lib.rs
188index 0c698884bb717efdd6fb15fbe9d622e170a3d0c7..90ddff22d138f459fba27b64fd79bb5c9951a5e8 100644
189--- a/src/lib.rs
190+++ b/src/lib.rs
191@@ -1,2 +1,3 @@
192 pub mod blog;
193 pub mod router;
194+pub mod assets;
195diff --git a/src/router.rs b/src/router.rs
196index 35fdf3e8bed96a76bda385f0242e4757bfa06553..c196ab8419c1cf6fa71737e2083a8672ceb2251b 100644
197--- a/src/router.rs
198+++ b/src/router.rs
199@@ -1,3 +1,6 @@
200+use std::borrow::Borrow;
201+
202+use crate::assets::PostAsset;
203 use regex::Regex;
204
205 const ACTION_REGEX: &str = r"/{0,1}(?P<action>\w*)/(?P<id>.+)";
206@@ -6,6 +9,10 @@ pub enum Router {
207 NotFound,
208 Index,
209 Post { page: String },
210+}
211+
212+pub fn blog_post_exists(name: &str) -> bool {
213+ PostAsset::iter().any(|x| name.eq(&x.to_string()))
214 }
215
216 impl Router {
217@@ -16,6 +23,13 @@ let action = match caps {
218 Some(ref value) => &value["action"],
219 None => "index",
220 };
221+
222+
223+ // this 7 means the "/posts/" from the full path
224+ let trimmed_path: String = path.chars().skip(7).collect();
225+ if action.eq("posts") && !blog_post_exists(&trimmed_path) {
226+ return Router::NotFound;
227+ }
228
229 match action {
230 "posts" => Router::Post {
231diff --git a/tests/test_blog.rs b/tests/test_blog.rs
232index b72f80014b9514a502450953c506968c5c53360c..6cd32495f611123f689f10cf5a47aac0290b8a69 100644
233--- a/tests/test_blog.rs
234+++ b/tests/test_blog.rs
235@@ -1,4 +1,5 @@
236 use macroblog::blog::*;
237+use macroblog::assets::*;
238
239 use chrono::NaiveDate;
240
241@@ -17,7 +18,7 @@
242 #[test]
243 fn test_read_assets() {
244 // This test meant to test if all files are parsed correctly
245- let assets = BlogEntry::read_assets();
246+ let assets = read_assets();
247 assert!(assets.iter().count() > 1)
248 }
249
250diff --git a/tests/test_router.rs b/tests/test_router.rs
251index 7ebe0196cbedddd7831d3a0ded8e62282997c056..cfd4c32bc8b356bfad745090c76a31d304614cdf 100644
252--- a/tests/test_router.rs
253+++ b/tests/test_router.rs
254@@ -1,11 +1,11 @@
255-use macroblog::router::{Router};
256+use macroblog::router::Router;
257
258 #[test]
259 fn test_router_new_posts() {
260- match Router::new("/posts/k8s.html") {
261+ match Router::new("/posts/2021-12-26Enable_NFS_on_K3S.md") {
262 Router::NotFound => assert!(false, "Wrong type parse"),
263 Router::Index => assert!(false, "Wrong type parse"),
264- Router::Post { page } => assert_eq!(page, "k8s.html".to_string())
265+ Router::Post { page } => assert_eq!(page, "2021-12-26Enable_NFS_on_K3S.md".to_string()),
266 };
267 }
268
269@@ -14,7 +14,7 @@ fn test_router_new_index() {
270 match Router::new("/") {
271 Router::Index => assert!(true),
272 Router::NotFound => assert!(false, "Wrong type parse"),
273- Router::Post { page: _ } => assert!(false, "Wrong type parse")
274+ Router::Post { page: _ } => assert!(false, "Wrong type parse"),
275 };
276 }
277
278@@ -23,6 +23,15 @@ fn test_router_new_not_found() {
279 match Router::new("/not_found") {
280 Router::NotFound => assert!(true),
281 Router::Index => assert!(false, "Wrong type parse"),
282- Router::Post { page: _ } => assert!(false, "Wrong type parse")
283+ Router::Post { page: _ } => assert!(false, "Wrong type parse"),
284+ };
285+}
286+
287+#[test]
288+fn test_router_new_not_found_matching_regex() {
289+ match Router::new("/posts/2021-12-03Enable_NFS_on_K3S.html") {
290+ Router::NotFound => assert!(true),
291+ Router::Index => assert!(false, "Wrong type parse"),
292+ Router::Post { page: _ } => assert!(false, "Wrong type parse"),
293 };
294 }