#![feature(proc_macro_hygiene, decl_macro)] extern crate serde; #[macro_use] extern crate serde_derive; extern crate serde_json; extern crate ledb; #[macro_use] extern crate ledb_derive; extern crate ledb_types; //#[macro_use] //extern crate tera; //use tera::Context; #[macro_use] extern crate rocket; extern crate rocket_contrib; use rocket::{Rocket, State}; use rocket::response::Redirect; use rocket_contrib::serve::StaticFiles; use rocket_contrib::templates::Template; use rand::seq::IteratorRandom; use ledb::{Options, Storage}; #[derive(Debug, Default, Clone, Serialize, Deserialize, Document)] struct BatchInfo { #[document(primary)] #[serde(default)] filename: String, #[serde(default)] description: String, #[serde(default)] author: String, #[serde(default)] comment: String, #[serde(default)] url: String, #[serde(default)] date: String, #[serde(default)] processed_by: String, #[serde(default)] redacted_by: String, #[serde(default)] copyright: String, #[serde(default)] theme: String, #[serde(default)] kind: String, #[serde(default)] source: String, #[serde(default)] rating: String, } #[derive(Debug, Default, Clone, Serialize, Deserialize, Document)] struct Question { #[document(primary)] #[serde(default)] num: u32, #[document(index)] id: String, description: String, answer: String, #[serde(default)] author: String, #[serde(default)] comment: String, #[serde(default)] comment1: String, #[serde(default)] tour: String, #[serde(default)] url: String, #[serde(default)] date: String, #[serde(default)] processed_by: String, #[serde(default)] redacted_by: String, #[serde(default)] copyright: String, #[serde(default)] theme: String, #[serde(default)] kind: String, #[serde(default)] source: String, #[serde(default)] rating: String, #[document(nested)] #[serde(default)] batch_info: BatchInfo, } struct AppState { storage: Storage, } fn get_question(storage: &Storage, id: u32) -> Result, ()> { if 0 == id { return Ok(None); } let collection = storage.collection("questions").expect("collection \"questions\""); let last_id = collection.last_id().expect("\"questions\" last_id"); if id > last_id { Err(()) } else { let question = collection.get::(id); if question.is_err() { Err(()) } else { Ok(question.unwrap()) } } } fn show_question_details(template_name: &'static str, data: &AppState, id: u32) -> Result { let question = get_question(&data.storage, id); if question.is_ok() { let question = question.unwrap(); if question.is_some() { let question = question.unwrap(); Ok(Template::render(template_name, &question)) } else { Err(Redirect::permanent("/q/")) } } else { use std::collections::HashMap; let context: HashMap = HashMap::new(); Ok(Template::render("404", &context)) } } #[get("/q/")] fn show_question(data: State, id: u32) -> Result { show_question_details("question", data.inner(), id) } #[get("/q//a")] fn show_answer(data: State, id: u32) -> Result { show_question_details("answer", data.inner(), id) } #[get("/")] fn index(data: State) -> Redirect { let mut rng = rand::thread_rng(); let collection = data.storage.collection("questions").expect("collection \"questions\""); let last_id = collection.last_id().expect("\"questions\" last_id"); let id = (1..(last_id + 1)).choose(&mut rng).unwrap(); 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 options: Options = serde_json::from_value(serde_json::json!({ "read_only": true, "no_lock": true, })) .unwrap(); let state = AppState { storage: Storage::new("db", options).expect("open db") }; let collection = state.storage.collection("questions").expect("collection \"questions\""); collection.last_id().expect("\"questions\" last_id"); rocket::ignite() .manage(state) .register(catchers![not_found]) .mount("/", routes![index, show_question, show_answer]) .mount("/q", routes![index]) .mount("/q/static", StaticFiles::from("static/")) .attach(Template::fairing()) } fn main() { rocket().launch(); }