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 ¯o_call.namespace[..],
505 ¯o_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 ¯o_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 ¯o_call.namespace,
529 ¯o_call.name,
530 frame_context,
531 self.tera.get_template(macro_template_name)?,
532 );
533
534 let output = self.render_body(¯o_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(¯o_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