]> luflow.net public git repositories - flow-web.git/blob - src/site/blog_post.rs
Initial commit.
[flow-web.git] / src / site / blog_post.rs
1 // luflow.net web site
2 // AGPL-3.0 License (see LICENSE)
3
4 use chrono::{Datelike, NaiveDateTime};
5 use sailfish::Template;
6 use std::path::PathBuf;
7
8 use crate::site::helper::Helper;
9
10 #[derive(Template)]
11 #[template(path = "blog_post.stpl")]
12 #[derive(Clone, Debug)]
13 pub struct BlogPost {
14 pub base_dir: String,
15
16 pub author: String,
17 pub published: NaiveDateTime,
18 pub published_for_feed: String,
19 pub updated: NaiveDateTime,
20 pub updated_for_feed: String,
21 pub topics_comma_separated: String,
22 pub topics: Vec<String>,
23 pub topics_sanitized: Vec<String>,
24 pub topic_base_dir: String,
25 pub title: String,
26 pub snippet: String,
27 pub html: String,
28 pub url: String,
29 }
30
31 impl BlogPost {
32 pub fn new(base_dir: String) -> BlogPost {
33 BlogPost {
34 base_dir: base_dir,
35
36 author: String::new(),
37 published: NaiveDateTime::parse_from_str("2000-01-01 23:56:04", "%Y-%m-%d %H:%M:%S")
38 .unwrap(),
39 published_for_feed: String::new(),
40 updated: NaiveDateTime::parse_from_str("2000-01-01 23:56:04", "%Y-%m-%d %H:%M:%S")
41 .unwrap(),
42 updated_for_feed: String::new(),
43 topics_comma_separated: String::new(),
44 topics: Vec::new(),
45 topics_sanitized: Vec::new(),
46 topic_base_dir: String::from("topic"),
47 title: String::new(),
48 snippet: String::new(),
49 html: String::new(),
50 url: String::new(),
51 }
52 }
53
54 pub fn get_topics(&self) -> &Vec<String> {
55 return &self.topics;
56 }
57
58 pub fn get_year(&self) -> String {
59 return self.published.year().to_string();
60 }
61
62 pub fn get_published_date(&self) -> &NaiveDateTime {
63 return &self.published;
64 }
65
66 fn get_date_for_feed(&self, date: &NaiveDateTime) -> String {
67 let month = date.month();
68 let day = date.day();
69
70 let mut month_str = month.to_string();
71 let mut day_str = day.to_string();
72
73 if month < 10 {
74 month_str = format!("0{}", month);
75 }
76
77 if day < 10 {
78 day_str = format!("0{}", day);
79 }
80
81 return format!(
82 "{}-{}-{}T{}.000Z",
83 date.year(),
84 month_str,
85 day_str,
86 date.time()
87 );
88 }
89
90 pub fn create_output_dir(&self) {
91 Helper::create_dir_all(
92 &Helper::get_output_dir()
93 .join(&self.base_dir)
94 .join(&self.url),
95 );
96 }
97
98 pub fn generate(&self) {
99 Helper::write_file_sync(
100 &Helper::get_output_dir()
101 .join(&self.base_dir)
102 .join(&self.url)
103 .join("index.html"),
104 &self.render().unwrap().as_bytes(),
105 )
106 .unwrap();
107 }
108 }
109
110 pub async fn parse_markdown_file(path: PathBuf, base_dir: String) -> BlogPost {
111 let contents = tokio::fs::read_to_string(path).await.unwrap();
112 let mut post_start_found = false;
113 let mut markdown = String::new();
114 let mut blog_post = BlogPost::new(base_dir.clone());
115
116 for line in contents.lines() {
117 if !post_start_found && line.len() > 0 {
118 if line == "---" {
119 post_start_found = true;
120 continue;
121 }
122
123 let v: Vec<&str> = line.splitn(2, ':').collect();
124 assert_eq!(v.len(), 2);
125
126 if let Some(key) = v.get(0) {
127 if *key == "author" {
128 if let Some(value) = v.get(1) {
129 blog_post.author = String::from(value.trim());
130 } else {
131 panic!("Unable to parse field: 'author'.");
132 }
133 } else if *key == "published" {
134 if let Some(value) = v.get(1) {
135 blog_post.published =
136 NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S").unwrap();
137 blog_post.published_for_feed =
138 blog_post.get_date_for_feed(&blog_post.published);
139 } else {
140 panic!("Unable to parse field: 'published'.");
141 }
142 } else if *key == "updated" {
143 if let Some(value) = v.get(1) {
144 blog_post.updated =
145 NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S").unwrap();
146 blog_post.updated_for_feed =
147 blog_post.get_date_for_feed(&blog_post.updated);
148 } else {
149 panic!("Unable to parse field: 'updated'.");
150 }
151 } else if *key == "topics" {
152 if let Some(value) = v.get(1) {
153 blog_post.topics_comma_separated = String::from(value.trim());
154 let v = blog_post.topics_comma_separated.split(",");
155 for topic in v {
156 let topic_trimmed = topic.trim();
157 blog_post.topics.push(String::from(topic_trimmed));
158 blog_post
159 .topics_sanitized
160 .push(Helper::sanitize_string(topic_trimmed));
161 }
162 } else {
163 panic!("Unable to parse field: 'topics'.");
164 }
165 } else if *key == "title" {
166 if let Some(value) = v.get(1) {
167 blog_post.title = String::from(value.trim());
168 blog_post.url = Helper::sanitize_string(&blog_post.title);
169 } else {
170 panic!("Unable to parse field: 'title'.");
171 }
172 } else if *key == "snippet" {
173 if let Some(value) = v.get(1) {
174 blog_post.snippet = String::from(value.trim());
175 } else {
176 panic!("Unable to parse field: 'snippet'.");
177 }
178 }
179 }
180 } else {
181 if markdown.len() > 0 {
182 markdown += "\n";
183 }
184 markdown += line;
185 }
186 }
187
188 // convert markdown to html:
189 blog_post.html = markdown::to_html(&markdown);
190
191 return blog_post;
192 }