tera/renderer/
processor.rs

1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::io::Write;
4
5use serde_json::{to_string_pretty, to_value, Number, Value};
6
7use crate::context::{ValueRender, ValueTruthy};
8use crate::errors::{Error, Result};
9use crate::parser::ast::*;
10use crate::renderer::call_stack::CallStack;
11use crate::renderer::for_loop::ForLoop;
12use crate::renderer::macros::MacroCollection;
13use crate::renderer::square_brackets::pull_out_square_bracket;
14use crate::renderer::stack_frame::{FrameContext, FrameType, Val};
15use crate::template::Template;
16use crate::tera::Tera;
17use crate::utils::render_to_string;
18use crate::Context;
19
20/// Special string indicating request to dump context
21static MAGICAL_DUMP_VAR: &str = "__tera_context";
22
23/// This will convert a Tera variable to a json pointer if it is possible by replacing
24/// the index with their evaluated stringified value
25fn evaluate_sub_variables(key: &str, call_stack: &CallStack) -> Result<String> {
26    let sub_vars_to_calc = pull_out_square_bracket(key);
27    let mut new_key = key.to_string();
28
29    for sub_var in &sub_vars_to_calc {
30        // Translate from variable name to variable value
31        match process_path(sub_var.as_ref(), call_stack) {
32            Err(e) => {
33                return Err(Error::msg(format!(
34                    "Variable {} can not be evaluated because: {}",
35                    key, e
36                )));
37            }
38            Ok(post_var) => {
39                let post_var_as_str = match *post_var {
40                    Value::String(ref s) => format!(r#""{}""#, s),
41                    Value::Number(ref n) => n.to_string(),
42                    _ => {
43                        return Err(Error::msg(format!(
44                            "Only variables evaluating to String or Number can be used as \
45                             index (`{}` of `{}`)",
46                            sub_var, key,
47                        )));
48                    }
49                };
50
51                // Rebuild the original key String replacing variable name with value
52                let nk = new_key.clone();
53                let divider = "[".to_string() + sub_var + "]";
54                let mut the_parts = nk.splitn(2, divider.as_str());
55
56                new_key = the_parts.next().unwrap().to_string()
57                    + "."
58                    + post_var_as_str.as_ref()
59                    + the_parts.next().unwrap_or("");
60            }
61        }
62    }
63
64    Ok(new_key
65        .replace('/', "~1") // https://tools.ietf.org/html/rfc6901#section-3
66        .replace("['", ".\"")
67        .replace("[\"", ".\"")
68        .replace('[', ".")
69        .replace("']", "\"")
70        .replace("\"]", "\"")
71        .replace(']', ""))
72}
73
74fn process_path<'a>(path: &str, call_stack: &CallStack<'a>) -> Result<Val<'a>> {
75    if !path.contains('[') {
76        match call_stack.lookup(path) {
77            Some(v) => Ok(v),
78            None => Err(Error::msg(format!(
79                "Variable `{}` not found in context while rendering '{}'",
80                path,
81                call_stack.active_template().name
82            ))),
83        }
84    } else {
85        let full_path = evaluate_sub_variables(path, call_stack)?;
86
87        match call_stack.lookup(&full_path) {
88            Some(v) => Ok(v),
89            None => Err(Error::msg(format!(
90                "Variable `{}` not found in context while rendering '{}': \
91                 the evaluated version was `{}`. Maybe the index is out of bounds?",
92                path,
93                call_stack.active_template().name,
94                full_path,
95            ))),
96        }
97    }
98}
99
100/// Processes the ast and renders the output
101pub struct Processor<'a> {
102    /// The template we're trying to render
103    template: &'a Template,
104    /// Root template of template to render - contains ast to use for rendering
105    /// Can be the same as `template` if a template has no inheritance
106    template_root: &'a Template,
107    /// The Tera object with template details
108    tera: &'a Tera,
109    /// The call stack for processing
110    call_stack: CallStack<'a>,
111    /// The macros organised by template and namespaces
112    macros: MacroCollection<'a>,
113    /// If set, rendering should be escaped
114    should_escape: bool,
115    /// Used when super() is used in a block, to know where we are in our stack of
116    /// definitions and for which block
117    /// Vec<(block name, tpl_name, level)>
118    blocks: Vec<(&'a str, &'a str, usize)>,
119}
120
121impl<'a> Processor<'a> {
122    /// Create a new `Processor` that will do the rendering
123    pub fn new(
124        template: &'a Template,
125        tera: &'a Tera,
126        context: &'a Context,
127        should_escape: bool,
128    ) -> Self {
129        // Gets the root template if we are rendering something with inheritance or just return
130        // the template we're dealing with otherwise
131        let template_root = template
132            .parents
133            .last()
134            .map(|parent| tera.get_template(parent).unwrap())
135            .unwrap_or(template);
136
137        let call_stack = CallStack::new(context, template);
138
139        Processor {
140            template,
141            template_root,
142            tera,
143            call_stack,
144            macros: MacroCollection::from_original_template(template, tera),
145            should_escape,
146            blocks: Vec::new(),
147        }
148    }
149
150    fn render_body(&mut self, body: &'a [Node], write: &mut impl Write) -> Result<()> {
151        for n in body {
152            self.render_node(n, write)?;
153
154            if self.call_stack.should_break_body() {
155                break;
156            }
157        }
158
159        Ok(())
160    }
161
162    fn render_for_loop(&mut self, for_loop: &'a Forloop, write: &mut impl Write) -> Result<()> {
163        let container_name = match for_loop.container.val {
164            ExprVal::Ident(ref ident) => ident,
165            ExprVal::FunctionCall(FunctionCall { ref name, .. }) => name,
166            ExprVal::Array(_) => "an array literal",
167            _ => return Err(Error::msg(format!(
168                "Forloop containers have to be an ident or a function call (tried to iterate on '{:?}')",
169                for_loop.container.val,
170            ))),
171        };
172
173        let for_loop_name = &for_loop.value;
174        let for_loop_body = &for_loop.body;
175        let for_loop_empty_body = &for_loop.empty_body;
176
177        let container_val = self.safe_eval_expression(&for_loop.container)?;
178
179        let for_loop = match *container_val {
180            Value::Array(_) => {
181                if for_loop.key.is_some() {
182                    return Err(Error::msg(format!(
183                        "Tried to iterate using key value on variable `{}`, but it isn't an object/map",
184                        container_name,
185                    )));
186                }
187                ForLoop::from_array(&for_loop.value, container_val)
188            }
189            Value::String(_) => {
190                if for_loop.key.is_some() {
191                    return Err(Error::msg(format!(
192                        "Tried to iterate using key value on variable `{}`, but it isn't an object/map",
193                        container_name,
194                    )));
195                }
196                ForLoop::from_string(&for_loop.value, container_val)
197            }
198            Value::Object(_) => {
199                if for_loop.key.is_none() {
200                    return Err(Error::msg(format!(
201                        "Tried to iterate using key value on variable `{}`, but it is missing a key",
202                        container_name,
203                    )));
204                }
205                match container_val {
206                    Cow::Borrowed(c) => {
207                        ForLoop::from_object(for_loop.key.as_ref().unwrap(), &for_loop.value, c)
208                    }
209                    Cow::Owned(c) => ForLoop::from_object_owned(
210                        for_loop.key.as_ref().unwrap(),
211                        &for_loop.value,
212                        c,
213                    ),
214                }
215            }
216            _ => {
217                return Err(Error::msg(format!(
218                    "Tried to iterate on a container (`{}`) that has a unsupported type",
219                    container_name,
220                )));
221            }
222        };
223
224        let len = for_loop.len();
225        match (len, for_loop_empty_body) {
226            (0, Some(empty_body)) => self.render_body(empty_body, write),
227            (0, _) => Ok(()),
228            (_, _) => {
229                self.call_stack.push_for_loop_frame(for_loop_name, for_loop);
230
231                for _ in 0..len {
232                    self.render_body(for_loop_body, write)?;
233
234                    if self.call_stack.should_break_for_loop() {
235                        break;
236                    }
237
238                    self.call_stack.increment_for_loop()?;
239                }
240
241                self.call_stack.pop();
242
243                Ok(())
244            }
245        }
246    }
247
248    fn render_if_node(&mut self, if_node: &'a If, write: &mut impl Write) -> Result<()> {
249        for (_, expr, body) in &if_node.conditions {
250            if self.eval_as_bool(expr)? {
251                return self.render_body(body, write);
252            }
253        }
254
255        if let Some((_, ref body)) = if_node.otherwise {
256            return self.render_body(body, write);
257        }
258
259        Ok(())
260    }
261
262    /// The way inheritance work is that the top parent will be rendered by the renderer so for blocks
263    /// we want to look from the bottom (`level = 0`, the template the user is actually rendering)
264    /// to the top (the base template).
265    fn render_block(
266        &mut self,
267        block: &'a Block,
268        level: usize,
269        write: &mut impl Write,
270    ) -> Result<()> {
271        let level_template = match level {
272            0 => self.call_stack.active_template(),
273            _ => self
274                .tera
275                .get_template(&self.call_stack.active_template().parents[level - 1])
276                .unwrap(),
277        };
278
279        let blocks_definitions = &level_template.blocks_definitions;
280
281        // Can we find this one block in these definitions? If so render it
282        if let Some(block_def) = blocks_definitions.get(&block.name) {
283            let (_, Block { ref body, .. }) = block_def[0];
284            self.blocks.push((&block.name[..], &level_template.name[..], level));
285            return self.render_body(body, write);
286        }
287
288        // Do we have more parents to look through?
289        if level < self.call_stack.active_template().parents.len() {
290            return self.render_block(block, level + 1, write);
291        }
292
293        // Nope, just render the body we got
294        self.render_body(&block.body, write)
295    }
296
297    fn get_default_value(&mut self, expr: &'a Expr) -> Result<Val<'a>> {
298        if let Some(default_expr) = expr.filters[0].args.get("value") {
299            self.eval_expression(default_expr)
300        } else {
301            Err(Error::msg("The `default` filter requires a `value` argument."))
302        }
303    }
304
305    fn eval_in_condition(&mut self, in_cond: &'a In) -> Result<bool> {
306        let lhs = self.safe_eval_expression(&in_cond.lhs)?;
307        let rhs = self.safe_eval_expression(&in_cond.rhs)?;
308
309        let present = match *rhs {
310            Value::Array(ref v) => v.contains(&lhs),
311            Value::String(ref s) => match *lhs {
312                Value::String(ref s2) => s.contains(s2),
313                _ => {
314                    return Err(Error::msg(format!(
315                        "Tried to check if {:?} is in a string, but it isn't a string",
316                        lhs
317                    )))
318                }
319            },
320            Value::Object(ref map) => match *lhs {
321                Value::String(ref s2) => map.contains_key(s2),
322                _ => {
323                    return Err(Error::msg(format!(
324                        "Tried to check if {:?} is in a object, but it isn't a string",
325                        lhs
326                    )))
327                }
328            },
329            _ => {
330                return Err(Error::msg(
331                    "The `in` operator only supports strings, arrays and objects.",
332                ))
333            }
334        };
335
336        Ok(if in_cond.negated { !present } else { present })
337    }
338
339    fn eval_expression(&mut self, expr: &'a Expr) -> Result<Val<'a>> {
340        let mut needs_escape = false;
341
342        let mut res = match expr.val {
343            ExprVal::Array(ref arr) => {
344                let mut values = vec![];
345                for v in arr {
346                    values.push(self.eval_expression(v)?.into_owned());
347                }
348                Cow::Owned(Value::Array(values))
349            }
350            ExprVal::In(ref in_cond) => Cow::Owned(Value::Bool(self.eval_in_condition(in_cond)?)),
351            ExprVal::String(ref val) => {
352                needs_escape = true;
353                Cow::Owned(Value::String(val.to_string()))
354            }
355            ExprVal::StringConcat(ref str_concat) => {
356                let mut res = String::new();
357                for s in &str_concat.values {
358                    match *s {
359                        ExprVal::String(ref v) => res.push_str(v),
360                        ExprVal::Int(ref v) => res.push_str(&format!("{}", v)),
361                        ExprVal::Float(ref v) => res.push_str(&format!("{}", v)),
362                        ExprVal::Ident(ref i) => match *self.lookup_ident(i)? {
363                            Value::String(ref v) => res.push_str(v),
364                            Value::Number(ref v) => res.push_str(&v.to_string()),
365                            _ => return Err(Error::msg(format!(
366                                "Tried to concat a value that is not a string or a number from ident {}",
367                                i
368                            ))),
369                        },
370                        ExprVal::FunctionCall(ref fn_call) => match *self.eval_tera_fn_call(fn_call, &mut needs_escape)? {
371                            Value::String(ref v) => res.push_str(v),
372                            Value::Number(ref v) => res.push_str(&v.to_string()),
373                            _ => return Err(Error::msg(format!(
374                                "Tried to concat a value that is not a string or a number from function call {}",
375                                fn_call.name
376                            ))),
377                        },
378                        _ => unreachable!(),
379                    };
380                }
381
382                Cow::Owned(Value::String(res))
383            }
384            ExprVal::Int(val) => Cow::Owned(Value::Number(val.into())),
385            ExprVal::Float(val) => Cow::Owned(Value::Number(Number::from_f64(val).unwrap())),
386            ExprVal::Bool(val) => Cow::Owned(Value::Bool(val)),
387            ExprVal::Ident(ref ident) => {
388                needs_escape = ident != MAGICAL_DUMP_VAR;
389                // Negated idents are special cased as `not undefined_ident` should not
390                // error but instead be falsy values
391                match self.lookup_ident(ident) {
392                    Ok(val) => {
393                        if val.is_null() && expr.has_default_filter() {
394                            self.get_default_value(expr)?
395                        } else {
396                            val
397                        }
398                    }
399                    Err(e) => {
400                        if expr.has_default_filter() {
401                            self.get_default_value(expr)?
402                        } else {
403                            if !expr.negated {
404                                return Err(e);
405                            }
406                            // A negative undefined ident is !false so truthy
407                            return Ok(Cow::Owned(Value::Bool(true)));
408                        }
409                    }
410                }
411            }
412            ExprVal::FunctionCall(ref fn_call) => {
413                self.eval_tera_fn_call(fn_call, &mut needs_escape)?
414            }
415            ExprVal::MacroCall(ref macro_call) => {
416                let val = render_to_string(
417                    || format!("macro {}", macro_call.name),
418                    |w| self.eval_macro_call(macro_call, w),
419                )?;
420                Cow::Owned(Value::String(val))
421            }
422            ExprVal::Test(ref test) => Cow::Owned(Value::Bool(self.eval_test(test)?)),
423            ExprVal::Logic(_) => Cow::Owned(Value::Bool(self.eval_as_bool(expr)?)),
424            ExprVal::Math(_) => match self.eval_as_number(&expr.val) {
425                Ok(Some(n)) => Cow::Owned(Value::Number(n)),
426                Ok(None) => Cow::Owned(Value::String("NaN".to_owned())),
427                Err(e) => return Err(Error::msg(e)),
428            },
429        };
430
431        for filter in &expr.filters {
432            if filter.name == "safe" || filter.name == "default" {
433                continue;
434            }
435            res = self.eval_filter(&res, filter, &mut needs_escape)?;
436        }
437
438        // Lastly, we need to check if the expression is negated, thus turning it into a bool
439        if expr.negated {
440            return Ok(Cow::Owned(Value::Bool(!res.is_truthy())));
441        }
442
443        // Checks if it's a string and we need to escape it (if the last filter is `safe` we don't)
444        if self.should_escape && needs_escape && res.is_string() && !expr.is_marked_safe() {
445            res = Cow::Owned(
446                to_value(self.tera.get_escape_fn()(res.as_str().unwrap())).map_err(Error::json)?,
447            );
448        }
449
450        Ok(res)
451    }
452
453    /// Render an expression and never escape its result
454    fn safe_eval_expression(&mut self, expr: &'a Expr) -> Result<Val<'a>> {
455        let should_escape = self.should_escape;
456        self.should_escape = false;
457        let res = self.eval_expression(expr);
458        self.should_escape = should_escape;
459        res
460    }
461
462    /// Evaluate a set tag and add the value to the right context
463    fn eval_set(&mut self, set: &'a Set) -> Result<()> {
464        let assigned_value = self.safe_eval_expression(&set.value)?;
465        self.call_stack.add_assignment(&set.key[..], set.global, assigned_value);
466        Ok(())
467    }
468
469    fn eval_test(&mut self, test: &'a Test) -> Result<bool> {
470        let tester_fn = self.tera.get_tester(&test.name)?;
471        let err_wrap = |e| Error::call_test(&test.name, e);
472
473        let mut tester_args = vec![];
474        for arg in &test.args {
475            tester_args
476                .push(self.safe_eval_expression(arg).map_err(err_wrap)?.clone().into_owned());
477        }
478
479        let found = self.lookup_ident(&test.ident).map(|found| found.clone().into_owned()).ok();
480
481        let result = tester_fn.test(found.as_ref(), &tester_args).map_err(err_wrap)?;
482        if test.negated {
483            Ok(!result)
484        } else {
485            Ok(result)
486        }
487    }
488
489    fn eval_tera_fn_call(
490        &mut self,
491        function_call: &'a FunctionCall,
492        needs_escape: &mut bool,
493    ) -> Result<Val<'a>> {
494        let tera_fn = self.tera.get_function(&function_call.name)?;
495        *needs_escape = !tera_fn.is_safe();
496
497        let err_wrap = |e| Error::call_function(&function_call.name, e);
498
499        let mut args = HashMap::with_capacity(function_call.args.len());
500        for (arg_name, expr) in &function_call.args {
501            args.insert(
502                arg_name.to_string(),
503                self.safe_eval_expression(expr).map_err(err_wrap)?.clone().into_owned(),
504            );
505        }
506
507        Ok(Cow::Owned(tera_fn.call(&args).map_err(err_wrap)?))
508    }
509
510    fn eval_macro_call(&mut self, macro_call: &'a MacroCall, write: &mut impl Write) -> Result<()> {
511        let active_template_name = if let Some(block) = self.blocks.last() {
512            block.1
513        } else if self.template.name != self.template_root.name {
514            &self.template_root.name
515        } else {
516            &self.call_stack.active_template().name
517        };
518
519        let (macro_template_name, macro_definition) = self.macros.lookup_macro(
520            active_template_name,
521            &macro_call.namespace[..],
522            &macro_call.name[..],
523        )?;
524
525        let mut frame_context = FrameContext::with_capacity(macro_definition.args.len());
526
527        // First the default arguments
528        for (arg_name, default_value) in &macro_definition.args {
529            let value = match macro_call.args.get(arg_name) {
530                Some(val) => self.safe_eval_expression(val)?,
531                None => match *default_value {
532                    Some(ref val) => self.safe_eval_expression(val)?,
533                    None => {
534                        return Err(Error::msg(format!(
535                            "Macro `{}` is missing the argument `{}`",
536                            macro_call.name, arg_name
537                        )));
538                    }
539                },
540            };
541            frame_context.insert(arg_name, value);
542        }
543
544        self.call_stack.push_macro_frame(
545            &macro_call.namespace,
546            &macro_call.name,
547            frame_context,
548            self.tera.get_template(macro_template_name)?,
549        );
550
551        self.render_body(&macro_definition.body, write)?;
552
553        self.call_stack.pop();
554
555        Ok(())
556    }
557
558    fn eval_filter(
559        &mut self,
560        value: &Val<'a>,
561        fn_call: &'a FunctionCall,
562        needs_escape: &mut bool,
563    ) -> Result<Val<'a>> {
564        let filter_fn = self.tera.get_filter(&fn_call.name)?;
565        *needs_escape = !filter_fn.is_safe();
566
567        let err_wrap = |e| Error::call_filter(&fn_call.name, e);
568
569        let mut args = HashMap::with_capacity(fn_call.args.len());
570        for (arg_name, expr) in &fn_call.args {
571            args.insert(
572                arg_name.to_string(),
573                self.safe_eval_expression(expr).map_err(err_wrap)?.clone().into_owned(),
574            );
575        }
576
577        Ok(Cow::Owned(filter_fn.filter(value, &args).map_err(err_wrap)?))
578    }
579
580    fn eval_as_bool(&mut self, bool_expr: &'a Expr) -> Result<bool> {
581        let res = match bool_expr.val {
582            ExprVal::Logic(LogicExpr { ref lhs, ref rhs, ref operator }) => {
583                match *operator {
584                    LogicOperator::Or => self.eval_as_bool(lhs)? || self.eval_as_bool(rhs)?,
585                    LogicOperator::And => self.eval_as_bool(lhs)? && self.eval_as_bool(rhs)?,
586                    LogicOperator::Gt
587                    | LogicOperator::Gte
588                    | LogicOperator::Lt
589                    | LogicOperator::Lte => {
590                        let l = self.eval_expr_as_number(lhs)?;
591                        let r = self.eval_expr_as_number(rhs)?;
592                        let (ll, rr) = match (l, r) {
593                            (Some(nl), Some(nr)) => (nl, nr),
594                            _ => return Err(Error::msg("Comparison to NaN")),
595                        };
596
597                        match *operator {
598                            LogicOperator::Gte => ll.as_f64().unwrap() >= rr.as_f64().unwrap(),
599                            LogicOperator::Gt => ll.as_f64().unwrap() > rr.as_f64().unwrap(),
600                            LogicOperator::Lte => ll.as_f64().unwrap() <= rr.as_f64().unwrap(),
601                            LogicOperator::Lt => ll.as_f64().unwrap() < rr.as_f64().unwrap(),
602                            _ => unreachable!(),
603                        }
604                    }
605                    LogicOperator::Eq | LogicOperator::NotEq => {
606                        let mut lhs_val = self.eval_expression(lhs)?;
607                        let mut rhs_val = self.eval_expression(rhs)?;
608
609                        // Monomorphize number vals.
610                        if lhs_val.is_number() || rhs_val.is_number() {
611                            // We're not implementing JS so can't compare things of different types
612                            if !lhs_val.is_number() || !rhs_val.is_number() {
613                                return Ok(false);
614                            }
615
616                            lhs_val = Cow::Owned(Value::Number(
617                                Number::from_f64(lhs_val.as_f64().unwrap()).unwrap(),
618                            ));
619                            rhs_val = Cow::Owned(Value::Number(
620                                Number::from_f64(rhs_val.as_f64().unwrap()).unwrap(),
621                            ));
622                        }
623
624                        match *operator {
625                            LogicOperator::Eq => *lhs_val == *rhs_val,
626                            LogicOperator::NotEq => *lhs_val != *rhs_val,
627                            _ => unreachable!(),
628                        }
629                    }
630                }
631            }
632            ExprVal::Ident(_) => {
633                let mut res = self
634                    .eval_expression(bool_expr)
635                    .unwrap_or(Cow::Owned(Value::Bool(false)))
636                    .is_truthy();
637                if bool_expr.negated {
638                    res = !res;
639                }
640                res
641            }
642            ExprVal::Math(_) | ExprVal::Int(_) | ExprVal::Float(_) => {
643                match self.eval_as_number(&bool_expr.val)? {
644                    Some(n) => n.as_f64().unwrap() != 0.0,
645                    None => false,
646                }
647            }
648            ExprVal::In(ref in_cond) => self.eval_in_condition(in_cond)?,
649            ExprVal::Test(ref test) => self.eval_test(test)?,
650            ExprVal::Bool(val) => val,
651            ExprVal::String(ref string) => !string.is_empty(),
652            ExprVal::FunctionCall(ref fn_call) => {
653                let v = self.eval_tera_fn_call(fn_call, &mut false)?;
654                match v.as_bool() {
655                    Some(val) => val,
656                    None => {
657                        return Err(Error::msg(format!(
658                            "Function `{}` was used in a logic operation but is not returning a bool",
659                            fn_call.name,
660                        )));
661                    }
662                }
663            }
664            ExprVal::StringConcat(_) => {
665                let res = self.eval_expression(bool_expr)?;
666                !res.as_str().unwrap().is_empty()
667            }
668            ExprVal::MacroCall(ref macro_call) => {
669                let mut buf = Vec::new();
670                self.eval_macro_call(macro_call, &mut buf)?;
671                !buf.is_empty()
672            }
673            _ => unreachable!("unimplemented logic operation for {:?}", bool_expr),
674        };
675
676        if bool_expr.negated {
677            return Ok(!res);
678        }
679
680        Ok(res)
681    }
682
683    /// In some cases, we will have filters in lhs/rhs of a math expression
684    /// `eval_as_number` only works on ExprVal rather than Expr
685    fn eval_expr_as_number(&mut self, expr: &'a Expr) -> Result<Option<Number>> {
686        if !expr.filters.is_empty() {
687            match *self.eval_expression(expr)? {
688                Value::Number(ref s) => Ok(Some(s.clone())),
689                _ => {
690                    Err(Error::msg("Tried to do math with an expression not resulting in a number"))
691                }
692            }
693        } else {
694            self.eval_as_number(&expr.val)
695        }
696    }
697
698    /// Return the value of an expression as a number
699    fn eval_as_number(&mut self, expr: &'a ExprVal) -> Result<Option<Number>> {
700        let result = match *expr {
701            ExprVal::Ident(ref ident) => {
702                let v = &*self.lookup_ident(ident)?;
703                if v.is_i64() {
704                    Some(Number::from(v.as_i64().unwrap()))
705                } else if v.is_u64() {
706                    Some(Number::from(v.as_u64().unwrap()))
707                } else if v.is_f64() {
708                    Some(Number::from_f64(v.as_f64().unwrap()).unwrap())
709                } else {
710                    return Err(Error::msg(format!(
711                        "Variable `{}` was used in a math operation but is not a number",
712                        ident
713                    )));
714                }
715            }
716            ExprVal::Int(val) => Some(Number::from(val)),
717            ExprVal::Float(val) => Some(Number::from_f64(val).unwrap()),
718            ExprVal::Math(MathExpr { ref lhs, ref rhs, ref operator }) => {
719                let (l, r) = match (self.eval_expr_as_number(lhs)?, self.eval_expr_as_number(rhs)?)
720                {
721                    (Some(l), Some(r)) => (l, r),
722                    _ => return Ok(None),
723                };
724
725                match *operator {
726                    MathOperator::Mul => {
727                        if l.is_i64() && r.is_i64() {
728                            let ll = l.as_i64().unwrap();
729                            let rr = r.as_i64().unwrap();
730                            let res = match ll.checked_mul(rr) {
731                                Some(s) => s,
732                                None => {
733                                    return Err(Error::msg(format!(
734                                        "{} x {} results in an out of bounds i64",
735                                        ll, rr
736                                    )));
737                                }
738                            };
739
740                            Some(Number::from(res))
741                        } else if l.is_u64() && r.is_u64() {
742                            let ll = l.as_u64().unwrap();
743                            let rr = r.as_u64().unwrap();
744                            let res = match ll.checked_mul(rr) {
745                                Some(s) => s,
746                                None => {
747                                    return Err(Error::msg(format!(
748                                        "{} x {} results in an out of bounds u64",
749                                        ll, rr
750                                    )));
751                                }
752                            };
753                            Some(Number::from(res))
754                        } else {
755                            let ll = l.as_f64().unwrap();
756                            let rr = r.as_f64().unwrap();
757                            Number::from_f64(ll * rr)
758                        }
759                    }
760                    MathOperator::Div => {
761                        let ll = l.as_f64().unwrap();
762                        let rr = r.as_f64().unwrap();
763                        let res = ll / rr;
764                        if res.is_nan() {
765                            None
766                        } else if res.round() == res && res.is_finite() {
767                            Some(Number::from(res as i64))
768                        } else {
769                            Number::from_f64(res)
770                        }
771                    }
772                    MathOperator::Add => {
773                        if l.is_i64() && r.is_i64() {
774                            let ll = l.as_i64().unwrap();
775                            let rr = r.as_i64().unwrap();
776                            let res = match ll.checked_add(rr) {
777                                Some(s) => s,
778                                None => {
779                                    return Err(Error::msg(format!(
780                                        "{} + {} results in an out of bounds i64",
781                                        ll, rr
782                                    )));
783                                }
784                            };
785                            Some(Number::from(res))
786                        } else if l.is_u64() && r.is_u64() {
787                            let ll = l.as_u64().unwrap();
788                            let rr = r.as_u64().unwrap();
789                            let res = match ll.checked_add(rr) {
790                                Some(s) => s,
791                                None => {
792                                    return Err(Error::msg(format!(
793                                        "{} + {} results in an out of bounds u64",
794                                        ll, rr
795                                    )));
796                                }
797                            };
798                            Some(Number::from(res))
799                        } else {
800                            let ll = l.as_f64().unwrap();
801                            let rr = r.as_f64().unwrap();
802                            Some(Number::from_f64(ll + rr).unwrap())
803                        }
804                    }
805                    MathOperator::Sub => {
806                        if l.is_i64() && r.is_i64() {
807                            let ll = l.as_i64().unwrap();
808                            let rr = r.as_i64().unwrap();
809                            let res = match ll.checked_sub(rr) {
810                                Some(s) => s,
811                                None => {
812                                    return Err(Error::msg(format!(
813                                        "{} - {} results in an out of bounds i64",
814                                        ll, rr
815                                    )));
816                                }
817                            };
818                            Some(Number::from(res))
819                        } else if l.is_u64() && r.is_u64() {
820                            let ll = l.as_u64().unwrap();
821                            let rr = r.as_u64().unwrap();
822                            let res = match ll.checked_sub(rr) {
823                                Some(s) => s,
824                                None => {
825                                    return Err(Error::msg(format!(
826                                        "{} - {} results in an out of bounds u64",
827                                        ll, rr
828                                    )));
829                                }
830                            };
831                            Some(Number::from(res))
832                        } else {
833                            let ll = l.as_f64().unwrap();
834                            let rr = r.as_f64().unwrap();
835                            Some(Number::from_f64(ll - rr).unwrap())
836                        }
837                    }
838                    MathOperator::Modulo => {
839                        if l.is_i64() && r.is_i64() {
840                            let ll = l.as_i64().unwrap();
841                            let rr = r.as_i64().unwrap();
842                            if rr == 0 {
843                                return Err(Error::msg(format!(
844                                    "Tried to do a modulo by zero: {:?}/{:?}",
845                                    lhs, rhs
846                                )));
847                            }
848                            Some(Number::from(ll % rr))
849                        } else if l.is_u64() && r.is_u64() {
850                            let ll = l.as_u64().unwrap();
851                            let rr = r.as_u64().unwrap();
852                            if rr == 0 {
853                                return Err(Error::msg(format!(
854                                    "Tried to do a modulo by zero: {:?}/{:?}",
855                                    lhs, rhs
856                                )));
857                            }
858                            Some(Number::from(ll % rr))
859                        } else {
860                            let ll = l.as_f64().unwrap();
861                            let rr = r.as_f64().unwrap();
862                            Number::from_f64(ll % rr)
863                        }
864                    }
865                }
866            }
867            ExprVal::FunctionCall(ref fn_call) => {
868                let v = self.eval_tera_fn_call(fn_call, &mut false)?;
869                if v.is_i64() {
870                    Some(Number::from(v.as_i64().unwrap()))
871                } else if v.is_u64() {
872                    Some(Number::from(v.as_u64().unwrap()))
873                } else if v.is_f64() {
874                    Some(Number::from_f64(v.as_f64().unwrap()).unwrap())
875                } else {
876                    return Err(Error::msg(format!(
877                        "Function `{}` was used in a math operation but is not returning a number",
878                        fn_call.name
879                    )));
880                }
881            }
882            ExprVal::String(ref val) => {
883                return Err(Error::msg(format!("Tried to do math with a string: `{}`", val)));
884            }
885            ExprVal::Bool(val) => {
886                return Err(Error::msg(format!("Tried to do math with a boolean: `{}`", val)));
887            }
888            ExprVal::StringConcat(ref val) => {
889                return Err(Error::msg(format!(
890                    "Tried to do math with a string concatenation: {}",
891                    val.to_template_string()
892                )));
893            }
894            ExprVal::Test(ref test) => {
895                return Err(Error::msg(format!("Tried to do math with a test: {}", test.name)));
896            }
897            _ => unreachable!("unimplemented math expression for {:?}", expr),
898        };
899
900        Ok(result)
901    }
902
903    /// Only called while rendering a block.
904    /// This will look up the block we are currently rendering and its level and try to render
905    /// the block at level + n, where would be the next template in the hierarchy the block is present
906    fn do_super(&mut self, write: &mut impl Write) -> Result<()> {
907        let &(block_name, _, level) = self.blocks.last().unwrap();
908        let mut next_level = level + 1;
909
910        while next_level <= self.template.parents.len() {
911            let blocks_definitions = &self
912                .tera
913                .get_template(&self.template.parents[next_level - 1])
914                .unwrap()
915                .blocks_definitions;
916
917            if let Some(block_def) = blocks_definitions.get(block_name) {
918                let (ref tpl_name, Block { ref body, .. }) = block_def[0];
919                self.blocks.push((block_name, tpl_name, next_level));
920
921                self.render_body(body, write)?;
922                self.blocks.pop();
923
924                // Can't go any higher for that block anymore?
925                if next_level >= self.template.parents.len() {
926                    // then remove it from the stack, we're done with it
927                    self.blocks.pop();
928                }
929                return Ok(());
930            } else {
931                next_level += 1;
932            }
933        }
934
935        Err(Error::msg("Tried to use super() in the top level block"))
936    }
937
938    /// Looks up identifier and returns its value
939    fn lookup_ident(&self, key: &str) -> Result<Val<'a>> {
940        // Magical variable that just dumps the context
941        if key == MAGICAL_DUMP_VAR {
942            // Unwraps are safe since we are dealing with things that are already Value
943            return Ok(Cow::Owned(
944                to_value(
945                    to_string_pretty(&self.call_stack.current_context_cloned().take()).unwrap(),
946                )
947                .unwrap(),
948            ));
949        }
950
951        process_path(key, &self.call_stack)
952    }
953
954    /// Process the given node, appending the string result to the buffer
955    /// if it is possible
956    fn render_node(&mut self, node: &'a Node, write: &mut impl Write) -> Result<()> {
957        match *node {
958            // Comments are ignored when rendering
959            Node::Comment(_, _) => (),
960            Node::Text(ref s) | Node::Raw(_, ref s, _) => write!(write, "{}", s)?,
961            Node::VariableBlock(_, ref expr) => self.eval_expression(expr)?.render(write)?,
962            Node::Set(_, ref set) => self.eval_set(set)?,
963            Node::FilterSection(_, FilterSection { ref filter, ref body }, _) => {
964                let body = render_to_string(
965                    || format!("filter {}", filter.name),
966                    |w| self.render_body(body, w),
967                )?;
968                // the safe filter doesn't actually exist
969                if filter.name == "safe" {
970                    write!(write, "{}", body)?;
971                } else {
972                    self.eval_filter(&Cow::Owned(Value::String(body)), filter, &mut false)?
973                        .render(write)?;
974                }
975            }
976            // Macros have been imported at the beginning
977            Node::ImportMacro(_, _, _) => (),
978            Node::If(ref if_node, _) => self.render_if_node(if_node, write)?,
979            Node::Forloop(_, ref forloop, _) => self.render_for_loop(forloop, write)?,
980            Node::Break(_) => {
981                self.call_stack.break_for_loop()?;
982            }
983            Node::Continue(_) => {
984                self.call_stack.continue_for_loop()?;
985            }
986            Node::Block(_, ref block, _) => self.render_block(block, 0, write)?,
987            Node::Super => self.do_super(write)?,
988            Node::Include(_, ref tpl_names, ignore_missing) => {
989                let mut found = false;
990                for tpl_name in tpl_names {
991                    let template = self.tera.get_template(tpl_name);
992                    if template.is_err() {
993                        continue;
994                    }
995                    let template = template.unwrap();
996                    self.macros.add_macros_from_template(self.tera, template)?;
997                    self.call_stack.push_include_frame(tpl_name, template);
998                    self.render_body(&template.ast, write)?;
999                    self.call_stack.pop();
1000                    found = true;
1001                    break;
1002                }
1003                if !found && !ignore_missing {
1004                    return Err(Error::template_not_found(
1005                        ["[", &tpl_names.join(", "), "]"].join(""),
1006                    ));
1007                }
1008            }
1009            Node::Extends(_, ref name) => {
1010                return Err(Error::msg(format!(
1011                    "Inheritance in included templates is currently not supported: extended `{}`",
1012                    name
1013                )));
1014            }
1015            // Macro definitions are ignored when rendering
1016            Node::MacroDefinition(_, _, _) => (),
1017        };
1018
1019        Ok(())
1020    }
1021
1022    /// Helper fn that tries to find the current context: are we in a macro? in a parent template?
1023    /// in order to give the best possible error when getting an error when rendering a tpl
1024    fn get_error_location(&self) -> String {
1025        let mut error_location = format!("Failed to render '{}'", self.template.name);
1026
1027        // in a macro?
1028        if self.call_stack.current_frame().kind == FrameType::Macro {
1029            let frame = self.call_stack.current_frame();
1030            error_location += &format!(
1031                ": error while rendering macro `{}::{}`",
1032                frame.macro_namespace.expect("Macro namespace"),
1033                frame.name,
1034            );
1035        }
1036
1037        // which template are we in?
1038        if let Some(&(name, _template, ref level)) = self.blocks.last() {
1039            let block_def =
1040                self.template.blocks_definitions.get(&name.to_string()).and_then(|b| b.get(*level));
1041
1042            if let Some((tpl_name, _)) = block_def {
1043                if tpl_name != &self.template.name {
1044                    error_location += &format!(" (error happened in '{}').", tpl_name);
1045                }
1046            } else {
1047                error_location += " (error happened in a parent template)";
1048            }
1049        } else if let Some(parent) = self.template.parents.last() {
1050            // Error happened in the base template, outside of blocks
1051            error_location += &format!(" (error happened in '{}').", parent);
1052        }
1053
1054        error_location
1055    }
1056
1057    /// Entry point for the rendering
1058    pub fn render(&mut self, write: &mut impl Write) -> Result<()> {
1059        for node in &self.template_root.ast {
1060            self.render_node(node, write)
1061                .map_err(|e| Error::chain(self.get_error_location(), e))?;
1062        }
1063
1064        Ok(())
1065    }
1066}