summaryrefslogblamecommitdiff
path: root/src/main.rs
blob: ca0a3d90973a36782336967fbd354f4a5539ec45 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
                                   












                                 
                        
                   
                                       


                                      













                                                                  
                          



                                                                      

                                        






































                                                                

                                           







                               

                                            





                                                     










                                                                                                             


     


















                                                                              





                                                                          






                                                                  



                                                                                
use tower_http::services::ServeDir;
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: fmt::Debug {
    fn dump(&self);
    fn display_name(&self) -> Cow<str>;
    fn get_content(&self) -> Cow<str>;
}

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 Post for MdFilePost {
    fn dump(&self) {
        println!("{}: {}", &self.display_name(), &self.get_content());
    }

    fn display_name(&self) -> Cow<str> {
        self.name.to_string_lossy()
    }

    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: Template {
    fn from_post(post: &impl Post) -> Self;
}

#[derive(Template)]
#[template(path = "post.html")]
struct MdPage {
    article: String,
}

impl Page for MdPage {
    fn from_post(post: &impl Post) -> Self {
        Self {
            article: post.get_content().into_owned(),
        }
    }
}

#[derive(Template)]
#[template(path = "posts.html")]
struct PostsView {
    post_titles: Vec<String>
}

impl PostsView {
    fn with_posts(posts: &impl Posts) -> Self {
        Self {
            post_titles: posts.get_posts().map(|p| p.display_name().into_owned()).map(String::from).collect()
        }
    }
}

async fn posts_handler(State(posts): State<Arc<impl Posts>>) -> Html<String> {
    let view = PostsView::with_posts(&*posts);
    Html(view.render().unwrap())
}

#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate;

async fn index_handler() -> Html<String> {
    Html(IndexTemplate {}.render().unwrap())
}

#[derive(Template)]
#[template(path = "policies.html")]
struct PoliciesTemplate;

async fn policies_handler() -> Html<String> {
    Html(PoliciesTemplate{}.render().unwrap())
}

#[tokio::main]
async fn main() {
    let repo = Arc::new(FsDirPosts::new(&format!("/data/ct/{}", "blog")));

    let app = Router::new()
        .nest_service("/static", ServeDir::new("static"))
        .nest_service("/assets", ServeDir::new("/data/ct/assets"))
        .route("/posts", get(posts_handler))
        .route("/policies", get(policies_handler))
        .route("/", get(index_handler))
        .with_state(repo);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}