1 //! This module implements the bytecode interpreter that actually renders the templates.
2 
3 use compiler::TemplateCompiler;
4 use error::Error::*;
5 use error::*;
6 use instruction::{Instruction, PathSlice, PathStep};
7 use serde_json::Value;
8 use std::collections::HashMap;
9 use std::fmt::Write;
10 use std::slice;
11 use ValueFormatter;
12 
13 /// Enum defining the different kinds of records on the context stack.
14 enum ContextElement<'render, 'template> {
15     /// Object contexts shadow everything below them on the stack, because every name is looked up
16     /// in this object.
17     Object(&'render Value),
18     /// Named contexts shadow only one name. Any path that starts with that name is looked up in
19     /// this object, and all others are passed on down the stack.
20     Named(&'template str, &'render Value),
21     /// Iteration contexts shadow one name with the current value of the iteration. They also
22     /// store the iteration state. The two usizes are the index of the current value and the length
23     /// of the array that we're iterating over.
24     Iteration(
25         &'template str,
26         &'render Value,
27         usize,
28         usize,
29         slice::Iter<'render, Value>,
30     ),
31 }
32 
33 /// Helper struct which mostly exists so that I have somewhere to put functions that access the
34 /// rendering context stack.
35 struct RenderContext<'render, 'template> {
36     original_text: &'template str,
37     context_stack: Vec<ContextElement<'render, 'template>>,
38 }
39 impl<'render, 'template> RenderContext<'render, 'template> {
40     /// Look up the given path in the context stack and return the value (if found) or an error (if
41     /// not)
lookup(&self, path: PathSlice) -> Result<&'render Value>42     fn lookup(&self, path: PathSlice) -> Result<&'render Value> {
43         for stack_layer in self.context_stack.iter().rev() {
44             match stack_layer {
45                 ContextElement::Object(obj) => return self.lookup_in(path, obj),
46                 ContextElement::Named(name, obj) => {
47                     if *name == &*path[0] {
48                         return self.lookup_in(&path[1..], obj);
49                     }
50                 }
51                 ContextElement::Iteration(name, obj, _, _, _) => {
52                     if *name == &*path[0] {
53                         return self.lookup_in(&path[1..], obj);
54                     }
55                 }
56             }
57         }
58         panic!("Attempted to do a lookup with an empty context stack. That shouldn't be possible.")
59     }
60 
61     /// Look up a path within a given value object and return the resulting value (if found) or
62     /// an error (if not)
lookup_in(&self, path: PathSlice, object: &'render Value) -> Result<&'render Value>63     fn lookup_in(&self, path: PathSlice, object: &'render Value) -> Result<&'render Value> {
64         let mut current = object;
65         for step in path.iter() {
66             if let PathStep::Index(_, n) = step {
67                 if let Some(next) = current.get(n) {
68                     current = next;
69                     continue;
70                 }
71             }
72 
73             let step: &str = &*step;
74 
75             match current.get(step) {
76                 Some(next) => current = next,
77                 None => return Err(lookup_error(self.original_text, step, path, current)),
78             }
79         }
80         Ok(current)
81     }
82 
83     /// Look up the index and length values for the top iteration context on the stack.
lookup_index(&self) -> Result<(usize, usize)>84     fn lookup_index(&self) -> Result<(usize, usize)> {
85         for stack_layer in self.context_stack.iter().rev() {
86             match stack_layer {
87                 ContextElement::Iteration(_, _, index, length, _) => return Ok((*index, *length)),
88                 _ => continue,
89             }
90         }
91         Err(GenericError {
92             msg: "Used @index outside of a foreach block.".to_string(),
93         })
94     }
95 
96     /// Look up the root context object
lookup_root(&self) -> Result<&'render Value>97     fn lookup_root(&self) -> Result<&'render Value> {
98         match self.context_stack.get(0) {
99             Some(ContextElement::Object(obj)) => Ok(obj),
100             Some(_) => {
101                 panic!("Expected Object value at root of context stack, but was something else.")
102             }
103             None => panic!(
104                 "Attempted to do a lookup with an empty context stack. That shouldn't be possible."
105             ),
106         }
107     }
108 }
109 
110 /// Structure representing a parsed template. It holds the bytecode program for rendering the
111 /// template as well as the length of the original template string, which is used as a guess to
112 /// pre-size the output string buffer.
113 pub(crate) struct Template<'template> {
114     original_text: &'template str,
115     instructions: Vec<Instruction<'template>>,
116     template_len: usize,
117 }
118 impl<'template> Template<'template> {
119     /// Create a Template from the given template string.
compile(text: &'template str) -> Result<Template>120     pub fn compile(text: &'template str) -> Result<Template> {
121         Ok(Template {
122             original_text: text,
123             template_len: text.len(),
124             instructions: TemplateCompiler::new(text).compile()?,
125         })
126     }
127 
128     /// Render this template into a string and return it (or any error if one is encountered).
render( &self, context: &Value, template_registry: &HashMap<&str, Template>, formatter_registry: &HashMap<&str, Box<ValueFormatter>>, default_formatter: &ValueFormatter, ) -> Result<String>129     pub fn render(
130         &self,
131         context: &Value,
132         template_registry: &HashMap<&str, Template>,
133         formatter_registry: &HashMap<&str, Box<ValueFormatter>>,
134         default_formatter: &ValueFormatter,
135     ) -> Result<String> {
136         // The length of the original template seems like a reasonable guess at the length of the
137         // output.
138         let mut output = String::with_capacity(self.template_len);
139         self.render_into(
140             context,
141             template_registry,
142             formatter_registry,
143             default_formatter,
144             &mut output,
145         )?;
146         Ok(output)
147     }
148 
149     /// Render this template into a given string. Used for calling other templates.
render_into( &self, context: &Value, template_registry: &HashMap<&str, Template>, formatter_registry: &HashMap<&str, Box<ValueFormatter>>, default_formatter: &ValueFormatter, output: &mut String, ) -> Result<()>150     pub fn render_into(
151         &self,
152         context: &Value,
153         template_registry: &HashMap<&str, Template>,
154         formatter_registry: &HashMap<&str, Box<ValueFormatter>>,
155         default_formatter: &ValueFormatter,
156         output: &mut String,
157     ) -> Result<()> {
158         let mut program_counter = 0;
159         let mut render_context = RenderContext {
160             original_text: self.original_text,
161             context_stack: vec![ContextElement::Object(context)],
162         };
163 
164         while program_counter < self.instructions.len() {
165             match &self.instructions[program_counter] {
166                 Instruction::Literal(text) => {
167                     output.push_str(text);
168                     program_counter += 1;
169                 }
170                 Instruction::Value(path) => {
171                     let first = path.first().unwrap();
172                     if first.starts_with('@') {
173                         // Currently we just hard-code the special @-keywords and have special
174                         // lookup functions to use them because there are lifetime complexities with
175                         // looking up values that don't live for as long as the given context object.
176                         let first: &str = &*first;
177                         match first {
178                             "@index" => {
179                                 write!(output, "{}", render_context.lookup_index()?.0).unwrap()
180                             }
181                             "@first" => {
182                                 write!(output, "{}", render_context.lookup_index()?.0 == 0).unwrap()
183                             }
184                             "@last" => {
185                                 let (index, length) = render_context.lookup_index()?;
186                                 write!(output, "{}", index == length - 1).unwrap()
187                             }
188                             "@root" => {
189                                 let value_to_render = render_context.lookup_root()?;
190                                 default_formatter(value_to_render, output)?;
191                             }
192                             _ => panic!(), // This should have been caught by the parser.
193                         }
194                     } else {
195                         let value_to_render = render_context.lookup(path)?;
196                         default_formatter(value_to_render, output)?;
197                     }
198                     program_counter += 1;
199                 }
200                 Instruction::FormattedValue(path, name) => {
201                     // The @ keywords aren't supported for formatted values. Should they be?
202                     let value_to_render = render_context.lookup(path)?;
203                     match formatter_registry.get(name) {
204                         Some(formatter) => {
205                             let formatter_result = formatter(value_to_render, output);
206                             if let Err(err) = formatter_result {
207                                 return Err(called_formatter_error(self.original_text, name, err));
208                             }
209                         }
210                         None => return Err(unknown_formatter(self.original_text, name)),
211                     }
212                     program_counter += 1;
213                 }
214                 Instruction::Branch(path, negate, target) => {
215                     let first = path.first().unwrap();
216                     let mut truthy = if first.starts_with('@') {
217                         let first: &str = &*first;
218                         match &*first {
219                             "@index" => render_context.lookup_index()?.0 != 0,
220                             "@first" => render_context.lookup_index()?.0 == 0,
221                             "@last" => {
222                                 let (index, length) = render_context.lookup_index()?;
223                                 index == (length - 1)
224                             }
225                             "@root" => self.value_is_truthy(render_context.lookup_root()?, path)?,
226                             other => panic!("Unknown keyword {}", other), // This should have been caught by the parser.
227                         }
228                     } else {
229                         let value_to_render = render_context.lookup(path)?;
230                         self.value_is_truthy(value_to_render, path)?
231                     };
232                     if *negate {
233                         truthy = !truthy;
234                     }
235 
236                     if truthy {
237                         program_counter = *target;
238                     } else {
239                         program_counter += 1;
240                     }
241                 }
242                 Instruction::PushNamedContext(path, name) => {
243                     let context_value = render_context.lookup(path)?;
244                     render_context
245                         .context_stack
246                         .push(ContextElement::Named(name, context_value));
247                     program_counter += 1;
248                 }
249                 Instruction::PushIterationContext(path, name) => {
250                     // We push a context with an invalid index and no value and then wait for the
251                     // following Iterate instruction to set the index and value properly.
252                     let first = path.first().unwrap();
253                     let context_value = match first {
254                         PathStep::Name("@root") => render_context.lookup_root()?,
255                         PathStep::Name(other) if other.starts_with('@') => {
256                             return Err(not_iterable_error(self.original_text, path))
257                         }
258                         _ => render_context.lookup(path)?,
259                     };
260                     match context_value {
261                         Value::Array(ref arr) => {
262                             render_context.context_stack.push(ContextElement::Iteration(
263                                 name,
264                                 &Value::Null,
265                                 ::std::usize::MAX,
266                                 arr.len(),
267                                 arr.iter(),
268                             ))
269                         }
270                         _ => return Err(not_iterable_error(self.original_text, path)),
271                     };
272                     program_counter += 1;
273                 }
274                 Instruction::PopContext => {
275                     render_context.context_stack.pop();
276                     program_counter += 1;
277                 }
278                 Instruction::Goto(target) => {
279                     program_counter = *target;
280                 }
281                 Instruction::Iterate(target) => {
282                     match render_context.context_stack.last_mut() {
283                         Some(ContextElement::Iteration(_, val, index, _, iter)) => {
284                             match iter.next() {
285                                 Some(new_val) => {
286                                     *val = new_val;
287                                     // On the first iteration, this will be usize::MAX so it will
288                                     // wrap around to zero.
289                                     *index = index.wrapping_add(1);
290                                     program_counter += 1;
291                                 }
292                                 None => {
293                                     program_counter = *target;
294                                 }
295                             }
296                         }
297                         _ => panic!("Malformed program."),
298                     };
299                 }
300                 Instruction::Call(template_name, path) => {
301                     let context_value = render_context.lookup(path)?;
302                     match template_registry.get(template_name) {
303                         Some(templ) => {
304                             let called_templ_result = templ.render_into(
305                                 context_value,
306                                 template_registry,
307                                 formatter_registry,
308                                 default_formatter,
309                                 output,
310                             );
311                             if let Err(err) = called_templ_result {
312                                 return Err(called_template_error(
313                                     self.original_text,
314                                     template_name,
315                                     err,
316                                 ));
317                             }
318                         }
319                         None => return Err(unknown_template(self.original_text, template_name)),
320                     }
321                     program_counter += 1;
322                 }
323             }
324         }
325         Ok(())
326     }
327 
value_is_truthy(&self, value: &Value, path: PathSlice) -> Result<bool>328     fn value_is_truthy(&self, value: &Value, path: PathSlice) -> Result<bool> {
329         let truthy = match value {
330             Value::Null => false,
331             Value::Bool(b) => *b,
332             Value::Number(n) => match n.as_f64() {
333                 Some(float) => float != 0.0,
334                 None => {
335                     return Err(truthiness_error(self.original_text, path));
336                 }
337             },
338             Value::String(s) => !s.is_empty(),
339             Value::Array(arr) => !arr.is_empty(),
340             Value::Object(_) => true,
341         };
342         Ok(truthy)
343     }
344 }
345 
346 #[cfg(test)]
347 mod test {
348     use super::*;
349     use compiler::TemplateCompiler;
350 
compile(text: &'static str) -> Template<'static>351     fn compile(text: &'static str) -> Template<'static> {
352         Template {
353             original_text: text,
354             template_len: text.len(),
355             instructions: TemplateCompiler::new(text).compile().unwrap(),
356         }
357     }
358 
359     #[derive(Serialize)]
360     struct NestedContext {
361         value: usize,
362     }
363 
364     #[derive(Serialize)]
365     struct TestContext {
366         number: usize,
367         string: &'static str,
368         boolean: bool,
369         null: Option<usize>,
370         array: Vec<usize>,
371         nested: NestedContext,
372         escapes: &'static str,
373     }
374 
context() -> Value375     fn context() -> Value {
376         let ctx = TestContext {
377             number: 5,
378             string: "test",
379             boolean: true,
380             null: None,
381             array: vec![1, 2, 3],
382             nested: NestedContext { value: 10 },
383             escapes: "1:< 2:> 3:& 4:' 5:\"",
384         };
385         ::serde_json::to_value(&ctx).unwrap()
386     }
387 
other_templates() -> HashMap<&'static str, Template<'static>>388     fn other_templates() -> HashMap<&'static str, Template<'static>> {
389         let mut map = HashMap::new();
390         map.insert("my_macro", compile("{value}"));
391         map
392     }
393 
format(value: &Value, output: &mut String) -> Result<()>394     fn format(value: &Value, output: &mut String) -> Result<()> {
395         output.push_str("{");
396         ::format(value, output)?;
397         output.push_str("}");
398         Ok(())
399     }
400 
formatters() -> HashMap<&'static str, Box<ValueFormatter>>401     fn formatters() -> HashMap<&'static str, Box<ValueFormatter>> {
402         let mut map = HashMap::<&'static str, Box<ValueFormatter>>::new();
403         map.insert("my_formatter", Box::new(format));
404         map
405     }
406 
default_formatter() -> &'static ValueFormatter407     pub fn default_formatter() -> &'static ValueFormatter {
408         &::format
409     }
410 
411     #[test]
test_literal()412     fn test_literal() {
413         let template = compile("Hello!");
414         let context = context();
415         let template_registry = other_templates();
416         let formatter_registry = formatters();
417         let string = template
418             .render(
419                 &context,
420                 &template_registry,
421                 &formatter_registry,
422                 &default_formatter(),
423             )
424             .unwrap();
425         assert_eq!("Hello!", &string);
426     }
427 
428     #[test]
test_value()429     fn test_value() {
430         let template = compile("{ number }");
431         let context = context();
432         let template_registry = other_templates();
433         let formatter_registry = formatters();
434         let string = template
435             .render(
436                 &context,
437                 &template_registry,
438                 &formatter_registry,
439                 &default_formatter(),
440             )
441             .unwrap();
442         assert_eq!("5", &string);
443     }
444 
445     #[test]
test_path()446     fn test_path() {
447         let template = compile("The number of the day is { nested.value }.");
448         let context = context();
449         let template_registry = other_templates();
450         let formatter_registry = formatters();
451         let string = template
452             .render(
453                 &context,
454                 &template_registry,
455                 &formatter_registry,
456                 &default_formatter(),
457             )
458             .unwrap();
459         assert_eq!("The number of the day is 10.", &string);
460     }
461 
462     #[test]
test_if_taken()463     fn test_if_taken() {
464         let template = compile("{{ if boolean }}Hello!{{ endif }}");
465         let context = context();
466         let template_registry = other_templates();
467         let formatter_registry = formatters();
468         let string = template
469             .render(
470                 &context,
471                 &template_registry,
472                 &formatter_registry,
473                 &default_formatter(),
474             )
475             .unwrap();
476         assert_eq!("Hello!", &string);
477     }
478 
479     #[test]
test_if_untaken()480     fn test_if_untaken() {
481         let template = compile("{{ if null }}Hello!{{ endif }}");
482         let context = context();
483         let template_registry = other_templates();
484         let formatter_registry = formatters();
485         let string = template
486             .render(
487                 &context,
488                 &template_registry,
489                 &formatter_registry,
490                 &default_formatter(),
491             )
492             .unwrap();
493         assert_eq!("", &string);
494     }
495 
496     #[test]
test_if_else_taken()497     fn test_if_else_taken() {
498         let template = compile("{{ if boolean }}Hello!{{ else }}Goodbye!{{ endif }}");
499         let context = context();
500         let template_registry = other_templates();
501         let formatter_registry = formatters();
502         let string = template
503             .render(
504                 &context,
505                 &template_registry,
506                 &formatter_registry,
507                 &default_formatter(),
508             )
509             .unwrap();
510         assert_eq!("Hello!", &string);
511     }
512 
513     #[test]
test_if_else_untaken()514     fn test_if_else_untaken() {
515         let template = compile("{{ if null }}Hello!{{ else }}Goodbye!{{ endif }}");
516         let context = context();
517         let template_registry = other_templates();
518         let formatter_registry = formatters();
519         let string = template
520             .render(
521                 &context,
522                 &template_registry,
523                 &formatter_registry,
524                 &default_formatter(),
525             )
526             .unwrap();
527         assert_eq!("Goodbye!", &string);
528     }
529 
530     #[test]
test_ifnot_taken()531     fn test_ifnot_taken() {
532         let template = compile("{{ if not boolean }}Hello!{{ endif }}");
533         let context = context();
534         let template_registry = other_templates();
535         let formatter_registry = formatters();
536         let string = template
537             .render(
538                 &context,
539                 &template_registry,
540                 &formatter_registry,
541                 &default_formatter(),
542             )
543             .unwrap();
544         assert_eq!("", &string);
545     }
546 
547     #[test]
test_ifnot_untaken()548     fn test_ifnot_untaken() {
549         let template = compile("{{ if not null }}Hello!{{ endif }}");
550         let context = context();
551         let template_registry = other_templates();
552         let formatter_registry = formatters();
553         let string = template
554             .render(
555                 &context,
556                 &template_registry,
557                 &formatter_registry,
558                 &default_formatter(),
559             )
560             .unwrap();
561         assert_eq!("Hello!", &string);
562     }
563 
564     #[test]
test_ifnot_else_taken()565     fn test_ifnot_else_taken() {
566         let template = compile("{{ if not boolean }}Hello!{{ else }}Goodbye!{{ endif }}");
567         let context = context();
568         let template_registry = other_templates();
569         let formatter_registry = formatters();
570         let string = template
571             .render(
572                 &context,
573                 &template_registry,
574                 &formatter_registry,
575                 &default_formatter(),
576             )
577             .unwrap();
578         assert_eq!("Goodbye!", &string);
579     }
580 
581     #[test]
test_ifnot_else_untaken()582     fn test_ifnot_else_untaken() {
583         let template = compile("{{ if not null }}Hello!{{ else }}Goodbye!{{ endif }}");
584         let context = context();
585         let template_registry = other_templates();
586         let formatter_registry = formatters();
587         let string = template
588             .render(
589                 &context,
590                 &template_registry,
591                 &formatter_registry,
592                 &default_formatter(),
593             )
594             .unwrap();
595         assert_eq!("Hello!", &string);
596     }
597 
598     #[test]
test_nested_ifs()599     fn test_nested_ifs() {
600         let template = compile(
601             "{{ if boolean }}Hi, {{ if null }}there!{{ else }}Hello!{{ endif }}{{ endif }}",
602         );
603         let context = context();
604         let template_registry = other_templates();
605         let formatter_registry = formatters();
606         let string = template
607             .render(
608                 &context,
609                 &template_registry,
610                 &formatter_registry,
611                 &default_formatter(),
612             )
613             .unwrap();
614         assert_eq!("Hi, Hello!", &string);
615     }
616 
617     #[test]
test_with()618     fn test_with() {
619         let template = compile("{{ with nested as n }}{ n.value } { number }{{endwith}}");
620         let context = context();
621         let template_registry = other_templates();
622         let formatter_registry = formatters();
623         let string = template
624             .render(
625                 &context,
626                 &template_registry,
627                 &formatter_registry,
628                 &default_formatter(),
629             )
630             .unwrap();
631         assert_eq!("10 5", &string);
632     }
633 
634     #[test]
test_for_loop()635     fn test_for_loop() {
636         let template = compile("{{ for a in array }}{ a }{{ endfor }}");
637         let context = context();
638         let template_registry = other_templates();
639         let formatter_registry = formatters();
640         let string = template
641             .render(
642                 &context,
643                 &template_registry,
644                 &formatter_registry,
645                 &default_formatter(),
646             )
647             .unwrap();
648         assert_eq!("123", &string);
649     }
650 
651     #[test]
test_for_loop_index()652     fn test_for_loop_index() {
653         let template = compile("{{ for a in array }}{ @index }{{ endfor }}");
654         let context = context();
655         let template_registry = other_templates();
656         let formatter_registry = formatters();
657         let string = template
658             .render(
659                 &context,
660                 &template_registry,
661                 &formatter_registry,
662                 &default_formatter(),
663             )
664             .unwrap();
665         assert_eq!("012", &string);
666     }
667 
668     #[test]
test_for_loop_first()669     fn test_for_loop_first() {
670         let template =
671             compile("{{ for a in array }}{{if @first }}{ @index }{{ endif }}{{ endfor }}");
672         let context = context();
673         let template_registry = other_templates();
674         let formatter_registry = formatters();
675         let string = template
676             .render(
677                 &context,
678                 &template_registry,
679                 &formatter_registry,
680                 &default_formatter(),
681             )
682             .unwrap();
683         assert_eq!("0", &string);
684     }
685 
686     #[test]
test_for_loop_last()687     fn test_for_loop_last() {
688         let template =
689             compile("{{ for a in array }}{{ if @last}}{ @index }{{ endif }}{{ endfor }}");
690         let context = context();
691         let template_registry = other_templates();
692         let formatter_registry = formatters();
693         let string = template
694             .render(
695                 &context,
696                 &template_registry,
697                 &formatter_registry,
698                 &default_formatter(),
699             )
700             .unwrap();
701         assert_eq!("2", &string);
702     }
703 
704     #[test]
test_whitespace_stripping_value()705     fn test_whitespace_stripping_value() {
706         let template = compile("1  \n\t   {- number -}  \n   1");
707         let context = context();
708         let template_registry = other_templates();
709         let formatter_registry = formatters();
710         let string = template
711             .render(
712                 &context,
713                 &template_registry,
714                 &formatter_registry,
715                 &default_formatter(),
716             )
717             .unwrap();
718         assert_eq!("151", &string);
719     }
720 
721     #[test]
test_call()722     fn test_call() {
723         let template = compile("{{ call my_macro with nested }}");
724         let context = context();
725         let template_registry = other_templates();
726         let formatter_registry = formatters();
727         let string = template
728             .render(
729                 &context,
730                 &template_registry,
731                 &formatter_registry,
732                 &default_formatter(),
733             )
734             .unwrap();
735         assert_eq!("10", &string);
736     }
737 
738     #[test]
test_formatter()739     fn test_formatter() {
740         let template = compile("{ nested.value | my_formatter }");
741         let context = context();
742         let template_registry = other_templates();
743         let formatter_registry = formatters();
744         let string = template
745             .render(
746                 &context,
747                 &template_registry,
748                 &formatter_registry,
749                 &default_formatter(),
750             )
751             .unwrap();
752         assert_eq!("{10}", &string);
753     }
754 
755     #[test]
test_unknown()756     fn test_unknown() {
757         let template = compile("{ foobar }");
758         let context = context();
759         let template_registry = other_templates();
760         let formatter_registry = formatters();
761         template
762             .render(
763                 &context,
764                 &template_registry,
765                 &formatter_registry,
766                 &default_formatter(),
767             )
768             .unwrap_err();
769     }
770 
771     #[test]
test_escaping()772     fn test_escaping() {
773         let template = compile("{ escapes }");
774         let context = context();
775         let template_registry = other_templates();
776         let formatter_registry = formatters();
777         let string = template
778             .render(
779                 &context,
780                 &template_registry,
781                 &formatter_registry,
782                 &default_formatter(),
783             )
784             .unwrap();
785         assert_eq!("1:&lt; 2:&gt; 3:&amp; 4:&#39; 5:&quot;", &string);
786     }
787 
788     #[test]
test_unescaped()789     fn test_unescaped() {
790         let template = compile("{ escapes | unescaped }");
791         let context = context();
792         let template_registry = other_templates();
793         let mut formatter_registry = formatters();
794         formatter_registry.insert("unescaped", Box::new(::format_unescaped));
795         let string = template
796             .render(
797                 &context,
798                 &template_registry,
799                 &formatter_registry,
800                 &default_formatter(),
801             )
802             .unwrap();
803         assert_eq!("1:< 2:> 3:& 4:' 5:\"", &string);
804     }
805 
806     #[test]
test_root_print()807     fn test_root_print() {
808         let template = compile("{ @root }");
809         let context = "Hello World!";
810         let context = ::serde_json::to_value(&context).unwrap();
811         let template_registry = other_templates();
812         let formatter_registry = formatters();
813         let string = template
814             .render(
815                 &context,
816                 &template_registry,
817                 &formatter_registry,
818                 &default_formatter(),
819             )
820             .unwrap();
821         assert_eq!("Hello World!", &string);
822     }
823 
824     #[test]
test_root_branch()825     fn test_root_branch() {
826         let template = compile("{{ if @root }}Hello World!{{ endif }}");
827         let context = true;
828         let context = ::serde_json::to_value(&context).unwrap();
829         let template_registry = other_templates();
830         let formatter_registry = formatters();
831         let string = template
832             .render(
833                 &context,
834                 &template_registry,
835                 &formatter_registry,
836                 &default_formatter(),
837             )
838             .unwrap();
839         assert_eq!("Hello World!", &string);
840     }
841 
842     #[test]
test_root_iterate()843     fn test_root_iterate() {
844         let template = compile("{{ for a in @root }}{ a }{{ endfor }}");
845         let context = vec!["foo", "bar"];
846         let context = ::serde_json::to_value(&context).unwrap();
847         let template_registry = other_templates();
848         let formatter_registry = formatters();
849         let string = template
850             .render(
851                 &context,
852                 &template_registry,
853                 &formatter_registry,
854                 &default_formatter(),
855             )
856             .unwrap();
857         assert_eq!("foobar", &string);
858     }
859 
860     #[test]
test_number_truthiness_zero()861     fn test_number_truthiness_zero() {
862         let template = compile("{{ if @root }}truthy{{else}}not truthy{{ endif }}");
863         let context = 0;
864         let context = ::serde_json::to_value(&context).unwrap();
865         let template_registry = other_templates();
866         let formatter_registry = formatters();
867         let string = template
868             .render(
869                 &context,
870                 &template_registry,
871                 &formatter_registry,
872                 &default_formatter(),
873             )
874             .unwrap();
875         assert_eq!("not truthy", &string);
876     }
877 
878     #[test]
test_number_truthiness_one()879     fn test_number_truthiness_one() {
880         let template = compile("{{ if @root }}truthy{{else}}not truthy{{ endif }}");
881         let context = 1;
882         let context = ::serde_json::to_value(&context).unwrap();
883         let template_registry = other_templates();
884         let formatter_registry = formatters();
885         let string = template
886             .render(
887                 &context,
888                 &template_registry,
889                 &formatter_registry,
890                 &default_formatter(),
891             )
892             .unwrap();
893         assert_eq!("truthy", &string);
894     }
895 
896     #[test]
test_indexed_paths()897     fn test_indexed_paths() {
898         #[derive(Serialize)]
899         struct Context {
900             foo: (usize, usize),
901         }
902 
903         let template = compile("{ foo.1 }{ foo.0 }");
904         let context = Context { foo: (123, 456) };
905         let context = ::serde_json::to_value(&context).unwrap();
906         let template_registry = other_templates();
907         let formatter_registry = formatters();
908         let string = template
909             .render(
910                 &context,
911                 &template_registry,
912                 &formatter_registry,
913                 &default_formatter(),
914             )
915             .unwrap();
916         assert_eq!("456123", &string);
917     }
918 
919     #[test]
test_indexed_paths_fall_back_to_string_lookup()920     fn test_indexed_paths_fall_back_to_string_lookup() {
921         #[derive(Serialize)]
922         struct Context {
923             foo: HashMap<&'static str, usize>,
924         }
925 
926         let template = compile("{ foo.1 }{ foo.0 }");
927         let mut foo = HashMap::new();
928         foo.insert("0", 123);
929         foo.insert("1", 456);
930         let context = Context { foo };
931         let context = ::serde_json::to_value(&context).unwrap();
932         let template_registry = other_templates();
933         let formatter_registry = formatters();
934         let string = template
935             .render(
936                 &context,
937                 &template_registry,
938                 &formatter_registry,
939                 &default_formatter(),
940             )
941             .unwrap();
942         assert_eq!("456123", &string);
943     }
944 }
945