#![feature(proc_macro_hygiene, decl_macro)] extern crate serde; extern crate serde_json; #[macro_use] extern crate rocket; extern crate rocket_contrib; use rocket::response::Redirect; use rocket::{Rocket, State}; use rocket_contrib::serve::StaticFiles; use rocket_contrib::templates::Template; use rand::distributions::Uniform; use rand::Rng; use chgk_ledb_lib::db; use chgk_ledb_lib::questions::Question; const DB_FILENAME: &str = "db.dat"; trait ErrorEmpty { type Output; fn err_empty(self) -> Result; } impl ErrorEmpty for Result { type Output = T; fn err_empty(self) -> Result { self.map_err(|_| ()) } } struct AppState{ database_distribution: Uniform, } fn open_db() -> db::Reader { db::Reader::new(DB_FILENAME, 2048).expect("new db reader") } fn get_database_distribution() -> Uniform { let last_id = open_db().len(); rand::distributions::Uniform::new_inclusive(1usize, last_id) } fn random_question_id(database_distribution: &Uniform) -> usize { let mut rng = rand::thread_rng(); rng.sample(database_distribution) } fn get_question(id: usize) -> Result { open_db().get(id - 1).err_empty() } fn show_question_details(template_name: &'static str, data: &AppState, id: usize) -> Template { match get_question(id) { Ok(question) => { let mut context = serde_json::to_value(question).expect("question serialize"); if context.is_object() { let next_id = random_question_id(&data.database_distribution); context["next"] = serde_json::to_value(next_id).expect("question id serialize"); } Template::render(template_name, &context) } Err(_) => { use std::collections::HashMap; let context: HashMap = HashMap::new(); Template::render("404", &context) } } } #[get("/q/")] fn show_question(data: State, id: usize) -> Template { show_question_details("question", data.inner(), id) } #[get("/q//a")] fn show_answer(data: State, id: usize) -> Template { show_question_details("answer", data.inner(), id) } #[get("/q/0")] fn question0() -> Redirect { Redirect::to("/") } #[get("/q/0/a")] fn answer0() -> Redirect { Redirect::to("/") } #[get("/")] fn index(data: State) -> Redirect { let id = random_question_id(&data.database_distribution); Redirect::temporary(format!("/q/{}", id)) } #[catch(404)] fn not_found(_req: &rocket::Request) -> Template { use std::collections::HashMap; let context: HashMap = HashMap::new(); Template::render("404", &context) } fn rocket() -> Rocket { let state = AppState { database_distribution: get_database_distribution(), }; rocket::ignite() .manage(state) .register(catchers![not_found]) .mount( "/", routes![index, show_question, show_answer, question0, answer0], ) .mount("/q", routes![index]) .mount("/q/static", StaticFiles::from("static/")) .attach(Template::fairing()) } fn main() { rocket().launch(); }