qwreey_rocket/
tera_utility.rs

1use std::{borrow::Cow, collections::HashMap, sync::Arc};
2
3use rocket_dyn_templates::{tera::Tera, Template};
4use serde::Serialize;
5
6use crate::{RocketOrbit, TeraError, TeraValue};
7
8mod builtin {
9    use super::*;
10
11    fn js_global(map: &HashMap<String, TeraValue>) -> Result<String, TeraError> {
12        let id = map
13            .get("id")
14            .ok_or_else(|| TeraError::msg("js_global: missing id argument"))?
15            .as_str()
16            .ok_or_else(|| TeraError::msg("js_global: id should be string"))?;
17        let mut buf = format!("window[\"{id}\"]={{",);
18
19        for (key, value) in map {
20            if key == "id" {
21                continue;
22            }
23            buf.push_str(
24                format!(
25                    "\"{key}\":\"{}\",",
26                    value
27                        .as_str()
28                        .ok_or_else(|| {
29                            TeraError::msg(format!(
30                                "function - js_global: failed to stringify value {key}"
31                            ))
32                        })?
33                        .replace("\\", "\\\\")
34                        .replace("\n", "\\n")
35                        .replace("\"", "\\\"")
36                )
37                .as_str(),
38            );
39        }
40        buf.pop();
41        buf.push_str("}");
42
43        Ok(buf)
44    }
45
46    fn js_global_function(args: &HashMap<String, TeraValue>) -> Result<TeraValue, TeraError> {
47        Ok(TeraValue::String(format!(
48            "<script>{}</script>",
49            js_global(args)?
50        )))
51    }
52    fn js_global_filter(
53        value: &TeraValue,
54        _args: &HashMap<String, TeraValue>,
55    ) -> Result<TeraValue, TeraError> {
56        match value {
57            TeraValue::Object(obj) => Ok(js_global_function(&obj.to_tera_hash_map())?),
58            TeraValue::Array(arr) => {
59                let mut buf = String::from("<script>");
60                for obj in arr {
61                    buf.push_str(
62                        js_global(
63                            &obj.as_object().ok_or_else(|| TeraError::msg("filter - js_global: value is array but not a array of objects"))?.to_tera_hash_map()
64                        )?.as_str()
65                    );
66                    buf.push(';');
67                }
68                buf.push_str("</script>");
69                Ok(TeraValue::String(buf))
70            }
71            _ => Err(TeraError::msg(
72                "filter - js_global: value must be object or array of objects",
73            )),
74        }
75    }
76
77    fn wrap_script_filter(
78        value: &TeraValue,
79        _args: &HashMap<String, TeraValue>,
80    ) -> Result<TeraValue, TeraError> {
81        match value {
82            TeraValue::String(script) => {
83                Ok(TeraValue::String(format!("<script>{script}</script>")))
84            }
85            _ => Err(TeraError::msg("Inner value should be string")),
86        }
87    }
88
89    fn newline_to_br_filter(
90        value: &TeraValue,
91        _args: &HashMap<String, TeraValue>,
92    ) -> Result<TeraValue, TeraError> {
93        match value {
94            TeraValue::String(script) => Ok(TeraValue::String(script.replace("\n", "<br/>"))),
95            _ => Err(TeraError::msg("Inner value should be string")),
96        }
97    }
98
99    pub fn add_builtin(tera: &mut Tera) {
100        tera.register_function("js_global", js_global_function);
101        tera.register_filter("js_global", js_global_filter);
102        tera.register_filter("wrap_script", wrap_script_filter);
103        tera.register_filter("newline_to_br", newline_to_br_filter);
104    }
105}
106pub use builtin::add_builtin;
107
108pub trait ErrToTeraError<Value> {
109    fn err_to_tera_error(self) -> Result<Value, TeraError>;
110}
111impl<T, E: ToString> ErrToTeraError<T> for Result<T, E> {
112    fn err_to_tera_error(self) -> Result<T, TeraError> {
113        self.map_err(|err| TeraError::msg(err.to_string()))
114    }
115}
116
117pub trait TemplateToContent {
118    fn template_to_content<S: Into<Cow<'static, str>>, C: Serialize>(
119        &self,
120        name: S,
121        context: C,
122    ) -> Result<Arc<str>, String>;
123}
124impl TemplateToContent for RocketOrbit {
125    fn template_to_content<S: Into<Cow<'static, str>>, C: Serialize>(
126        &self,
127        name: S,
128        context: C,
129    ) -> Result<Arc<str>, String> {
130        Ok(Arc::from(
131            Template::show(self, name, context).ok_or_else(|| String::from("Failed to render"))?,
132        ))
133    }
134}
135
136pub trait ToTeraHashMap {
137    fn to_tera_hash_map(self) -> HashMap<String, TeraValue>;
138}
139impl ToTeraHashMap for serde_json::Map<String, TeraValue> {
140    fn to_tera_hash_map(self) -> HashMap<String, TeraValue> {
141        let mut result = HashMap::with_capacity(self.len());
142        for (key, value) in self {
143            result.insert(key, value);
144        }
145        result
146    }
147}
148impl ToTeraHashMap for &serde_json::Map<String, TeraValue> {
149    fn to_tera_hash_map(self) -> HashMap<String, TeraValue> {
150        let mut result = HashMap::with_capacity(self.len());
151        for (key, value) in self {
152            result.insert(key.clone(), value.clone());
153        }
154        result
155    }
156}