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