sato/
renderer.rs

1use std::collections::HashMap;
2use std::convert::{From, Into};
3
4use crate::context::{ContextValue, RenderContext};
5use crate::template::{Template, TemplateExprNode, TemplateAttribute};
6use crate::builtins;
7
8type NodeHandler = dyn for<'a> Fn(Attributes, &[TemplateExprNode], &'a Renderer, &'a RenderContext) -> Result<RenderValue, RenderError> + Send + Sync;
9
10#[derive(Debug, Clone)]
11pub enum RenderValue {
12    String(String),
13    Integer(i64),
14    Boolean(bool),
15    Vec(Vec<RenderValue>),
16    Object(HashMap<String, RenderValue>),
17    Template(Template),
18    Empty,
19}
20
21impl RenderValue {
22    pub fn finalize(self) -> String {
23        match self {
24            RenderValue::String(s) => s,
25            RenderValue::Integer(i) => i.to_string(),
26            RenderValue::Boolean(b) => b.to_string(),
27            RenderValue::Vec(v) => v.into_iter().map(|e| e.finalize()).collect::<Vec<_>>().join(""),
28            RenderValue::Object(o) => o.into_iter().map(|(_k, v)| v.finalize()).collect::<Vec<_>>().join(""),
29            RenderValue::Template(_t) => "".into(),
30            RenderValue::Empty => "".into(),
31        }
32    }
33
34    pub fn as_string(&self) -> Option<&String> {
35        match self {
36            RenderValue::String(s) => Some(s),
37            _ => None
38        }
39    }
40
41    pub fn as_int(&self) -> Option<i64> {
42        match self {
43            RenderValue::Integer(i) => Some(*i),
44            _ => None
45        }
46    }
47
48    pub fn join(&self) -> RenderValue {
49        match self {
50            RenderValue::String(_) => self.clone(),
51            RenderValue::Integer(_) => self.clone(),
52            RenderValue::Boolean(_) => self.clone(),
53            RenderValue::Vec(v) => RenderValue::String(v.iter().map(|e| e.clone().finalize()).collect::<Vec<_>>().join("")),
54            RenderValue::Object(o) => RenderValue::String(o.iter().map(|(_k, v)| v.clone().finalize()).collect::<Vec<_>>().join("")),
55            RenderValue::Template(_) => self.clone(),
56            RenderValue::Empty => self.clone(),
57        }
58    }
59}
60
61impl From<&str> for RenderValue {
62    fn from(other: &str) -> Self {
63        RenderValue::String(other.into())
64    }
65}
66
67impl From<String> for RenderValue {
68    fn from(other: String) -> Self {
69        RenderValue::String(other)
70    }
71}
72
73impl<T: Into<RenderValue>> From<Vec<T>> for RenderValue {
74    fn from(other: Vec<T>) -> Self {
75        RenderValue::Vec(other.into_iter().map(|k| k.into()).collect())
76    }
77}
78
79impl From<i64> for RenderValue {
80    fn from(other: i64) -> Self {
81        RenderValue::Integer(other)
82    }
83}
84
85impl From<bool> for RenderValue {
86    fn from(other: bool) -> Self {
87        RenderValue::Boolean(other)
88    }
89}
90
91impl From<&ContextValue> for RenderValue {
92    fn from(other: &ContextValue) -> Self {
93        match other {
94            ContextValue::Integer(i) => RenderValue::Integer(*i),
95            ContextValue::Boolean(b) => RenderValue::Boolean(*b),
96            ContextValue::String(s) => RenderValue::String(s.clone()),
97            ContextValue::Vec(v) => RenderValue::Vec(v.iter().map(|e| RenderValue::from(e)).collect::<Vec<_>>()),
98            ContextValue::Object(o) => {
99                RenderValue::Object(o.0.iter()
100                                       .map(|(k, v)| (k.clone(), RenderValue::from(v)))
101                                       .collect::<HashMap<String, RenderValue>>())
102            },
103            ContextValue::Template(t) => RenderValue::Template(t.clone()),
104        }
105    }
106}
107
108impl PartialEq for RenderValue {
109    fn eq(&self, other: &RenderValue) -> bool {
110        match (self, other) {
111            (RenderValue::Integer(a), RenderValue::Integer(b)) => a == b,
112            (RenderValue::Boolean(a), RenderValue::Boolean(b)) => a == b,
113            (RenderValue::String(a), RenderValue::String(b)) => a == b,
114            (RenderValue::Vec(a), RenderValue::Vec(b)) => a == b,
115            _ => false,
116        }
117    }
118}
119
120impl PartialOrd for RenderValue {
121    fn partial_cmp(&self, other: &RenderValue) -> Option<std::cmp::Ordering> {
122        match (self, other) {
123            (RenderValue::Integer(a), RenderValue::Integer(b)) => a.partial_cmp(b),
124            (RenderValue::Boolean(a), RenderValue::Boolean(b)) => a.partial_cmp(b),
125            (RenderValue::String(a), RenderValue::String(b)) => a.partial_cmp(b),
126            (RenderValue::Vec(a), RenderValue::Vec(b)) => a.partial_cmp(b),
127            _ => None,
128        }
129    }
130}
131
132
133#[derive(Debug, Clone)]
134pub struct Attribute(pub String, pub String);
135
136#[derive(Debug, Clone)]
137pub struct Attributes(Vec<Attribute>);
138
139
140impl Attributes {
141    pub fn new(attrs: Vec<Attribute>) -> Attributes {
142        Attributes(attrs)
143    }
144
145    pub fn push(&mut self, attr: Attribute) {
146        self.0.push(attr)
147    }
148
149    pub fn get(&self, name: &str) -> Option<&String> {
150        self.0
151            .iter()
152            .find(|a| a.0 == name)
153            .map(|a| &a.1)
154    }
155}
156
157impl<'a> IntoIterator for &'a Attributes {
158    type Item = &'a Attribute;
159    type IntoIter = std::slice::Iter<'a, Attribute>;
160
161    fn into_iter(self) -> Self::IntoIter {
162        self.0.iter()
163    }
164}
165
166
167
168#[derive(thiserror::Error, Debug)]
169pub enum RenderError {
170    #[error("expected a variable, found {0}")]
171    ExpectedVariable(String),
172    #[error("error expanding variable: {0} ({1:?})")]
173    ExpandVariable(String, String),
174    #[error("error in `is-set`: {0} ({1:?})")]
175    IsSet(String, Vec<TemplateExprNode>),
176    #[error("error in `eq`: {0} ({1:?})")]
177    Cmp(String, Vec<TemplateExprNode>),
178    #[error("error in `if`: {0} ({1:?})")]
179    If(String, Vec<TemplateExprNode>),
180    #[error("error in `case`: {0} ({1:?})")]
181    Case(String, Vec<TemplateExprNode>),
182    #[error("error in `switch`: {0} ({1:?})")]
183    Switch(String, Vec<TemplateExprNode>),
184    #[error("error in `for`: {0} {1:?} ({2:?})")]
185    For(String, Attributes, Vec<TemplateExprNode>),
186    #[error("error in `get`: {0} {1:?}")]
187    Get(String, Vec<TemplateExprNode>),
188
189    #[error("error in math operator: {0} ({1:?})")]
190    Math(String, Vec<TemplateExprNode>),
191
192    #[error("error in `{0}`: {1} ({2:?})")]
193    UserDefined(String, String, Vec<TemplateExprNode>),
194
195    #[error("error in `eval`: {0}")]
196    Evaluate(String),
197
198}
199
200pub struct Renderer {
201    functions: HashMap<String, Box<NodeHandler>>,
202}
203
204pub(crate) fn expand_variable(expr: &String, renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
205    Ok(
206        if expr.starts_with('$') {
207            if expr.contains(".") {
208                expr[1..].split('.').try_fold((context.clone(), None), |(mut context, output), expr| {
209                    if output.is_some() {
210                        return Ok((context, output))
211                    }
212
213                    match context.get(expr) {
214                        Some(ContextValue::Object(o)) => {
215                            context = o.clone();
216                            Ok((context, output))
217                        },
218                        Some(item) => {
219                            let item = item.into();
220                            Ok((context, Some(item)))
221                        },
222                        None => Ok((context, Some(RenderValue::Boolean(false))))
223                    }
224                })?
225                    .1
226                    .unwrap_or_else(|| expr.clone().into())
227            }
228            else {
229                context.get(&expr[1..])
230                    .map(|e| e.try_into())
231                    .unwrap_or(Ok(RenderValue::String(expr.clone())))
232                    .map(|e| {
233                        match e {
234                            RenderValue::Vec(v) => {
235                                Ok(RenderValue::Vec(v.iter()
236                                                    .map(|v| {
237                                                        match v {
238                                                            RenderValue::String(s) => expand_variable(s, renderer, context),
239                                                            _ => Ok(v.clone())
240                                                        }
241                                                    })
242                                                    .collect::<Result<Vec<_>, _>>()?))
243                            },
244                            RenderValue::Template(t) => {
245                                Ok(RenderValue::String(renderer.render(&t, context)?))
246                            }
247                            _ => Ok(e)
248                        }
249                    })
250                    .unwrap_or(Ok(RenderValue::String(expr.clone())))?
251            }
252        }
253        else {
254            RenderValue::String(expr.clone())
255        }
256    )
257}
258
259pub(crate) fn basic_html_tag(tag: String, attrs: &Attributes, expr: &[TemplateExprNode], renderer: &Renderer, context: &RenderContext) -> Result<RenderValue, RenderError> {
260    let mut l = Vec::<RenderValue>::new();
261    let attr_str = attrs.0.iter()
262        .map(|attr| {
263            Ok(format!(" {}=\"{}\"", attr.0, attr.1))
264        })
265        .collect::<Result<Vec<_>, RenderError>>()?
266        .join("");
267    if expr.len() == 0 {
268        l.push(format!("<{}{} />", tag, attr_str).into());
269    }
270    else {
271        l.push(format!("<{}{}>", tag, attr_str).into());
272        l.push(renderer.evaluate_multiple(expr, context)?.into());
273        l.push(format!("</{}>", tag).into());
274    }
275    Ok(l.into())
276}
277
278
279fn standard_issue_functions() -> HashMap<String, Box<NodeHandler>> {
280    let mut functions = HashMap::new();
281    functions.insert("html".into(), Box::new(builtins::do_html) as Box<NodeHandler>);
282    functions.insert("is-set".into(), Box::new(builtins::do_is_set));
283    functions.insert("if".into(), Box::new(builtins::do_if));
284    functions.insert("switch".into(), Box::new(builtins::do_switch));
285    functions.insert("case".into(), Box::new(builtins::do_case));
286    functions.insert("for".into(), Box::new(builtins::do_for));
287    functions.insert("get".into(), Box::new(builtins::do_get));
288
289    functions.insert("eq".into(), Box::new(|a,e,r,c| builtins::do_cmp_op(a,e,r,c, |q, w| q == w)));
290    functions.insert("lt".into(), Box::new(|a,e,r,c| builtins::do_cmp_op(a,e,r,c, |q, w| q < w)));
291    functions.insert("gt".into(), Box::new(|a,e,r,c| builtins::do_cmp_op(a,e,r,c, |q, w| q > w)));
292    functions.insert("lte".into(), Box::new(|a,e,r,c| builtins::do_cmp_op(a,e,r,c, |q, w| q <= w)));
293    functions.insert("gte".into(), Box::new(|a,e,r,c| builtins::do_cmp_op(a,e,r,c, |q, w| q >= w)));
294    functions.insert("ne".into(), Box::new(|a,e,r,c| builtins::do_cmp_op(a,e,r,c, |q, w| q != w)));
295
296    functions.insert("+".into(), Box::new(|a,e,r,c| builtins::do_math_op(a,e,r,c, |q, w| q + w)));
297    functions.insert("-".into(), Box::new(|a,e,r,c| builtins::do_math_op(a,e,r,c, |q, w| q - w)));
298    functions.insert("*".into(), Box::new(|a,e,r,c| builtins::do_math_op(a,e,r,c, |q, w| q * w)));
299    functions.insert("/".into(), Box::new(|a,e,r,c| builtins::do_math_op(a,e,r,c, |q, w| q / w)));
300    functions.insert("%".into(), Box::new(|a,e,r,c| builtins::do_math_op(a,e,r,c, |q, w| q % w)));
301
302    functions
303}
304
305impl Renderer {
306    pub fn builder() -> RendererBuilder {
307        RendererBuilder::new()
308    }
309
310    pub fn evaluate_multiple(&self, expr: &[TemplateExprNode], context: &RenderContext) -> Result<RenderValue, RenderError> {
311        Ok(expr
312           .into_iter()
313           .map(|e| self.evaluate(&e, context))
314           .collect::<Result<Vec<_>, _>>()?
315           .into())
316    }
317
318    pub fn evaluate_attrs(&self, attrs: &Vec<TemplateAttribute>, context: &RenderContext) -> Result<Attributes, RenderError> {
319        Ok(Attributes(attrs
320                      .iter()
321                      .map(|attr| {
322                          Ok(Attribute(self.evaluate(&attr.0, context)?.finalize(), self.evaluate_multiple(&attr.1, context)?.finalize()))
323                      })
324                      .collect::<Result<Vec<_>, _>>()?))
325    }
326
327    pub fn evaluate(&self, expr: &TemplateExprNode, context: &RenderContext) -> Result<RenderValue, RenderError> {
328        Ok(match expr {
329            TemplateExprNode::Identifier(ident) => {
330                expand_variable(&ident, self, context)?
331            },
332            TemplateExprNode::Integer(i) => {
333                (*i).into()
334            },
335            TemplateExprNode::Tag(tag) => {
336                let eval_attrs = self.evaluate_attrs(&tag.attrs, context)?;
337                match self.functions.get(&tag.tag) {
338                    Some(op_func) => op_func(eval_attrs, &tag.children, self, context)?,
339                    None => basic_html_tag(tag.tag.clone(), &eval_attrs, &tag.children, self, context)?,
340                }
341            },
342        })
343    }
344
345    pub fn render(&self, template: &Template, context: &RenderContext) -> Result<String, RenderError> {
346        Ok(self.evaluate(&template.expr, context)?.finalize())
347    }
348}
349
350impl Default for Renderer {
351    fn default() -> Renderer {
352        Renderer::builder().build()
353    }
354}
355
356
357pub struct RendererBuilder {
358    functions: HashMap<String, Box<NodeHandler>>,
359}
360
361impl RendererBuilder {
362    fn new() -> Self {
363        RendererBuilder {
364            functions: standard_issue_functions(),
365        }
366    }
367
368    pub fn function<S>(mut self, name: S, func: Box<NodeHandler>) -> Self
369    where
370        S: std::convert::Into<String>
371    {
372        self.functions.insert(name.into(), func);
373        self
374    }
375
376    pub fn build(self) -> Renderer {
377        Renderer {
378            functions: self.functions,
379        }
380    }
381}
382