geoffrey/src/state.rs

93 lines
2.7 KiB
Rust

use async_std::sync::{Arc, RwLock};
use handlebars::Handlebars;
use log::*;
use y10n::*;
#[derive(Clone, Debug)]
pub struct AppState<'a> {
hb: Arc<RwLock<Handlebars<'a>>>,
y10n: Arc<RwLock<Y10n>>,
}
impl AppState<'_> {
pub fn new() -> Self {
let mut hb = Handlebars::new();
// This ensures that we get errors rather than empty strings for bad values
hb.set_strict_mode(true);
let y10n = Y10n::from_glob("i18n/**/*.yml");
Self {
hb: Arc::new(RwLock::new(hb)),
y10n: Arc::new(RwLock::new(y10n)),
}
}
pub async fn register_templates(&self) -> Result<(), handlebars::TemplateError> {
let mut hb = self.hb.write().await;
hb.clear_templates();
hb.register_templates_directory(".hbs", "views")
}
pub async fn render(
&self,
name: &str,
langs: &Vec<Language>,
data: Option<serde_json::Value>,
) -> Result<tide::Body, tide::Error> {
/*
* In debug mode, reload the templates on ever render to avoid
* needing a restart
*/
#[cfg(debug_assertions)]
{
self.register_templates().await?;
}
if let Some(data) = &data {
if !data.is_object() {
warn!(
"The render function was called without a map, this can lead to funny results"
);
}
}
let y10n = self.y10n.read().await;
let localize = y10n.localize(&langs);
let mut mustache = json!({ "t": localize });
merge(&mut mustache, data.unwrap_or(serde_json::Value::Null));
let hb = self.hb.read().await;
match hb.render(name, &mustache) {
Ok(view) => {
let mut body = tide::Body::from_string(view);
body.set_mime("text/html");
Ok(body)
}
Err(e) => {
error!("Failed to render {}: {:?}", name, e);
Err(e.into())
}
}
}
}
fn merge(a: &mut serde_json::Value, b: serde_json::Value) {
match (a, b) {
(a @ &mut serde_json::Value::Object(_), serde_json::Value::Object(b)) => {
let a = a.as_object_mut().unwrap();
for (k, v) in b {
if v.is_array() && a.contains_key(&k) && a.get(&k).as_ref().unwrap().is_array() {
let mut _a = a.get(&k).unwrap().as_array().unwrap().to_owned();
_a.append(&mut v.as_array().unwrap().to_owned());
a[&k] = serde_json::Value::from(_a);
} else {
merge(a.entry(k).or_insert(serde_json::Value::Null), v);
}
}
}
(a, b) => *a = b,
}
}