|
@@ -1,11 +1,9 @@
|
1
|
1
|
use std::env;
|
2
|
|
-use std::ffi::OsStr;
|
3
|
|
-use std::fs::{read_dir, File};
|
|
2
|
+use std::fs::File;
|
4
|
3
|
use std::io::prelude::*;
|
5
|
4
|
use std::io::{Error, ErrorKind};
|
6
|
5
|
use std::path::PathBuf;
|
7
|
6
|
|
8
|
|
-use async_std::fs::read_to_string;
|
9
|
7
|
use chrono::prelude::Local;
|
10
|
8
|
use dotenv;
|
11
|
9
|
use serde::{Deserialize, Serialize};
|
|
@@ -15,8 +13,10 @@ use tide::utils::After;
|
15
|
13
|
use tide::{Body, Response, StatusCode};
|
16
|
14
|
use uuid::Uuid;
|
17
|
15
|
|
|
16
|
+mod fs;
|
|
17
|
+
|
18
|
18
|
#[derive(Debug, Serialize, Deserialize)]
|
19
|
|
-struct Post {
|
|
19
|
+pub struct Post {
|
20
|
20
|
id: String,
|
21
|
21
|
title: String,
|
22
|
22
|
body: String,
|
|
@@ -30,67 +30,26 @@ struct User {
|
30
|
30
|
}
|
31
|
31
|
|
32
|
32
|
impl Post {
|
33
|
|
- fn save(&mut self) -> std::io::Result<()> {
|
34
|
|
- let mut path: PathBuf = get_posts_directory()?;
|
|
33
|
+ async fn save(&mut self) -> std::io::Result<()> {
|
|
34
|
+ let mut path: PathBuf = fs::get_posts_directory_path().await?;
|
35
|
35
|
let filename = format!("{}.json", self.id);
|
36
|
36
|
path = path.join(&filename);
|
37
|
37
|
let mut file = File::create(&path)?;
|
38
|
38
|
file.write_all(serde_json::to_string(&self)?.as_bytes())?;
|
39
|
39
|
Ok(())
|
40
|
40
|
}
|
41
|
|
-}
|
42
|
|
-
|
43
|
|
-fn get_posts_directory() -> Result<PathBuf, Error> {
|
44
|
|
- match env::var("POSTS_DIR") {
|
45
|
|
- Ok(dir) => Ok(dir.into()),
|
46
|
|
- Err(_) => Err(Error::new(
|
47
|
|
- ErrorKind::Other,
|
48
|
|
- "Posts directory environment variable not set",
|
49
|
|
- )),
|
50
|
|
- }
|
51
|
|
-}
|
52
|
41
|
|
53
|
|
-async fn read_all_posts() -> Result<Vec<Post>, Error> {
|
54
|
|
- let path = match get_posts_directory() {
|
55
|
|
- Ok(p) => Ok(p),
|
56
|
|
- Err(_) => Err(Error::new(
|
57
|
|
- ErrorKind::Other,
|
58
|
|
- "POSTS_DIR variable is not set",
|
59
|
|
- )),
|
60
|
|
- }?;
|
61
|
|
- let mut posts: Vec<Post> = vec![];
|
62
|
|
-
|
63
|
|
- let files = match read_dir(path) {
|
64
|
|
- Ok(f) => Ok(f),
|
65
|
|
- Err(_) => Err(Error::new(
|
66
|
|
- ErrorKind::Other,
|
67
|
|
- "Posts directory does not exist",
|
68
|
|
- )),
|
69
|
|
- }?;
|
70
|
|
-
|
71
|
|
- for file in files {
|
72
|
|
- let file = file?;
|
73
|
|
- if let Some("json") = file.path().extension().and_then(OsStr::to_str) {
|
74
|
|
- let contents = match read_to_string(file.path()).await {
|
75
|
|
- Ok(c) => Ok(c),
|
76
|
|
- Err(_) => Err(Error::new(
|
77
|
|
- ErrorKind::Other,
|
78
|
|
- format!("Error reading post {:?}", file.path()),
|
79
|
|
- )),
|
80
|
|
- }?;
|
81
|
|
- let mut post: Post = match serde_json::from_str(&contents) {
|
82
|
|
- Ok(p) => Ok(p),
|
83
|
|
- Err(_) => Err(Error::new(
|
84
|
|
- ErrorKind::Other,
|
85
|
|
- format!("Error deserializing post"),
|
86
|
|
- )),
|
87
|
|
- }?;
|
88
|
|
- post.body = post.body.replace("\n", "<br>");
|
89
|
|
- posts.push(post);
|
90
|
|
- }
|
|
42
|
+ fn from_str(blob: &str) -> Result<Post, Error> {
|
|
43
|
+ let mut post: Post = match serde_json::from_str(blob) {
|
|
44
|
+ Ok(p) => Ok(p),
|
|
45
|
+ Err(_) => Err(Error::new(
|
|
46
|
+ ErrorKind::Other,
|
|
47
|
+ format!("Error deserializing post"),
|
|
48
|
+ )),
|
|
49
|
+ }?;
|
|
50
|
+ post.body = post.body.replace("\n", "<br>");
|
|
51
|
+ Ok(post)
|
91
|
52
|
}
|
92
|
|
-
|
93
|
|
- Ok(posts)
|
94
|
53
|
}
|
95
|
54
|
|
96
|
55
|
fn render(template: &str, context: &Context) -> Result<Body, tide::Error> {
|
|
@@ -132,7 +91,7 @@ async fn main() -> std::io::Result<()> {
|
132
|
91
|
));
|
133
|
92
|
|
134
|
93
|
app.at("/").get(|req: tide::Request<()>| async move {
|
135
|
|
- let posts = read_all_posts().await?;
|
|
94
|
+ let posts: Vec<Post> = fs::get_all_posts().await?;
|
136
|
95
|
let mut context = Context::new();
|
137
|
96
|
context.insert("posts", &posts);
|
138
|
97
|
let logged_in: bool = req.session().get("logged_in").unwrap_or(false);
|
|
@@ -150,11 +109,22 @@ async fn main() -> std::io::Result<()> {
|
150
|
109
|
post.id = Uuid::new_v4().to_string();
|
151
|
110
|
post.date = Local::now().date().naive_local().to_string();
|
152
|
111
|
post.body = post.body.trim().to_owned();
|
153
|
|
- post.save()?;
|
|
112
|
+ post.save().await?;
|
154
|
113
|
Ok(tide::Redirect::new("/"))
|
155
|
114
|
}
|
156
|
115
|
});
|
157
|
116
|
|
|
117
|
+ app.at("/posts/:id")
|
|
118
|
+ .get(|req: tide::Request<()>| async move {
|
|
119
|
+ let mut context = Context::new();
|
|
120
|
+ let logged_in: bool = req.session().get("logged_in").unwrap_or(false);
|
|
121
|
+ context.insert("logged_in", &logged_in);
|
|
122
|
+ let post_id = req.param("id")?;
|
|
123
|
+ let post = fs::get_one_post(post_id).await?;
|
|
124
|
+ context.insert("post", &post);
|
|
125
|
+ render("single.html", &context)
|
|
126
|
+ });
|
|
127
|
+
|
158
|
128
|
app.at("/login")
|
159
|
129
|
.get(|mut req: tide::Request<()>| async move {
|
160
|
130
|
let mut context = Context::new();
|