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 format;
7 use instruction::{Instruction, PathSlice};
8 use serde_json::Value;
9 use std::collections::HashMap;
10 use std::fmt::Write;
11 use std::slice;
12 use ValueFormatter;
13 
14 /// Enum defining the different kinds of records on the context stack.
15 enum ContextElement<'render, 'template> {
16     /// Object contexts shadow everything below them on the stack, because every name is looked up
17     /// in this object.
18     Object(&'render Value),
19     /// Named contexts shadow only one name. Any path that starts with that name is looked up in
20     /// this object, and all others are passed on down the stack.
21     Named(&'template str, &'render Value),
22     /// Iteration contexts shadow one name with the current value of the iteration. They also
23     /// store the iteration state. The two usizes are the index of the current value and the length
24     /// of the array that we're iterating over.
25     Iteration(
26         &'template str,
27         &'render Value,
28         usize,
29         usize,
30         slice::Iter<'render, Value>,
31     ),
32 }
33 
34 /// Helper struct which mostly exists so that I have somewhere to put functions that access the
35 /// rendering context stack.
36 struct RenderContext<'render, 'template> {
37     original_text: &'template str,
38     context_stack: Vec<ContextElement<'render, 'template>>,
39 }
40 impl<'render, 'template> RenderContext<'render, 'template> {
41     /// Look up the given path in the context stack and return the value (if found) or an error (if
42     /// not)
lookup(&self, path: PathSlice) -> Result<&'render Value>43     fn lookup(&self, path: PathSlice) -> Result<&'render Value> {
44         for stack_layer in self.context_stack.iter().rev() {
45             match stack_layer {
46                 ContextElement::Object(obj) => return self.lookup_in(path, obj),
47                 ContextElement::Named(name, obj) => {
48                     if *name == path[0] {
49                         return self.lookup_in(&path[1..], obj);
50                     }
51                 }
52                 ContextElement::Iteration(name, obj, _, _, _) => {
53                     if *name == path[0] {
54                         return self.lookup_in(&path[1..], obj);
55                     }
56                 }
57             }
58         }
59         panic!("Attempted to do a lookup with an empty context stack. That shouldn't be possible.")
60     }
61 
62     /// Look up a path within a given value object and return the resulting value (if found) or
63     /// an error (if not)
lookup_in(&self, path: PathSlice, object: &'render Value) -> Result<&'render Value>64     fn lookup_in(&self, path: PathSlice, object: &'render Value) -> Result<&'render Value> {
65         let mut current = object;
66         for step in path.iter() {
67             match current.get(step) {
68                 Some(next) => current = next,
69                 None => return Err(lookup_error(self.original_text, step, path, current)),
70             }
71         }
72         Ok(current)
73     }
74 
75     /// Look up the index and length values for the top iteration context on the stack.
lookup_index(&self) -> Result<(usize, usize)>76     fn lookup_index(&self) -> Result<(usize, usize)> {
77         for stack_layer in self.context_stack.iter().rev() {
78             match stack_layer {
79                 ContextElement::Iteration(_, _, index, length, _) => return Ok((*index, *length)),
80                 _ => continue,
81             }
82         }
83         Err(GenericError {
84             msg: "Used @index outside of a foreach block.".to_string(),
85         })
86     }
87 }
88 
89 /// Structure representing a parsed template. It holds the bytecode program for rendering the
90 /// template as well as the length of the original template string, which is used as a guess to
91 /// pre-size the output string buffer.
92 pub(crate) struct Template<'template> {
93     original_text: &'template str,
94     instructions: Vec<Instruction<'template>>,
95     template_len: usize,
96 }
97 impl<'template> Template<'template> {
98     /// Create a Template from the given template string.
compile(text: &'template str) -> Result<Template>99     pub fn compile(text: &'template str) -> Result<Template> {
100         Ok(Template {
101             original_text: text,
102             template_len: text.len(),
103             instructions: TemplateCompiler::new(text).compile()?,
104         })
105     }
106 
107     /// 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>>, ) -> Result<String>108     pub fn render(
109         &self,
110         context: &Value,
111         template_registry: &HashMap<&str, Template>,
112         formatter_registry: &HashMap<&str, Box<ValueFormatter>>,
113     ) -> Result<String> {
114         // The length of the original template seems like a reasonable guess at the length of the
115         // output.
116         let mut output = String::with_capacity(self.template_len);
117         self.render_into(context, template_registry, formatter_registry, &mut output)?;
118         Ok(output)
119     }
120 
121     /// 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>>, output: &mut String, ) -> Result<()>122     pub fn render_into(
123         &self,
124         context: &Value,
125         template_registry: &HashMap<&str, Template>,
126         formatter_registry: &HashMap<&str, Box<ValueFormatter>>,
127         output: &mut String,
128     ) -> Result<()> {
129         let mut program_counter = 0;
130         let mut render_context = RenderContext {
131             original_text: self.original_text,
132             context_stack: vec![ContextElement::Object(context)],
133         };
134 
135         while program_counter < self.instructions.len() {
136             match &self.instructions[program_counter] {
137                 Instruction::Literal(text) => {
138                     output.push_str(text);
139                     program_counter += 1;
140                 }
141                 Instruction::Value(path) => {
142                     let first = *path.first().unwrap();
143                     if first.starts_with('@') {
144                         // Currently we just hard-code the special @-keywords and have special
145                         // lookup functions to use them because there are lifetime complexities with
146                         // looking up values that don't live for as long as the given context object.
147                         match first {
148                             "@index" => {
149                                 write!(output, "{}", render_context.lookup_index()?.0).unwrap()
150                             }
151                             "@first" => {
152                                 write!(output, "{}", render_context.lookup_index()?.0 == 0).unwrap()
153                             }
154                             "@last" => {
155                                 let (index, length) = render_context.lookup_index()?;
156                                 write!(output, "{}", index == length - 1).unwrap()
157                             }
158                             _ => panic!(), // This should have been caught by the parser.
159                         }
160                     } else {
161                         let value_to_render = render_context.lookup(path)?;
162                         format(value_to_render, output)?;
163                     }
164                     program_counter += 1;
165                 }
166                 Instruction::FormattedValue(path, name) => {
167                     // The @ keywords aren't supported for formatted values. Should they be?
168                     let value_to_render = render_context.lookup(path)?;
169                     match formatter_registry.get(name) {
170                         Some(formatter) => {
171                             let formatter_result = formatter(value_to_render, output);
172                             if let Err(err) = formatter_result {
173                                 return Err(called_formatter_error(self.original_text, name, err));
174                             }
175                         }
176                         None => return Err(unknown_formatter(self.original_text, name)),
177                     }
178                     program_counter += 1;
179                 }
180                 Instruction::Branch(path, negate, target) => {
181                     let first = *path.first().unwrap();
182                     let mut truthy = if first.starts_with('@') {
183                         match first {
184                             "@index" => render_context.lookup_index()?.0 != 0,
185                             "@first" => render_context.lookup_index()?.0 == 0,
186                             "@last" => {
187                                 let (index, length) = render_context.lookup_index()?;
188                                 index == (length - 1)
189                             }
190                             _ => panic!(), // This should have been caught by the parser.
191                         }
192                     } else {
193                         let value_to_render = render_context.lookup(path)?;
194                         match value_to_render {
195                             Value::Null => false,
196                             Value::Bool(b) => *b,
197                             Value::Number(n) => match n.as_f64() {
198                                 Some(float) => float != 0.0,
199                                 None => {
200                                     return Err(truthiness_error(self.original_text, path));
201                                 }
202                             },
203                             Value::String(s) => !s.is_empty(),
204                             Value::Array(arr) => !arr.is_empty(),
205                             Value::Object(_) => true,
206                         }
207                     };
208                     if *negate {
209                         truthy = !truthy;
210                     }
211 
212                     if truthy {
213                         program_counter = *target;
214                     } else {
215                         program_counter += 1;
216                     }
217                 }
218                 Instruction::PushNamedContext(path, name) => {
219                     let context_value = render_context.lookup(path)?;
220                     render_context
221                         .context_stack
222                         .push(ContextElement::Named(name, context_value));
223                     program_counter += 1;
224                 }
225                 Instruction::PushIterationContext(path, name) => {
226                     // We push a context with an invalid index and no value and then wait for the
227                     // following Iterate instruction to set the index and value properly.
228                     let context_value = render_context.lookup(path)?;
229                     match context_value {
230                         Value::Array(ref arr) => {
231                             render_context.context_stack.push(ContextElement::Iteration(
232                                 name,
233                                 &Value::Null,
234                                 ::std::usize::MAX,
235                                 arr.len(),
236                                 arr.iter(),
237                             ))
238                         }
239                         _ => return Err(not_iterable_error(self.original_text, path)),
240                     };
241                     program_counter += 1;
242                 }
243                 Instruction::PopContext => {
244                     render_context.context_stack.pop();
245                     program_counter += 1;
246                 }
247                 Instruction::Goto(target) => {
248                     program_counter = *target;
249                 }
250                 Instruction::Iterate(target) => {
251                     match render_context.context_stack.last_mut() {
252                         Some(ContextElement::Iteration(_, val, index, _, iter)) => {
253                             match iter.next() {
254                                 Some(new_val) => {
255                                     *val = new_val;
256                                     // On the first iteration, this will be usize::MAX so it will
257                                     // wrap around to zero.
258                                     *index = index.wrapping_add(1);
259                                     program_counter += 1;
260                                 }
261                                 None => {
262                                     program_counter = *target;
263                                 }
264                             }
265                         }
266                         _ => panic!("Malformed program."),
267                     };
268                 }
269                 Instruction::Call(template_name, path) => {
270                     let context_value = render_context.lookup(path)?;
271                     match template_registry.get(template_name) {
272                         Some(templ) => {
273                             let called_templ_result = templ.render_into(
274                                 context_value,
275                                 template_registry,
276                                 formatter_registry,
277                                 output,
278                             );
279                             if let Err(err) = called_templ_result {
280                                 return Err(called_template_error(
281                                     self.original_text,
282                                     template_name,
283                                     err,
284                                 ));
285                             }
286                         }
287                         None => return Err(unknown_template(self.original_text, template_name)),
288                     }
289                     program_counter += 1;
290                 }
291             }
292         }
293         Ok(())
294     }
295 }
296 
297 #[cfg(test)]
298 mod test {
299     use super::*;
300     use compiler::TemplateCompiler;
301 
compile(text: &'static str) -> Template<'static>302     fn compile(text: &'static str) -> Template<'static> {
303         Template {
304             original_text: text,
305             template_len: text.len(),
306             instructions: TemplateCompiler::new(text).compile().unwrap(),
307         }
308     }
309 
310     #[derive(Serialize)]
311     struct NestedContext {
312         value: usize,
313     }
314 
315     #[derive(Serialize)]
316     struct TestContext {
317         number: usize,
318         string: &'static str,
319         boolean: bool,
320         null: Option<usize>,
321         array: Vec<usize>,
322         nested: NestedContext,
323         escapes: &'static str,
324     }
325 
context() -> Value326     fn context() -> Value {
327         let ctx = TestContext {
328             number: 5,
329             string: "test",
330             boolean: true,
331             null: None,
332             array: vec![1, 2, 3],
333             nested: NestedContext { value: 10 },
334             escapes: "1:< 2:> 3:& 4:' 5:\"",
335         };
336         ::serde_json::to_value(&ctx).unwrap()
337     }
338 
other_templates() -> HashMap<&'static str, Template<'static>>339     fn other_templates() -> HashMap<&'static str, Template<'static>> {
340         let mut map = HashMap::new();
341         map.insert("my_macro", compile("{value}"));
342         map
343     }
344 
format(value: &Value, output: &mut String) -> Result<()>345     fn format(value: &Value, output: &mut String) -> Result<()> {
346         output.push_str("{");
347         ::format(value, output)?;
348         output.push_str("}");
349         Ok(())
350     }
351 
formatters() -> HashMap<&'static str, Box<ValueFormatter>>352     fn formatters() -> HashMap<&'static str, Box<ValueFormatter>> {
353         let mut map = HashMap::<&'static str, Box<ValueFormatter>>::new();
354         map.insert("my_formatter", Box::new(format));
355         map
356     }
357 
358     #[test]
test_literal()359     fn test_literal() {
360         let template = compile("Hello!");
361         let context = context();
362         let template_registry = other_templates();
363         let formatter_registry = formatters();
364         let string = template
365             .render(&context, &template_registry, &formatter_registry)
366             .unwrap();
367         assert_eq!("Hello!", &string);
368     }
369 
370     #[test]
test_value()371     fn test_value() {
372         let template = compile("{ number }");
373         let context = context();
374         let template_registry = other_templates();
375         let formatter_registry = formatters();
376         let string = template
377             .render(&context, &template_registry, &formatter_registry)
378             .unwrap();
379         assert_eq!("5", &string);
380     }
381 
382     #[test]
test_path()383     fn test_path() {
384         let template = compile("The number of the day is { nested.value }.");
385         let context = context();
386         let template_registry = other_templates();
387         let formatter_registry = formatters();
388         let string = template
389             .render(&context, &template_registry, &formatter_registry)
390             .unwrap();
391         assert_eq!("The number of the day is 10.", &string);
392     }
393 
394     #[test]
test_if_taken()395     fn test_if_taken() {
396         let template = compile("{{ if boolean }}Hello!{{ endif }}");
397         let context = context();
398         let template_registry = other_templates();
399         let formatter_registry = formatters();
400         let string = template
401             .render(&context, &template_registry, &formatter_registry)
402             .unwrap();
403         assert_eq!("Hello!", &string);
404     }
405 
406     #[test]
test_if_untaken()407     fn test_if_untaken() {
408         let template = compile("{{ if null }}Hello!{{ endif }}");
409         let context = context();
410         let template_registry = other_templates();
411         let formatter_registry = formatters();
412         let string = template
413             .render(&context, &template_registry, &formatter_registry)
414             .unwrap();
415         assert_eq!("", &string);
416     }
417 
418     #[test]
test_if_else_taken()419     fn test_if_else_taken() {
420         let template = compile("{{ if boolean }}Hello!{{ else }}Goodbye!{{ endif }}");
421         let context = context();
422         let template_registry = other_templates();
423         let formatter_registry = formatters();
424         let string = template
425             .render(&context, &template_registry, &formatter_registry)
426             .unwrap();
427         assert_eq!("Hello!", &string);
428     }
429 
430     #[test]
test_if_else_untaken()431     fn test_if_else_untaken() {
432         let template = compile("{{ if null }}Hello!{{ else }}Goodbye!{{ endif }}");
433         let context = context();
434         let template_registry = other_templates();
435         let formatter_registry = formatters();
436         let string = template
437             .render(&context, &template_registry, &formatter_registry)
438             .unwrap();
439         assert_eq!("Goodbye!", &string);
440     }
441 
442     #[test]
test_ifnot_taken()443     fn test_ifnot_taken() {
444         let template = compile("{{ if not boolean }}Hello!{{ endif }}");
445         let context = context();
446         let template_registry = other_templates();
447         let formatter_registry = formatters();
448         let string = template
449             .render(&context, &template_registry, &formatter_registry)
450             .unwrap();
451         assert_eq!("", &string);
452     }
453 
454     #[test]
test_ifnot_untaken()455     fn test_ifnot_untaken() {
456         let template = compile("{{ if not null }}Hello!{{ endif }}");
457         let context = context();
458         let template_registry = other_templates();
459         let formatter_registry = formatters();
460         let string = template
461             .render(&context, &template_registry, &formatter_registry)
462             .unwrap();
463         assert_eq!("Hello!", &string);
464     }
465 
466     #[test]
test_ifnot_else_taken()467     fn test_ifnot_else_taken() {
468         let template = compile("{{ if not boolean }}Hello!{{ else }}Goodbye!{{ endif }}");
469         let context = context();
470         let template_registry = other_templates();
471         let formatter_registry = formatters();
472         let string = template
473             .render(&context, &template_registry, &formatter_registry)
474             .unwrap();
475         assert_eq!("Goodbye!", &string);
476     }
477 
478     #[test]
test_ifnot_else_untaken()479     fn test_ifnot_else_untaken() {
480         let template = compile("{{ if not null }}Hello!{{ else }}Goodbye!{{ endif }}");
481         let context = context();
482         let template_registry = other_templates();
483         let formatter_registry = formatters();
484         let string = template
485             .render(&context, &template_registry, &formatter_registry)
486             .unwrap();
487         assert_eq!("Hello!", &string);
488     }
489 
490     #[test]
test_nested_ifs()491     fn test_nested_ifs() {
492         let template = compile(
493             "{{ if boolean }}Hi, {{ if null }}there!{{ else }}Hello!{{ endif }}{{ endif }}",
494         );
495         let context = context();
496         let template_registry = other_templates();
497         let formatter_registry = formatters();
498         let string = template
499             .render(&context, &template_registry, &formatter_registry)
500             .unwrap();
501         assert_eq!("Hi, Hello!", &string);
502     }
503 
504     #[test]
test_with()505     fn test_with() {
506         let template = compile("{{ with nested as n }}{ n.value } { number }{{endwith}}");
507         let context = context();
508         let template_registry = other_templates();
509         let formatter_registry = formatters();
510         let string = template
511             .render(&context, &template_registry, &formatter_registry)
512             .unwrap();
513         assert_eq!("10 5", &string);
514     }
515 
516     #[test]
test_for_loop()517     fn test_for_loop() {
518         let template = compile("{{ for a in array }}{ a }{{ endfor }}");
519         let context = context();
520         let template_registry = other_templates();
521         let formatter_registry = formatters();
522         let string = template
523             .render(&context, &template_registry, &formatter_registry)
524             .unwrap();
525         assert_eq!("123", &string);
526     }
527 
528     #[test]
test_for_loop_index()529     fn test_for_loop_index() {
530         let template = compile("{{ for a in array }}{ @index }{{ endfor }}");
531         let context = context();
532         let template_registry = other_templates();
533         let formatter_registry = formatters();
534         let string = template
535             .render(&context, &template_registry, &formatter_registry)
536             .unwrap();
537         assert_eq!("012", &string);
538     }
539 
540     #[test]
test_for_loop_first()541     fn test_for_loop_first() {
542         let template =
543             compile("{{ for a in array }}{{if @first }}{ @index }{{ endif }}{{ endfor }}");
544         let context = context();
545         let template_registry = other_templates();
546         let formatter_registry = formatters();
547         let string = template
548             .render(&context, &template_registry, &formatter_registry)
549             .unwrap();
550         assert_eq!("0", &string);
551     }
552 
553     #[test]
test_for_loop_last()554     fn test_for_loop_last() {
555         let template =
556             compile("{{ for a in array }}{{ if @last}}{ @index }{{ endif }}{{ endfor }}");
557         let context = context();
558         let template_registry = other_templates();
559         let formatter_registry = formatters();
560         let string = template
561             .render(&context, &template_registry, &formatter_registry)
562             .unwrap();
563         assert_eq!("2", &string);
564     }
565 
566     #[test]
test_whitespace_stripping_value()567     fn test_whitespace_stripping_value() {
568         let template = compile("1  \n\t   {- number -}  \n   1");
569         let context = context();
570         let template_registry = other_templates();
571         let formatter_registry = formatters();
572         let string = template
573             .render(&context, &template_registry, &formatter_registry)
574             .unwrap();
575         assert_eq!("151", &string);
576     }
577 
578     #[test]
test_call()579     fn test_call() {
580         let template = compile("{{ call my_macro with nested }}");
581         let context = context();
582         let template_registry = other_templates();
583         let formatter_registry = formatters();
584         let string = template
585             .render(&context, &template_registry, &formatter_registry)
586             .unwrap();
587         assert_eq!("10", &string);
588     }
589 
590     #[test]
test_formatter()591     fn test_formatter() {
592         let template = compile("{ nested.value | my_formatter }");
593         let context = context();
594         let template_registry = other_templates();
595         let formatter_registry = formatters();
596         let string = template
597             .render(&context, &template_registry, &formatter_registry)
598             .unwrap();
599         assert_eq!("{10}", &string);
600     }
601 
602     #[test]
test_unknown()603     fn test_unknown() {
604         let template = compile("{ foobar }");
605         let context = context();
606         let template_registry = other_templates();
607         let formatter_registry = formatters();
608         template
609             .render(&context, &template_registry, &formatter_registry)
610             .unwrap_err();
611     }
612 
613     #[test]
test_escaping()614     fn test_escaping() {
615         let template = compile("{ escapes }");
616         let context = context();
617         let template_registry = other_templates();
618         let formatter_registry = formatters();
619         let string = template
620             .render(&context, &template_registry, &formatter_registry)
621             .unwrap();
622         assert_eq!("1:&lt; 2:&gt; 3:&amp; 4:&#39; 5:&quot;", &string);
623     }
624 
625     #[test]
test_unescaped()626     fn test_unescaped() {
627         let template = compile("{ escapes | unescaped }");
628         let context = context();
629         let template_registry = other_templates();
630         let mut formatter_registry = formatters();
631         formatter_registry.insert("unescaped", Box::new(::format_unescaped));
632         let string = template
633             .render(&context, &template_registry, &formatter_registry)
634             .unwrap();
635         assert_eq!("1:< 2:> 3:& 4:' 5:\"", &string);
636     }
637 }
638