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
20static MAGICAL_DUMP_VAR: &str = "__tera_context";
22
23fn 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 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 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") .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
100pub struct Processor<'a> {
102 template: &'a Template,
104 template_root: &'a Template,
107 tera: &'a Tera,
109 call_stack: CallStack<'a>,
111 macros: MacroCollection<'a>,
113 should_escape: bool,
115 blocks: Vec<(&'a str, &'a str, usize)>,
119}
120
121impl<'a> Processor<'a> {
122 pub fn new(
124 template: &'a Template,
125 tera: &'a Tera,
126 context: &'a Context,
127 should_escape: bool,
128 ) -> Self {
129 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 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 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 if level < self.call_stack.active_template().parents.len() {
290 return self.render_block(block, level + 1, write);
291 }
292
293 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 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 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 if expr.negated {
440 return Ok(Cow::Owned(Value::Bool(!res.is_truthy())));
441 }
442
443 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 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 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 ¯o_call.namespace[..],
522 ¯o_call.name[..],
523 )?;
524
525 let mut frame_context = FrameContext::with_capacity(macro_definition.args.len());
526
527 for (arg_name, default_value) in ¯o_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 ¯o_call.namespace,
546 ¯o_call.name,
547 frame_context,
548 self.tera.get_template(macro_template_name)?,
549 );
550
551 self.render_body(¯o_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 if lhs_val.is_number() || rhs_val.is_number() {
611 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 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 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 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 if next_level >= self.template.parents.len() {
926 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 fn lookup_ident(&self, key: &str) -> Result<Val<'a>> {
940 if key == MAGICAL_DUMP_VAR {
942 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 fn render_node(&mut self, node: &'a Node, write: &mut impl Write) -> Result<()> {
957 match *node {
958 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 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 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 Node::MacroDefinition(_, _, _) => (),
1017 };
1018
1019 Ok(())
1020 }
1021
1022 fn get_error_location(&self) -> String {
1025 let mut error_location = format!("Failed to render '{}'", self.template.name);
1026
1027 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 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_location += &format!(" (error happened in '{}').", parent);
1052 }
1053
1054 error_location
1055 }
1056
1057 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}