aboutsummaryrefslogblamecommitdiff
path: root/backend/src/main.rs
blob: 0474f892f507ba8f4f9a07f3bb8956575c6cecf0 (plain) (tree)































































































































































                                                                                                                                          
mod config;
mod monolith;
mod render;

use std::env;
use std::collections::hash_map::{HashMap, RandomState};

enum CGIStatusCode {
    S200,
    C400,
    C404,
    C405
}

type CGIHTTPHeaders = Vec<(String, String)>;

struct CGIResponse {
    status: CGIStatusCode,
    headers: CGIHTTPHeaders,
    body: String
}

fn cgi_handle_request(conf: &config::Config) -> Result<CGIResponse, CGIResponse> {
    let headers = vec![
        (String::from("Allow"), String::from("GET")),
        (String::from("Content-type"), String::from("text/html"))
    ];
    let mkerr = |status| CGIResponse {
        status,
        headers: headers.clone(),
        body: String::from("")
    };
    let request_method = env::var("REQUEST_METHOD").map_err(|_| mkerr(CGIStatusCode::C400))?;
    if request_method != "GET" {
        return Err(mkerr(CGIStatusCode::C405));
    }
    let query: HashMap<_, _, RandomState> = 
        if let Ok(query_string) = env::var("QUERY_STRING") {
            if query_string.len() > 0 {
                HashMap::from_iter(query_string.split('&').map(|qi| {
                    let (k, v) = config::split_at_first(qi, "=");
                    (String::from(k), String::from(v))
                }))
            } else { HashMap::from([(String::from("page"), String::from("1"))]) }
        } else { HashMap::from([(String::from("page"), String::from("1"))]) };
    let m = monolith::Monolith::new(String::from("posts.monolith"));
    if let Some(ps) = query.get("page") {
        let p = usize::from_str_radix(&ps, 10).map_err(|_| mkerr(CGIStatusCode::C400))?.checked_sub(1).ok_or(mkerr(CGIStatusCode::C404))?;
        let ps = m.get_page_posts(p).ok_or(mkerr(CGIStatusCode::C404))?;
        let r = render::Renderer::load("./template");
        return Ok(CGIResponse {
            status: CGIStatusCode::S200,
            headers,
            body: r.render_page(ps, p, m.get_page_count(), conf)
        });
    } else if let Some(ds) = query.get("post") {
        let d = i64::from_str_radix(&ds, 10).map_err(|_| mkerr(CGIStatusCode::C400))?;
        let p = m.get_post_2(d).ok_or(mkerr(CGIStatusCode::C404))?;
        let r = render::Renderer::load("./template");
        return Ok(CGIResponse {
            status: CGIStatusCode::S200,
            headers,
            body: r.render_single_post(p, conf)
        });
    }
    Err(mkerr(CGIStatusCode::C400))
}

fn cgimain(conf: config::Config) -> Result<(), &'static str> {
    let r = match cgi_handle_request(&conf) {
        Ok(r) => r,
        Err(r) => r
    };
    let (status, status_str) = match r.status {
        CGIStatusCode::S200 => (200, "OK"),
        CGIStatusCode::C400 => (400, "Bad Request"),
        CGIStatusCode::C404 => (404, "Not Found"),
        CGIStatusCode::C405 => (405, "Method Not Allowed")
    };
    print!("Status: {} {}\r\n", status, status_str);
    r.headers.iter().for_each(|(f, v)| print!("{}: {}\r\n", f, v));
    print!("\r\n");
    if status < 400 {
        print!("{}", r.body);
    } else {
        let rdr = render::Renderer::load("./template");
        print!("{}", rdr.render_error(status, String::from(status_str), &conf));
    }
    Ok(())
}

fn dbgmain(conf: config::Config) -> Result<(), &'static str> {
    eprintln!("in debug mode");
    eprintln!("notekins version {}", conf.get("VERSION_STRING"));
    let mut m = monolith::Monolith::new(String::from("posts.monolith"));
    let mut args = env::args();
    args.next();
    if let Some(dbgop) = args.next() {
        match dbgop.as_str() {
            "get_post" => {
                let tss = args.next().ok_or("missing timestamp")?;
                let ts = i64::from_str_radix(&tss, 10).map_err(|_| "invalid timestamp")?;
                m.load_index();
                let p = m.get_post(ts).ok_or("post not found")?;
                monolith::test_print_post(&p);
                Ok(())
            },
            "get_post2" => {
                let tss = args.next().ok_or("missing timestamp")?;
                let ts = i64::from_str_radix(&tss, 10).map_err(|_| "invalid timestamp")?;
                let p = m.get_post_2(ts).ok_or("post not found")?;
                monolith::test_print_post(&p);
                Ok(())
            },
            "get_page" => {
                let pgs = args.next().ok_or("missing page")?;
                let pg = usize::from_str_radix(&pgs, 10).map_err(|_| "invalid page")?;
                let ps = m.get_page_posts(pg).ok_or("page out of range")?;
                for p in ps {
                    monolith::test_print_post(&p);
                }
                Ok(())
            },
            "render_page" => {
                let pgs = args.next().ok_or("missing page")?;
                let pg = usize::from_str_radix(&pgs, 10).map_err(|_| "invalid page")?;
                let ps = m.get_page_posts(pg).ok_or("page out of range")?;
                let r = render::Renderer::load("./template");
                println!("{}", r.render_page(ps, pg, m.get_page_count(), &conf));
                Ok(())
            },
            "render_post" => {
                let tss = args.next().ok_or("missing timestamp")?;
                let ts = i64::from_str_radix(&tss, 10).map_err(|_| "invalid timestamp")?;
                let p = m.get_post_2(ts).ok_or("post not found")?;
                let r = render::Renderer::load("./template");
                println!("{}", r.render_single_post(p, &conf));
                Ok(())
            },
            _ => Err("unsupported debug option")
        }
    } else {
        m.load_index();
        let dates = m.get_all_dates();
        for d in dates {
            let p = m.get_post(d);
            println!("{:?}", p)
        }
        Ok(())
    }
}

fn main() -> Result<(), &'static str> {
    let conf = config::Config::parse_config("notekins.conf");
    if let Ok(_) = env::var("SERVER_SOFTWARE") {
        cgimain(conf)
    } else {
        dbgmain(conf)
    }
}