1use compiler::TemplateCompiler;
4use error::Error::*;
5use error::*;
6use instruction::{Instruction, PathSlice};
7use serde_json::Value;
8use std::collections::HashMap;
9use std::fmt::Write;
10use std::slice;
11use ValueFormatter;
12
13enum ContextElement<'render, 'template> {
15 Object(&'render Value),
18 Named(&'template str, &'render Value),
21 Iteration(
25 &'template str,
26 &'render Value,
27 usize,
28 usize,
29 slice::Iter<'render, Value>,
30 ),
31}
32
33struct RenderContext<'render, 'template> {
36 original_text: &'template str,
37 context_stack: Vec<ContextElement<'render, 'template>>,
38}
39impl<'render, 'template> RenderContext<'render, 'template> {
40 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 fn lookup_in(&self, path: PathSlice, object: &'render Value) -> Result<&'render Value> {
64 let mut current = object;
65 for step in path.iter() {
66 match current.get(step) {
67 Some(next) => current = next,
68 None => return Err(lookup_error(self.original_text, step, path, current)),
69 }
70 }
71 Ok(current)
72 }
73
74 fn lookup_index(&self) -> Result<(usize, usize)> {
76 for stack_layer in self.context_stack.iter().rev() {
77 match stack_layer {
78 ContextElement::Iteration(_, _, index, length, _) => return Ok((*index, *length)),
79 _ => continue,
80 }
81 }
82 Err(GenericError {
83 msg: "Used @index outside of a foreach block.".to_string(),
84 })
85 }
86
87 fn lookup_root(&self) -> Result<&'render Value> {
89 match self.context_stack.get(0) {
90 Some(ContextElement::Object(obj)) => Ok(obj),
91 Some(_) => {
92 panic!("Expected Object value at root of context stack, but was something else.")
93 }
94 None => panic!(
95 "Attempted to do a lookup with an empty context stack. That shouldn't be possible."
96 ),
97 }
98 }
99}
100
101pub(crate) struct Template<'template> {
105 original_text: &'template str,
106 instructions: Vec<Instruction<'template>>,
107 template_len: usize,
108}
109impl<'template> Template<'template> {
110 pub fn compile(text: &'template str) -> Result<Template> {
112 Ok(Template {
113 original_text: text,
114 template_len: text.len(),
115 instructions: TemplateCompiler::new(text).compile()?,
116 })
117 }
118
119 pub fn render(
121 &self,
122 context: &Value,
123 template_registry: &HashMap<&str, Template>,
124 formatter_registry: &HashMap<&str, Box<ValueFormatter>>,
125 default_formatter: &ValueFormatter,
126 ) -> Result<String> {
127 let mut output = String::with_capacity(self.template_len);
130 self.render_into(
131 context,
132 template_registry,
133 formatter_registry,
134 default_formatter,
135 &mut output,
136 )?;
137 Ok(output)
138 }
139
140 pub fn render_into(
142 &self,
143 context: &Value,
144 template_registry: &HashMap<&str, Template>,
145 formatter_registry: &HashMap<&str, Box<ValueFormatter>>,
146 default_formatter: &ValueFormatter,
147 output: &mut String,
148 ) -> Result<()> {
149 let mut program_counter = 0;
150 let mut render_context = RenderContext {
151 original_text: self.original_text,
152 context_stack: vec![ContextElement::Object(context)],
153 };
154
155 while program_counter < self.instructions.len() {
156 match &self.instructions[program_counter] {
157 Instruction::Literal(text) => {
158 output.push_str(text);
159 program_counter += 1;
160 }
161 Instruction::Value(path) => {
162 let first = *path.first().unwrap();
163 if first.starts_with('@') {
164 match first {
168 "@index" => {
169 write!(output, "{}", render_context.lookup_index()?.0).unwrap()
170 }
171 "@first" => {
172 write!(output, "{}", render_context.lookup_index()?.0 == 0).unwrap()
173 }
174 "@last" => {
175 let (index, length) = render_context.lookup_index()?;
176 write!(output, "{}", index == length - 1).unwrap()
177 }
178 "@root" => {
179 let value_to_render = render_context.lookup_root()?;
180 default_formatter(value_to_render, output)?;
181 }
182 _ => panic!(), }
184 } else {
185 let value_to_render = render_context.lookup(path)?;
186 default_formatter(value_to_render, output)?;
187 }
188 program_counter += 1;
189 }
190 Instruction::FormattedValue(path, name) => {
191 let value_to_render = render_context.lookup(path)?;
193 match formatter_registry.get(name) {
194 Some(formatter) => {
195 let formatter_result = formatter(value_to_render, output);
196 if let Err(err) = formatter_result {
197 return Err(called_formatter_error(self.original_text, name, err));
198 }
199 }
200 None => return Err(unknown_formatter(self.original_text, name)),
201 }
202 program_counter += 1;
203 }
204 Instruction::Branch(path, negate, target) => {
205 let first = *path.first().unwrap();
206 let mut truthy = if first.starts_with('@') {
207 match first {
208 "@index" => render_context.lookup_index()?.0 != 0,
209 "@first" => render_context.lookup_index()?.0 == 0,
210 "@last" => {
211 let (index, length) = render_context.lookup_index()?;
212 index == (length - 1)
213 }
214 "@root" => self.value_is_truthy(render_context.lookup_root()?, path)?,
215 other => panic!("Unknown keyword {}", other), }
217 } else {
218 let value_to_render = render_context.lookup(path)?;
219 self.value_is_truthy(value_to_render, path)?
220 };
221 if *negate {
222 truthy = !truthy;
223 }
224
225 if truthy {
226 program_counter = *target;
227 } else {
228 program_counter += 1;
229 }
230 }
231 Instruction::PushNamedContext(path, name) => {
232 let context_value = render_context.lookup(path)?;
233 render_context
234 .context_stack
235 .push(ContextElement::Named(name, context_value));
236 program_counter += 1;
237 }
238 Instruction::PushIterationContext(path, name) => {
239 let first = *path.first().unwrap();
242 let context_value = match first {
243 "@root" => render_context.lookup_root()?,
244 other if other.starts_with('@') => {
245 return Err(not_iterable_error(self.original_text, path))
246 }
247 _ => render_context.lookup(path)?,
248 };
249 match context_value {
250 Value::Array(ref arr) => {
251 render_context.context_stack.push(ContextElement::Iteration(
252 name,
253 &Value::Null,
254 ::std::usize::MAX,
255 arr.len(),
256 arr.iter(),
257 ))
258 }
259 _ => return Err(not_iterable_error(self.original_text, path)),
260 };
261 program_counter += 1;
262 }
263 Instruction::PopContext => {
264 render_context.context_stack.pop();
265 program_counter += 1;
266 }
267 Instruction::Goto(target) => {
268 program_counter = *target;
269 }
270 Instruction::Iterate(target) => {
271 match render_context.context_stack.last_mut() {
272 Some(ContextElement::Iteration(_, val, index, _, iter)) => {
273 match iter.next() {
274 Some(new_val) => {
275 *val = new_val;
276 *index = index.wrapping_add(1);
279 program_counter += 1;
280 }
281 None => {
282 program_counter = *target;
283 }
284 }
285 }
286 _ => panic!("Malformed program."),
287 };
288 }
289 Instruction::Call(template_name, path) => {
290 let context_value = render_context.lookup(path)?;
291 match template_registry.get(template_name) {
292 Some(templ) => {
293 let called_templ_result = templ.render_into(
294 context_value,
295 template_registry,
296 formatter_registry,
297 default_formatter,
298 output,
299 );
300 if let Err(err) = called_templ_result {
301 return Err(called_template_error(
302 self.original_text,
303 template_name,
304 err,
305 ));
306 }
307 }
308 None => return Err(unknown_template(self.original_text, template_name)),
309 }
310 program_counter += 1;
311 }
312 }
313 }
314 Ok(())
315 }
316
317 fn value_is_truthy(&self, value: &Value, path: &[&str]) -> Result<bool> {
318 let truthy = match value {
319 Value::Null => false,
320 Value::Bool(b) => *b,
321 Value::Number(n) => match n.as_f64() {
322 Some(float) => float == 0.0,
323 None => {
324 return Err(truthiness_error(self.original_text, path));
325 }
326 },
327 Value::String(s) => !s.is_empty(),
328 Value::Array(arr) => !arr.is_empty(),
329 Value::Object(_) => true,
330 };
331 Ok(truthy)
332 }
333}
334
335#[cfg(test)]
336mod test {
337 use super::*;
338 use compiler::TemplateCompiler;
339
340 fn compile(text: &'static str) -> Template<'static> {
341 Template {
342 original_text: text,
343 template_len: text.len(),
344 instructions: TemplateCompiler::new(text).compile().unwrap(),
345 }
346 }
347
348 #[derive(Serialize)]
349 struct NestedContext {
350 value: usize,
351 }
352
353 #[derive(Serialize)]
354 struct TestContext {
355 number: usize,
356 string: &'static str,
357 boolean: bool,
358 null: Option<usize>,
359 array: Vec<usize>,
360 nested: NestedContext,
361 escapes: &'static str,
362 }
363
364 fn context() -> Value {
365 let ctx = TestContext {
366 number: 5,
367 string: "test",
368 boolean: true,
369 null: None,
370 array: vec![1, 2, 3],
371 nested: NestedContext { value: 10 },
372 escapes: "1:< 2:> 3:& 4:' 5:\"",
373 };
374 ::serde_json::to_value(&ctx).unwrap()
375 }
376
377 fn other_templates() -> HashMap<&'static str, Template<'static>> {
378 let mut map = HashMap::new();
379 map.insert("my_macro", compile("{value}"));
380 map
381 }
382
383 fn format(value: &Value, output: &mut String) -> Result<()> {
384 output.push_str("{");
385 ::format(value, output)?;
386 output.push_str("}");
387 Ok(())
388 }
389
390 fn formatters() -> HashMap<&'static str, Box<ValueFormatter>> {
391 let mut map = HashMap::<&'static str, Box<ValueFormatter>>::new();
392 map.insert("my_formatter", Box::new(format));
393 map
394 }
395
396 pub fn default_formatter() -> &'static ValueFormatter {
397 &::format
398 }
399
400 #[test]
401 fn test_literal() {
402 let template = compile("Hello!");
403 let context = context();
404 let template_registry = other_templates();
405 let formatter_registry = formatters();
406 let string = template
407 .render(
408 &context,
409 &template_registry,
410 &formatter_registry,
411 &default_formatter(),
412 )
413 .unwrap();
414 assert_eq!("Hello!", &string);
415 }
416
417 #[test]
418 fn test_value() {
419 let template = compile("{ number }");
420 let context = context();
421 let template_registry = other_templates();
422 let formatter_registry = formatters();
423 let string = template
424 .render(
425 &context,
426 &template_registry,
427 &formatter_registry,
428 &default_formatter(),
429 )
430 .unwrap();
431 assert_eq!("5", &string);
432 }
433
434 #[test]
435 fn test_path() {
436 let template = compile("The number of the day is { nested.value }.");
437 let context = context();
438 let template_registry = other_templates();
439 let formatter_registry = formatters();
440 let string = template
441 .render(
442 &context,
443 &template_registry,
444 &formatter_registry,
445 &default_formatter(),
446 )
447 .unwrap();
448 assert_eq!("The number of the day is 10.", &string);
449 }
450
451 #[test]
452 fn test_if_taken() {
453 let template = compile("{{ if boolean }}Hello!{{ endif }}");
454 let context = context();
455 let template_registry = other_templates();
456 let formatter_registry = formatters();
457 let string = template
458 .render(
459 &context,
460 &template_registry,
461 &formatter_registry,
462 &default_formatter(),
463 )
464 .unwrap();
465 assert_eq!("Hello!", &string);
466 }
467
468 #[test]
469 fn test_if_untaken() {
470 let template = compile("{{ if null }}Hello!{{ endif }}");
471 let context = context();
472 let template_registry = other_templates();
473 let formatter_registry = formatters();
474 let string = template
475 .render(
476 &context,
477 &template_registry,
478 &formatter_registry,
479 &default_formatter(),
480 )
481 .unwrap();
482 assert_eq!("", &string);
483 }
484
485 #[test]
486 fn test_if_else_taken() {
487 let template = compile("{{ if boolean }}Hello!{{ else }}Goodbye!{{ endif }}");
488 let context = context();
489 let template_registry = other_templates();
490 let formatter_registry = formatters();
491 let string = template
492 .render(
493 &context,
494 &template_registry,
495 &formatter_registry,
496 &default_formatter(),
497 )
498 .unwrap();
499 assert_eq!("Hello!", &string);
500 }
501
502 #[test]
503 fn test_if_else_untaken() {
504 let template = compile("{{ if null }}Hello!{{ else }}Goodbye!{{ endif }}");
505 let context = context();
506 let template_registry = other_templates();
507 let formatter_registry = formatters();
508 let string = template
509 .render(
510 &context,
511 &template_registry,
512 &formatter_registry,
513 &default_formatter(),
514 )
515 .unwrap();
516 assert_eq!("Goodbye!", &string);
517 }
518
519 #[test]
520 fn test_ifnot_taken() {
521 let template = compile("{{ if not boolean }}Hello!{{ endif }}");
522 let context = context();
523 let template_registry = other_templates();
524 let formatter_registry = formatters();
525 let string = template
526 .render(
527 &context,
528 &template_registry,
529 &formatter_registry,
530 &default_formatter(),
531 )
532 .unwrap();
533 assert_eq!("", &string);
534 }
535
536 #[test]
537 fn test_ifnot_untaken() {
538 let template = compile("{{ if not null }}Hello!{{ endif }}");
539 let context = context();
540 let template_registry = other_templates();
541 let formatter_registry = formatters();
542 let string = template
543 .render(
544 &context,
545 &template_registry,
546 &formatter_registry,
547 &default_formatter(),
548 )
549 .unwrap();
550 assert_eq!("Hello!", &string);
551 }
552
553 #[test]
554 fn test_ifnot_else_taken() {
555 let template = compile("{{ if not boolean }}Hello!{{ else }}Goodbye!{{ endif }}");
556 let context = context();
557 let template_registry = other_templates();
558 let formatter_registry = formatters();
559 let string = template
560 .render(
561 &context,
562 &template_registry,
563 &formatter_registry,
564 &default_formatter(),
565 )
566 .unwrap();
567 assert_eq!("Goodbye!", &string);
568 }
569
570 #[test]
571 fn test_ifnot_else_untaken() {
572 let template = compile("{{ if not null }}Hello!{{ else }}Goodbye!{{ endif }}");
573 let context = context();
574 let template_registry = other_templates();
575 let formatter_registry = formatters();
576 let string = template
577 .render(
578 &context,
579 &template_registry,
580 &formatter_registry,
581 &default_formatter(),
582 )
583 .unwrap();
584 assert_eq!("Hello!", &string);
585 }
586
587 #[test]
588 fn test_nested_ifs() {
589 let template = compile(
590 "{{ if boolean }}Hi, {{ if null }}there!{{ else }}Hello!{{ endif }}{{ endif }}",
591 );
592 let context = context();
593 let template_registry = other_templates();
594 let formatter_registry = formatters();
595 let string = template
596 .render(
597 &context,
598 &template_registry,
599 &formatter_registry,
600 &default_formatter(),
601 )
602 .unwrap();
603 assert_eq!("Hi, Hello!", &string);
604 }
605
606 #[test]
607 fn test_with() {
608 let template = compile("{{ with nested as n }}{ n.value } { number }{{endwith}}");
609 let context = context();
610 let template_registry = other_templates();
611 let formatter_registry = formatters();
612 let string = template
613 .render(
614 &context,
615 &template_registry,
616 &formatter_registry,
617 &default_formatter(),
618 )
619 .unwrap();
620 assert_eq!("10 5", &string);
621 }
622
623 #[test]
624 fn test_for_loop() {
625 let template = compile("{{ for a in array }}{ a }{{ endfor }}");
626 let context = context();
627 let template_registry = other_templates();
628 let formatter_registry = formatters();
629 let string = template
630 .render(
631 &context,
632 &template_registry,
633 &formatter_registry,
634 &default_formatter(),
635 )
636 .unwrap();
637 assert_eq!("123", &string);
638 }
639
640 #[test]
641 fn test_for_loop_index() {
642 let template = compile("{{ for a in array }}{ @index }{{ endfor }}");
643 let context = context();
644 let template_registry = other_templates();
645 let formatter_registry = formatters();
646 let string = template
647 .render(
648 &context,
649 &template_registry,
650 &formatter_registry,
651 &default_formatter(),
652 )
653 .unwrap();
654 assert_eq!("012", &string);
655 }
656
657 #[test]
658 fn test_for_loop_first() {
659 let template =
660 compile("{{ for a in array }}{{if @first }}{ @index }{{ endif }}{{ endfor }}");
661 let context = context();
662 let template_registry = other_templates();
663 let formatter_registry = formatters();
664 let string = template
665 .render(
666 &context,
667 &template_registry,
668 &formatter_registry,
669 &default_formatter(),
670 )
671 .unwrap();
672 assert_eq!("0", &string);
673 }
674
675 #[test]
676 fn test_for_loop_last() {
677 let template =
678 compile("{{ for a in array }}{{ if @last}}{ @index }{{ endif }}{{ endfor }}");
679 let context = context();
680 let template_registry = other_templates();
681 let formatter_registry = formatters();
682 let string = template
683 .render(
684 &context,
685 &template_registry,
686 &formatter_registry,
687 &default_formatter(),
688 )
689 .unwrap();
690 assert_eq!("2", &string);
691 }
692
693 #[test]
694 fn test_whitespace_stripping_value() {
695 let template = compile("1 \n\t {- number -} \n 1");
696 let context = context();
697 let template_registry = other_templates();
698 let formatter_registry = formatters();
699 let string = template
700 .render(
701 &context,
702 &template_registry,
703 &formatter_registry,
704 &default_formatter(),
705 )
706 .unwrap();
707 assert_eq!("151", &string);
708 }
709
710 #[test]
711 fn test_call() {
712 let template = compile("{{ call my_macro with nested }}");
713 let context = context();
714 let template_registry = other_templates();
715 let formatter_registry = formatters();
716 let string = template
717 .render(
718 &context,
719 &template_registry,
720 &formatter_registry,
721 &default_formatter(),
722 )
723 .unwrap();
724 assert_eq!("10", &string);
725 }
726
727 #[test]
728 fn test_formatter() {
729 let template = compile("{ nested.value | my_formatter }");
730 let context = context();
731 let template_registry = other_templates();
732 let formatter_registry = formatters();
733 let string = template
734 .render(
735 &context,
736 &template_registry,
737 &formatter_registry,
738 &default_formatter(),
739 )
740 .unwrap();
741 assert_eq!("{10}", &string);
742 }
743
744 #[test]
745 fn test_unknown() {
746 let template = compile("{ foobar }");
747 let context = context();
748 let template_registry = other_templates();
749 let formatter_registry = formatters();
750 template
751 .render(
752 &context,
753 &template_registry,
754 &formatter_registry,
755 &default_formatter(),
756 )
757 .unwrap_err();
758 }
759
760 #[test]
761 fn test_escaping() {
762 let template = compile("{ escapes }");
763 let context = context();
764 let template_registry = other_templates();
765 let formatter_registry = formatters();
766 let string = template
767 .render(
768 &context,
769 &template_registry,
770 &formatter_registry,
771 &default_formatter(),
772 )
773 .unwrap();
774 assert_eq!("1:< 2:> 3:& 4:' 5:"", &string);
775 }
776
777 #[test]
778 fn test_unescaped() {
779 let template = compile("{ escapes | unescaped }");
780 let context = context();
781 let template_registry = other_templates();
782 let mut formatter_registry = formatters();
783 formatter_registry.insert("unescaped", Box::new(::format_unescaped));
784 let string = template
785 .render(
786 &context,
787 &template_registry,
788 &formatter_registry,
789 &default_formatter(),
790 )
791 .unwrap();
792 assert_eq!("1:< 2:> 3:& 4:' 5:\"", &string);
793 }
794
795 #[test]
796 fn test_root_print() {
797 let template = compile("{ @root }");
798 let context = "Hello World!";
799 let context = ::serde_json::to_value(&context).unwrap();
800 let template_registry = other_templates();
801 let formatter_registry = formatters();
802 let string = template
803 .render(
804 &context,
805 &template_registry,
806 &formatter_registry,
807 &default_formatter(),
808 )
809 .unwrap();
810 assert_eq!("Hello World!", &string);
811 }
812
813 #[test]
814 fn test_root_branch() {
815 let template = compile("{{ if @root }}Hello World!{{ endif }}");
816 let context = true;
817 let context = ::serde_json::to_value(&context).unwrap();
818 let template_registry = other_templates();
819 let formatter_registry = formatters();
820 let string = template
821 .render(
822 &context,
823 &template_registry,
824 &formatter_registry,
825 &default_formatter(),
826 )
827 .unwrap();
828 assert_eq!("Hello World!", &string);
829 }
830
831 #[test]
832 fn test_root_iterate() {
833 let template = compile("{{ for a in @root }}{ a }{{ endfor }}");
834 let context = vec!["foo", "bar"];
835 let context = ::serde_json::to_value(&context).unwrap();
836 let template_registry = other_templates();
837 let formatter_registry = formatters();
838 let string = template
839 .render(
840 &context,
841 &template_registry,
842 &formatter_registry,
843 &default_formatter(),
844 )
845 .unwrap();
846 assert_eq!("foobar", &string);
847 }
848}