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)
}
}