geoffrey/src/state.rs

121 lines
3.8 KiB
Rust

use async_std::sync::{Arc, RwLock};
use handlebars::Handlebars;
use log::*;
use std::fs::File;
use std::path::Path;
use crate::i18n::Language;
#[derive(Clone, Debug)]
pub struct AppState<'a> {
hb: Arc<RwLock<Handlebars<'a>>>,
}
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);
Self {
hb: Arc::new(RwLock::new(hb)),
}
}
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 mut i18n = serde_yaml::Value::Null;
// merge in the langages from lowest priority
for lang in langs.iter().rev() {
let filename = format!("i18n/{}.yml", lang.code);
if ! Path::exists(Path::new(&filename)) {
continue;
}
let t: serde_yaml::Value = serde_yaml::from_reader(File::open(filename)?)?;
merge_yaml(&mut i18n, t);
}
let mut mustache = json!({"t" : i18n});
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,
}
}
fn merge_yaml(a: &mut serde_yaml::Value, b: serde_yaml::Value) {
match (a, b) {
(a @ &mut serde_yaml::Value::Mapping(_), serde_yaml::Value::Mapping(b)) => {
let a = a.as_mapping_mut().unwrap();
for (k, v) in b {
if v.is_sequence() && a.contains_key(&k) && a[&k].is_sequence() {
let mut _b = a.get(&k).unwrap().as_sequence().unwrap().to_owned();
_b.append(&mut v.as_sequence().unwrap().to_owned());
a[&k] = serde_yaml::Value::from(_b);
continue;
}
if !a.contains_key(&k) {a.insert(k.to_owned(), v.to_owned());}
else { merge_yaml(&mut a[&k], v); }
}
}
(a, b) => *a = b,
}
}