From 6efbfc0e9c6e0280da62ef0e6ce244972caffd97 Mon Sep 17 00:00:00 2001 From: Ondrej Vlach Date: Mon, 27 Nov 2023 01:19:10 +0100 Subject: [PATCH] translations #2 --- Cargo.lock | 15 ++++++++ Cargo.toml | 3 +- src/lib.rs | 9 ++--- src/routes/root.rs | 21 +++++++----- src/tools/mod.rs | 3 +- src/tools/rocket.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++ src/tools/tera.rs | 16 ++++----- 7 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 src/tools/rocket.rs diff --git a/Cargo.lock b/Cargo.lock index 67aa067..7ceb326 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1288,6 +1288,7 @@ dependencies = [ "fluent-resmgr", "log", "ovlach_data", + "phf", "reqwest", "rocket", "rocket_dyn_templates", @@ -1410,6 +1411,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros", "phf_shared", ] @@ -1433,6 +1435,19 @@ dependencies = [ "rand", ] +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "phf_shared" version = "0.11.2" diff --git a/Cargo.toml b/Cargo.toml index f230c32..d7ea420 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,5 @@ chrono = "0.4.31" sha256 = "1.4.0" fluent-bundle = "0.15.2" fluent-resmgr = "0.0.6" -unic-langid = "0.9.1" \ No newline at end of file +unic-langid = "0.9.1" +phf = { version = "0.11.2", features = ["macros"] } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 253b307..1fc377b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,6 @@ -use std::collections::HashMap; - use rocket::{*, fairing::AdHoc}; -use rocket_dyn_templates::{Template, tera::Value}; +use rocket_dyn_templates::Template; use ::serde::Deserialize; use tools::tera::{static_filter, translate_filter, calculate_age, insert_space_every, lang_entity, gravatar_link}; @@ -44,7 +42,10 @@ pub fn rocket_builder() -> Rocket { AdHoc::config::() ).attach( AdHoc::config::() + ).attach( + tools::rocket::RequestTimer ).mount("/", routes![ - routes::root::index + routes::root::index, + routes::root::index_without_lang ]) } \ No newline at end of file diff --git a/src/routes/root.rs b/src/routes/root.rs index fcd7bd6..7f9a364 100644 --- a/src/routes/root.rs +++ b/src/routes/root.rs @@ -1,10 +1,10 @@ use log::error; use ovlach_data::cv::cv::CV; -use rocket::{get, State, response::status::NotFound, http::Status}; -use rocket_dyn_templates::{Template, context}; +use rocket::{get, State, response::Redirect, http::Status}; +use rocket_dyn_templates::Template; use serde::Serialize; -use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig}; +use crate::{PresentationConfig, services::cv::fetch_cv_data_from_backend, CVBackendConfig, tools::rocket::RequestLanguage}; #[derive(Serialize, Debug)] struct RootPage { @@ -14,14 +14,14 @@ struct RootPage { lang: String, } -#[get("/")] -pub async fn index(presentation_config: &State, cv_config: &State) -> Result { +#[get("/")] +pub async fn index(presentation_config: &State, cv_config: &State, language: RequestLanguage) -> Result { let context = match fetch_cv_data_from_backend(cv_config.cv_backend_path.clone()).await { Ok(cv) => RootPage { static_host: presentation_config.static_route.clone(), cv, download_cv_url: "FIXME!".to_string(), - lang: "en".to_string(), + lang: language.language, }, Err(e) => { error!("Can't fetch CV data from backend {:?}", e); @@ -29,7 +29,12 @@ pub async fn index(presentation_config: &State, cv_config: & } }; - //return Templ; - Ok(Template::render("default", &context)) } + + +#[get("/")] +pub fn index_without_lang() -> Redirect { + // Default language is czech (TODO: config) + Redirect::to(format!("{}/{}", "", "cs")) +} diff --git a/src/tools/mod.rs b/src/tools/mod.rs index e7c5379..345def0 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -1 +1,2 @@ -pub mod tera; \ No newline at end of file +pub mod tera; +pub mod rocket; diff --git a/src/tools/rocket.rs b/src/tools/rocket.rs new file mode 100644 index 0000000..23ef136 --- /dev/null +++ b/src/tools/rocket.rs @@ -0,0 +1,83 @@ +use std::time::SystemTime; +use log::info; +use rocket::{Request, Response, Data, fairing::{Kind, Info, Fairing}, request::{self, FromRequest, FromParam}, http::Status}; +use phf::phf_map; + + +pub struct RequestLanguage { + pub language: String, +} + +static LANG_TO_CODES: phf::Map<&'static str, &'static str> = phf_map! { + "cs" => "cs-CZ", + "en" => "en-US", +}; + +impl<'r> FromParam<'r> for RequestLanguage { + type Error = &'r str; + + fn from_param(param: &'r str) -> Result { + match LANG_TO_CODES.get(param) { + Some(val) => Ok(RequestLanguage { + language: val.to_string(), + }), + None => Ok(RequestLanguage { + language: LANG_TO_CODES["en"].to_string(), + }), + } + } +} + + +/// Fairing for timing requests. +pub struct RequestTimer; + +/// Value stored in request-local state. +#[derive(Copy, Clone)] +struct TimerStart(Option); + +#[rocket::async_trait] +impl Fairing for RequestTimer { + fn info(&self) -> Info { + Info { + name: "Request Timer", + kind: Kind::Request | Kind::Response + } + } + + /// Stores the start time of the request in request-local state. + async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) { + // Store a `TimerStart` instead of directly storing a `SystemTime` + // to ensure that this usage doesn't conflict with anything else + // that might store a `SystemTime` in request-local cache. + request.local_cache(|| TimerStart(Some(SystemTime::now()))); + } + + /// Adds a header to the response indicating how long the server took to + /// process the request. + async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) { + let start_time = req.local_cache(|| TimerStart(None)); + if let Some(Ok(duration)) = start_time.0.map(|st| st.elapsed()) { + let ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64; + res.set_raw_header("X-Response-Time", format!("{} ms", ms)); + info!("Response time: {} ms", ms); + } + } +} + +/// Request guard used to retrieve the start time of a request. +#[derive(Copy, Clone)] +pub struct StartTime(pub SystemTime); + +// Allows a route to access the time a request was initiated. +#[rocket::async_trait] +impl<'r> FromRequest<'r> for StartTime { + type Error = (); + + async fn from_request(request: &'r Request<'_>) -> request::Outcome { + match *request.local_cache(|| TimerStart(None)) { + TimerStart(Some(time)) => request::Outcome::Success(StartTime(time)), + TimerStart(None) => request::Outcome::Error((Status::InternalServerError, ())), + } + } +} \ No newline at end of file diff --git a/src/tools/tera.rs b/src/tools/tera.rs index 2ec986f..f544726 100644 --- a/src/tools/tera.rs +++ b/src/tools/tera.rs @@ -4,9 +4,8 @@ use chrono::{Utc, Datelike}; use log::error; use rocket_dyn_templates::tera::{Value, Error}; use ovlach_data::cv::chrono::from_string; -use sha256::{digest, try_digest}; +use sha256::digest; use fluent_resmgr::resource_manager::ResourceManager; -use tokio::sync::broadcast::error; use unic_langid::LanguageIdentifier; // TODO: tenhle modul je trochu prasacky.. @@ -23,9 +22,10 @@ pub fn translate_filter( value: &Value, args: &HashMap ) -> Result { + // TODO: prepis me! let mgr = ResourceManager::new("./resources/{locale}/{res_id}".into()); - let locales = vec![LanguageIdentifier::from_str("en-US").unwrap()]; - let val_id = vec![value.as_str().unwrap().to_string()]; + let lang = args["lang"].as_str().unwrap(); + let locales = vec![LanguageIdentifier::from_str(lang).unwrap()]; let val = value.as_str().unwrap().to_string(); let bundle = mgr.get_bundle(locales, vec!["ovlach_frontend".to_string()]); @@ -54,7 +54,7 @@ pub fn translate_filter( pub fn lang_entity( value: &Value, - args: &HashMap + _: &HashMap ) -> Result { error!("{:?}", value); // TODO: rewrite me!!! return Ok(rocket_dyn_templates::tera::Value::String(format!("{}", value["en"].as_str().unwrap()))); // TODO: fix-me here! @@ -62,7 +62,7 @@ pub fn lang_entity( pub fn gravatar_link( value: &Value, - args: &HashMap + _: &HashMap ) -> Result { let val = digest(value.as_str().unwrap()); return Ok(rocket_dyn_templates::tera::Value::String(format!("https://gravatar.com/avatar/{}", val))); // TODO: fix-me here! @@ -72,7 +72,7 @@ pub fn insert_space_every( value: &Value, args: &HashMap ) -> Result { - let mut input = value.as_u64().unwrap().to_string(); + let input = value.as_u64().unwrap().to_string(); let times = args.get("times").unwrap().as_u64().unwrap(); let mut result = String::new(); @@ -88,7 +88,7 @@ pub fn insert_space_every( pub fn calculate_age( value: &Value, - args: &HashMap + _: &HashMap ) -> Result { error!("{:?}", value.as_str()); //let s = value.to_string().trim_matches('"'); // TODO: unwrap here!