diff --git a/src/main.rs b/src/main.rs index b7ab9b0..6519446 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,10 @@ extern crate rocket; use rocket::fs::FileServer; use rocket::fs::Options; +use rocket::http::ContentType; +use rocket::request::Request; use rocket::response::Redirect; +use rocket::response::{self, Responder, Response}; use rocket::State; use rocket_dyn_templates::tera; use rocket_dyn_templates::Template; @@ -54,11 +57,21 @@ impl WebError { } } +/// wrapper for terra:Value (json, context), to cache values by ref +/// implements Responder as json content type #[derive(Clone)] struct ArcTemplateData { value: Arc<tera::Value>, } +impl Deref for ArcTemplateData { + type Target = tera::Value; + + fn deref(&self) -> &Self::Target { + self.value.deref() + } +} + impl ArcTemplateData { fn new(value: tera::Value) -> ArcTemplateData { ArcTemplateData { @@ -66,7 +79,15 @@ impl ArcTemplateData { } } fn render(&self, name: &'static str) -> Template { - Template::render(name, self.value.deref()) + Template::render(name, self.deref()) + } +} + +impl<'r> Responder<'r, 'static> for ArcTemplateData { + fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { + Response::build_from(self.to_string().respond_to(req)?) + .header(ContentType::JSON) + .ok() } } @@ -101,14 +122,13 @@ async fn get_question(db: &DataBase, id: usize) -> Result<Question, ()> { db.get(id - 1).await.err_empty() } -async fn show_question_details( - template_name: &'static str, +async fn get_question_data( data: &AppState, cache: &TemplateCache, id: usize, -) -> Option<Template> { +) -> Option<ArcTemplateData> { if let Some(value) = cache.get(&id) { - return Some(value.render(template_name)); + return Some(value); } match get_question(&data.db, id).await { @@ -119,16 +139,34 @@ async fn show_question_details( context["next"] = tera::to_value(next_id).expect("question id serialize"); } - let value = ArcTemplateData::new(context); - let result = value.render(template_name); - cache.insert(id, value); - - Some(result) + let data = ArcTemplateData::new(context); + cache.insert(id, data.clone()); + Some(data) } Err(_) => None, } } +async fn show_question_details( + template_name: &'static str, + data: &AppState, + cache: &TemplateCache, + id: usize, +) -> Option<Template> { + get_question_data(data, cache, id) + .await + .map(|data| data.render(template_name)) +} + +#[get("/q/<id>/json")] +async fn json_question( + data: &State<AppState>, + cache: &State<TemplateCache>, + id: usize, +) -> Option<ArcTemplateData> { + get_question_data(data.inner(), cache.inner(), id).await +} + #[get("/q/<id>")] async fn show_question( data: &State<AppState>, @@ -157,6 +195,12 @@ fn answer0() -> Redirect { Redirect::to("/") } +#[get("/q/0/json")] +fn json0(data: &State<AppState>) -> Redirect { + let id = random_question_id(&data.database_distribution); + Redirect::temporary(format!("/q/{}/json", id)) +} + #[get("/")] fn index(data: &State<AppState>) -> Redirect { let id = random_question_id(&data.database_distribution); @@ -190,7 +234,15 @@ async fn rocket() -> _ { .register("/", catchers![not_found, server_error]) .mount( "/", - routes![index, show_question, show_answer, question0, answer0], + routes![ + index, + json_question, + show_question, + show_answer, + question0, + answer0, + json0 + ], ) .mount("/q", routes![index]) .mount("/q/static", FileServer::new("static/", Options::None))