1use chrono::NaiveDate;
2use pulldown_cmark::{html, Options, Parser};
3use regex::Regex;
4use rust_embed::RustEmbed;
5use sailfish::TemplateOnce;
6use std::cmp::{Eq, Ord, PartialEq, PartialOrd};
7use std::str;
8
9const BLOG_REGEX: &str = r"(?P<date>[\d]{4}-[\d]{2}-[\d]{2})(?P<title>[a-zA-Z0-9-_]*)";
10
11#[derive(RustEmbed)]
12#[folder = "content/posts/"]
13struct PostAsset;
14
15#[derive(TemplateOnce)]
16#[template(path = "index.html")]
17struct IndexTemplate {
18 posts: Vec<BlogEntry>,
19}
20
21#[derive(TemplateOnce)]
22#[template(path = "post.html")]
23struct PostTemplate {
24 content: String,
25 title: String,
26 date: String,
27}
28
29#[derive(PartialEq, Eq, PartialOrd, Ord)]
30pub struct BlogEntry {
31 pub title: String,
32 pub datetime: NaiveDate,
33 pub file: String,
34}
35
36impl BlogEntry {
37 pub fn new(path: &String) -> BlogEntry {
38 let re = Regex::new(BLOG_REGEX).unwrap();
39 let caps = re.captures(path).unwrap();
40 let date = &caps["date"];
41 let title = str::replace(&caps["title"], "_", " ");
42
43 BlogEntry {
44 title: String::from(title),
45 file: String::from(path),
46 datetime: NaiveDate::parse_from_str(date, "%Y-%m-%d").unwrap(),
47 }
48 }
49
50 pub fn read_assets() -> Vec<BlogEntry> {
51 let mut entries: Vec<BlogEntry> = PostAsset::iter()
52 .map(|e| format!("{}", e))
53 .map(|e| BlogEntry::new(&e))
54 .collect();
55
56 entries.sort_by(|a, b| b.datetime.cmp(&a.datetime));
57
58 entries
59 }
60}
61
62fn get_file_content(path: &str) -> String {
63 let buffer = PostAsset::get(path).unwrap().data.into_owned();
64 let md = String::from_utf8(buffer).unwrap();
65 let parser = Parser::new_ext(&md, Options::empty());
66 let mut html_output = &mut String::new();
67 html::push_html(&mut html_output, parser);
68 return html_output.to_string();
69}
70
71pub fn render_post_page(path: &String) -> String {
72 let blog = BlogEntry::new(path);
73
74 PostTemplate {
75 content: get_file_content(path),
76 title: blog.title,
77 date: blog.datetime.format("%Y-%m-%d").to_string(),
78 }
79 .render_once()
80 .unwrap()
81}
82
83pub fn render_index_page() -> String {
84 IndexTemplate {
85 posts: BlogEntry::read_assets(),
86 }
87 .render_once()
88 .unwrap()
89}