Static site generator + my presonnal website written in rust for some reason.
1use std::{error::Error, fs::{self, read_dir, DirBuilder, File}, io::Write, path::Path};
2
3use ascii::AsciiString;
4use tiny_http::{Response, Server};
5
6
7mod handlers;
8mod structs;
9pub mod blog_entries;
10pub mod rand_quote;
11
12
13
14fn main() {
15 let output_path = "output";
16 match DirBuilder::new()
17 .recursive(true)
18 .create(output_path) {
19 Ok(_) => (),
20 Err(_) => (),
21 }
22
23 match copy_dir_all("assets", format!("{output_path}/assets")) {
24 Ok(_) => (),
25 Err(err) => println!("Couldnt copy assets directory, err: {err}"),
26 }
27
28
29
30 fs::write(format!("{output_path}/index.html"), handlers::index().as_bytes()).expect("Couldnt write index file");
31 fs::write(format!("{output_path}/about.html"), handlers::about().as_bytes()).expect("Couldnt write about file");
32 fs::write(format!("{output_path}/404.html"), handlers::not_found().as_bytes()).expect("Couldnt write 404 file");
33
34 match DirBuilder::new()
35 .create(format!("{output_path}/blog")) {
36 Ok(_) => (),
37 Err(_) => (),
38 }
39
40
41 let post_dir_iter = match read_dir("posts/") {
42 Ok(iter) => iter,
43 Err(err) => panic!("could ls files, err {err}")
44 };
45
46 for entry in post_dir_iter {
47 if let Ok(entry) = entry {
48
49 let filename = entry.file_name().into_string().unwrap().split(".").collect::<Vec<_>>()[0].to_string();
50
51 fs::write(format!("{output_path}/blog/{filename}.html"), handlers::blog(filename).as_bytes()).expect("Couldnt write blog entry");
52 };
53
54 }
55
56}
57fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
58 std::fs::create_dir_all(&dst)?;
59 for entry in std::fs::read_dir(src)? {
60 let entry = entry?;
61 let ty = entry.file_type()?;
62 if ty.is_dir() {
63 copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
64 } else {
65 std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
66 }
67 }
68 Ok(())
69}
70
71fn old_main() {
72
73 // let app: axum::Router = Router::new()
74 // .route("/", get(handlers::index))
75 // .route("/about", get(handlers::about))
76 // .route("/blog",get(handlers::index))
77 // .route("/blog/:blog_path",get(handlers::blog))
78 // .route_service("/robots.txt", ServeFile::new("assets/robots.txt"))
79 // .fallback(get(handlers::not_found))
80 // .nest_service("/assets", ServeDir::new("assets"));
81 //
82 //
83 // let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
84 // axum::serve(listener, app).await.unwrap();
85 //
86 //-----
87 // let server = Arc::new(Server::http("127.0.0.1:3001").unwrap());
88 // println!("started server on port 3001");
89 //
90 // let mut handles = Vec::new();
91 //
92 // for thread_num in 0..6 {
93 // println!("starting thread {}",thread_num);
94 // let server = server.clone();
95 // handles.push(thread::spawn(move || server_thread(server)));
96 //
97 // }
98 // for h in handles {
99 // h.join().unwrap();
100 // }
101 // ------------
102 let server = Server::http("127.0.0.1:3000").unwrap();
103 println!("started server on port 3001");
104 server_thread(server);
105
106}
107
108fn server_thread(server: Server) {
109 for request in server.incoming_requests() {
110 println!("received request; method: {}, url: {}",
111 request.method(),request.url());
112
113 match request.url() {
114 "" | "/" => {
115 let response = Response::from_string(handlers::index());
116 let response = response.with_header(tiny_http::Header {
117 field: "Content-Type".parse().unwrap(),
118 value: AsciiString::from_ascii("text/html;charset=utf8").unwrap(),
119 });
120
121 let _ = request.respond(response);
122
123 }
124 "/about" => {
125 let response = Response::from_string(handlers::about());
126 let response = response.with_header(tiny_http::Header {
127 field: "Content-Type".parse().unwrap(),
128 value: AsciiString::from_ascii("text/html;charset=utf8").unwrap(),
129 });
130
131 let _ = request.respond(response);
132
133 }
134 asset_url @ _ if asset_url.starts_with("/assets/") => {
135 println!("getting asset : {}",asset_url);
136 if let Some(asset) = get_asset(&asset_url){
137 let _ = request.respond(asset);
138 }
139
140 }
141 blog_url @ _ if blog_url.starts_with("/blog/") => {
142
143
144 let response = Response::from_string(handlers::blog(blog_url[6..].to_string()));
145 let response = response.with_header(tiny_http::Header {
146 field: "Content-Type".parse().unwrap(),
147 value: AsciiString::from_ascii("text/html;charset=utf8").unwrap(),
148 });
149
150 let _ = request.respond(response);
151
152 }
153 &_ => {
154
155 let response = Response::from_string(handlers::not_found());
156 let response = response.with_header(tiny_http::Header {
157 field: "Content-Type".parse().unwrap(),
158 value: AsciiString::from_ascii("text/html;charset=utf8").unwrap(),
159 });
160
161 let _ = request.respond(response);
162 }
163 }
164 }
165}
166
167
168
169fn get_asset(asset:&str)-> Option<Response<File>>{
170
171 match Path::new(&asset[1..]).exists() {
172 true => {
173 println!("found asset");
174 Some(Response::from_file(File::open(&asset[1..]).unwrap()))
175 }
176 false => None,
177 }
178
179}
180
181
182
183
184
185
186
187
188
189