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