qwreey_rocket/
tera_utility.rs1use 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}