1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
use axum::response::Html;
use axum::extract::State;
use std::sync::Arc;
use askama_axum::Template;
use axum::{routing::get, Router};
use std::borrow::Cow;
use std::ffi::OsString;
use std::fmt;
use std::fs;
use std::io::*;
use std::path::Path;
use std::path::PathBuf;
trait Post<'a>: fmt::Debug {
fn dump(&self);
fn display_name(&'a self) -> &'a str;
fn get_content(&self) -> Cow<str>;
}
#[derive(Debug)]
struct MockPost;
impl<'a> Post<'a> for MockPost {
fn dump(&self) {
println!("Post content goes here.");
}
fn display_name(&'a self) -> &'a str {
""
}
fn get_content(&self) -> Cow<str> {
Cow::Borrowed("")
}
}
struct MdFilePost {
path: PathBuf,
name: OsString,
}
impl MdFilePost {
fn new(path: &Path) -> Self {
Self {
path: path.to_path_buf(),
name: path.file_stem().unwrap_or_default().to_owned(),
}
}
}
impl<'a> Post<'a> for MdFilePost {
fn dump(&self) {
println!("{}: {}", &self.display_name(), &self.get_content());
}
fn display_name(&'a self) -> &'a str {
self.name.to_str().unwrap()
}
fn get_content(&self) -> Cow<str> {
let mut file = std::fs::File::open(&self.path).unwrap();
let mut markdown = String::new();
file.read_to_string(&mut markdown).unwrap();
Cow::Owned(markdown)
}
}
impl fmt::Debug for MdFilePost {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MdFilePost '{}'", self.display_name())
}
}
trait Posts {
fn get_posts(&self) -> impl Iterator<Item = impl Post>;
}
struct FsDirPosts {
path: PathBuf,
}
impl FsDirPosts {
fn new(path: &str) -> Self {
Self {
path: PathBuf::from(path),
}
}
}
impl Posts for FsDirPosts {
fn get_posts(&self) -> impl Iterator<Item = impl Post> {
let dirs = fs::read_dir(&self.path).unwrap();
dirs.flatten().map(|d| MdFilePost::new(&d.path()))
}
}
trait Page<'a>: Template {
fn from_post(post: &impl Post<'a>) -> Self;
}
#[derive(Template)]
#[template(path = "post.html")]
struct MdPage {
article: String,
}
impl<'a> Page<'a> for MdPage {
fn from_post(post: &impl Post<'a>) -> Self {
Self {
article: post.get_content().into_owned(),
}
}
}
fn test_get_posts(posts: &impl Posts) {
for post in posts.get_posts() {
let page = MdPage::from_post(&post);
println!("{}", page.render().unwrap());
}
}
async fn handler(State(posts): State<Arc<impl Posts>>) -> Html<String> {
let post = posts.get_posts().next().unwrap();
let page = MdPage::from_post(&post);
Html(page.render().unwrap())
}
#[tokio::main]
async fn main() {
let repo = Arc::new(FsDirPosts::new(&format!("/data/ct/{}", "blog")));
let app = Router::new().route("/posts", get(handler)).with_state(repo);
let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
|