macroblog.rs @ 16ef06ffb363b6304d0d5269b30dedbdd07288df

  1pub mod router;
  2
  3use std::convert::Infallible;
  4use rust_embed::RustEmbed;
  5use std::{env, str};
  6use std::net::SocketAddr;
  7use hyper::{Body, Request, Response, Server};
  8use hyper::service::{make_service_fn, service_fn};
  9use sailfish::TemplateOnce;
 10use ::router::Router;
 11
 12struct PostEntry {
 13    title: String,
 14    file: String,
 15}
 16
 17#[derive(TemplateOnce)]
 18#[template(path = "index.html")]
 19struct IndexTemplate {
 20    posts: Vec<PostEntry>,
 21}
 22
 23#[derive(TemplateOnce)]
 24#[template(path = "post.html")]
 25struct PostTemplate {
 26    content: String,
 27}
 28
 29#[derive(RustEmbed)]
 30#[folder = "content/posts/"]
 31struct PostAsset;
 32
 33
 34fn get_file_content(path: &str) -> String {
 35    let buffer = PostAsset::get(path)
 36        .unwrap()
 37        .data
 38        .into_owned();
 39
 40    return String::from_utf8(buffer).unwrap();
 41}
 42
 43fn get_post_entry(path: &String) -> PostEntry {
 44    let sub_title = str::replace(path, "_", " ");
 45    let title = str::replace(sub_title.as_str(), ".html", "");
 46    PostEntry {
 47        title: String::from(title),
 48        file: String::from(path),
 49    }
 50}
 51
 52fn get_post_title() -> Vec<PostEntry> {
 53    PostAsset::iter()
 54        .map(|e| format!("{}", e))
 55        .map(|e| get_post_entry(&e))
 56        .collect()
 57}
 58
 59
 60async fn not_found() -> Result<Response<Body>, Infallible> {
 61    let resp: Response<Body> = Response::builder()
 62        .status(404)
 63        .body("Not Found".into())
 64        .unwrap();
 65    Ok(resp)
 66}
 67
 68
 69async fn index() -> Result<Response<Body>, Infallible> {
 70    let files = get_post_title();
 71    let body = IndexTemplate { posts: files }
 72        .render_once()
 73        .unwrap();
 74
 75    let resp: Response<Body> = Response::builder()
 76        .status(200)
 77        .header("posts-type", "text/html")
 78        .body(body.into())
 79        .unwrap();
 80
 81    Ok(resp)
 82}
 83
 84
 85async fn post(path: &String) -> Result<Response<Body>, Infallible> {
 86    let body = PostTemplate { content: get_file_content(path) }
 87        .render_once()
 88        .unwrap();
 89
 90    let resp: Response<Body> = Response::builder()
 91        .status(200)
 92        .header("posts-type", "text/html")
 93        .body(body.into())
 94        .unwrap();
 95
 96    Ok(resp)
 97}
 98
 99async fn request(req: Request<Body>) -> Result<Response<Body>, Infallible> {
100    let path = req.uri().path();
101
102    match Router::new(path) {
103        Router::Index => index().await,
104        Router::Post { page } => post(&page).await,
105        Router::NotFound => not_found().await
106    }
107}
108
109
110#[tokio::main]
111async fn main() {
112    let port = env::var("PORT").unwrap_or("3000".into()).parse::<u16>().unwrap_or(3000);
113    let addr = SocketAddr::from(([0, 0, 0, 0], port));
114
115    let make_svc = make_service_fn(|_conn| async {
116        Ok::<_, Infallible>(service_fn(request))
117    });
118
119    let server = Server::bind(&addr).serve(make_svc);
120
121    if let Err(e) = server.await {
122        eprintln!("server error: {}", e);
123    }
124}