121 lines
3.8 KiB
Rust
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,
|
|
}
|
|
}
|