1pub mod filter;
2pub mod pipe;
3
4mod compare;
5mod store;
6
7pub use store::Store;
8
9use std::{
10 borrow::Cow,
11 collections::HashMap,
12 fmt::{Display, Write},
13 mem::take,
14};
15
16use crate::{
17 compile::{tree::*, Scope, Template},
18 engine::INVALID_FILTER,
19 log::Error,
20 region::Region,
21 Engine,
22};
23
24use self::{
25 compare::{compare_values, is_truthy},
26 pipe::Pipe,
27 store::Shadow,
28};
29
30use serde::Serialize;
31use serde_json::Value;
32
33const INCOMPATIBLE_TYPES: &str = "incompatible types";
34
35pub struct Renderer<'source, 'store> {
37 engine: &'source Engine,
39 template: &'source Template,
41 shadow: Shadow<'store>,
43 blocks: BlockMap<'source>,
45}
46
47impl<'source, 'store> Renderer<'source, 'store> {
48 pub fn new(engine: &'source Engine, template: &'source Template, store: &'store Store) -> Self {
50 Renderer {
51 engine,
52 template,
53 shadow: Shadow::new(store),
54 blocks: HashMap::new(),
55 }
56 }
57
58 pub fn render(mut self, pipe: &mut Pipe) -> Result<(), Error> {
65 match &self.template.get_extends() {
66 Some(extended) => self.evaluate_scope(extended, pipe),
67 None => self.render_scope(self.template.get_scope(), pipe),
68 }
69 .map_err(|error| {
70 if !error.get_name().is_some() && self.template.get_name().is_some() {
73 return error.with_name(self.template.get_name().unwrap());
74 }
75
76 error
77 })?;
78
79 Ok(())
80 }
81
82 fn evaluate_scope(&mut self, extends: &Extends, pipe: &mut Pipe) -> Result<(), Error> {
92 let name = extends
93 .name
94 .get_region()
95 .literal(self.template.get_source());
96 let template = self
97 .engine
98 .get_template(name)
99 .ok_or_else(|| error_missing_template(name))?;
100 self.collect_blocks(self.template.get_scope());
101
102 Renderer::new(self.engine, &template, self.shadow.store)
103 .with_blocks(take(&mut self.blocks))
104 .render(pipe)
105 }
106
107 fn render_scope(&mut self, scope: &'source Scope, pipe: &mut Pipe) -> Result<(), Error> {
113 let mut iterator = scope.data.iter();
114 while let Some(next) = iterator.next() {
115 match next {
116 Tree::Raw(ra) => {
117 let value = self.evaluate_raw(ra);
118 pipe.write_str(value).map_err(|_| error_write())?
119 }
120 Tree::Output(ou) => {
121 let value = self.evaluate_expression(&ou.expression)?;
122 pipe.write_value(&value).map_err(|_| error_write())?
123 }
124 Tree::If(i) => {
125 self.render_if(i, pipe)?;
126 }
127 Tree::For(fo) => {
128 self.render_for(fo, pipe)?;
129 }
130 Tree::Let(le) => {
131 self.evaluate_let(le)?;
132 }
133 Tree::Include(inc) => {
134 self.render_include(inc, pipe)?;
135 }
136 Tree::Block(bl) => {
137 self.render_block(bl, pipe)?;
138 }
139 _ => unreachable!("parser must catch invalid top level tree"),
140 }
141 }
142
143 Ok(())
144 }
145
146 fn render_block(&mut self, block: &'source Block, pipe: &mut Pipe) -> Result<(), Error> {
154 let name = block.name.get_region().literal(self.template.get_source());
155
156 match self.blocks.get(name) {
157 Some(shadowed) => Renderer::new(self.engine, shadowed.template, self.shadow.store)
158 .render_scope(&shadowed.block.scope, pipe),
159 None => self.render_scope(&block.scope, pipe),
160 }
161 }
162
163 fn render_include(&mut self, include: &Include, pipe: &mut Pipe) -> Result<(), Error> {
170 let name = include
171 .name
172 .get_region()
173 .literal(self.template.get_source());
174 let template = self
175 .engine
176 .get_template(name)
177 .ok_or_else(|| error_missing_template(name))?;
178
179 if include.mount.is_some() {
180 let mut scoped_store = Store::new();
182
183 for point in include.mount.as_ref().unwrap().values.iter() {
184 let name = point.name.literal(self.template.get_source());
185 let value = self.evaluate_base(&point.value)?;
186 scoped_store.insert_must(name, value);
187 }
188 Renderer::new(self.engine, template, &scoped_store).render(pipe)?
189 } else {
190 Renderer::new(self.engine, template, self.shadow.store).render(pipe)?
192 };
193
194 Ok(())
195 }
196
197 fn render_if(&mut self, i: &'source If, pipe: &mut Pipe) -> Result<(), Error> {
204 for branch in i.tree.branches.iter() {
205 if !self.evaluate_branch(branch)? {
206 if i.else_branch.is_some() {
207 self.render_scope(i.else_branch.as_ref().unwrap(), pipe)?;
208 }
209 return Ok(());
210 }
211 }
212
213 self.render_scope(&i.then_branch, pipe)
214 }
215
216 fn render_for(&mut self, fo: &'source For, pipe: &mut Pipe) -> Result<(), Error> {
223 self.shadow.push();
224
225 let value = self.evaluate_base(&fo.base)?;
226 match value.as_ref() {
227 Value::String(st) => {
228 for (index, char) in st.to_owned().char_indices() {
229 self.shadow_set(&fo.set, (Some(index), char))?;
230 self.render_scope(&fo.scope, pipe)?;
231 }
232 }
233 Value::Array(ar) => {
234 for (index, value) in ar.to_owned().iter().enumerate() {
235 self.shadow_set(&fo.set, (Some(index), value))?;
236 self.render_scope(&fo.scope, pipe)?;
237 }
238 }
239 Value::Object(ob) => {
240 for (key, value) in ob.to_owned().iter() {
241 self.shadow_set(&fo.set, (Some(key), value))?;
242 self.render_scope(&fo.scope, pipe)?;
243 }
244 }
245 incompatible => {
246 return Err(Error::build(INCOMPATIBLE_TYPES).with_help(format!(
247 "iterating on value `{}` is not supported",
248 incompatible
249 )))
250 }
251 }
252 self.shadow.pop();
253
254 Ok(())
255 }
256
257 fn evaluate_branch(&self, branch: &IfBranch) -> Result<bool, Error> {
264 for leaf in branch {
265 let left = self.evaluate_base(&leaf.left)?;
266
267 match &leaf.right {
268 Some(base) => {
269 let right = self.evaluate_base(&base)?;
270 let operator = leaf
271 .operator
272 .expect("operator must exist when leaf.right exists");
273
274 if !compare_values(&left, operator, &right)
275 .map(|bool| if leaf.negate { !bool } else { bool })
276 .map_err(|error| {
277 error.with_pointer(
278 self.template.get_source(),
279 leaf.left.get_region().combine(base.get_region()),
280 )
281 })?
282 {
283 return Ok(false);
284 }
285 }
286 None => {
287 let result = match leaf.negate {
288 true => !is_truthy(&left),
289 false => is_truthy(&left),
290 };
291
292 if !result {
293 return Ok(false);
294 }
295 }
296 }
297 }
298
299 Ok(true)
300 }
301
302 fn evaluate_expression(&self, expression: &'source Expression) -> Result<Cow<Value>, Error> {
308 match &expression {
309 Expression::Base(base) => self.evaluate_base(base),
310 Expression::Call(call) => self.evaluate_call(call),
311 }
312 }
313
314 fn evaluate_base(&self, base: &'source Base) -> Result<Cow<Value>, Error> {
321 match base {
322 Base::Variable(variable) => self.evaluate_keys(&variable.path),
323 Base::Literal(literal) => Ok(Cow::Borrowed(&literal.value)),
324 }
325 }
326
327 fn evaluate_call(&self, call: &'store Call) -> Result<Cow<Value>, Error> {
340 let mut call_stack = vec![call];
341
342 let mut receiver: &Expression = &call.receiver;
343 while let Expression::Call(call) = receiver {
344 call_stack.push(call);
345 receiver = &call.receiver;
346 }
347 let mut value = match receiver {
348 Expression::Base(base) => self.evaluate_base(base)?,
349 _ => unreachable!(),
350 };
351
352 for call in call_stack.iter().rev() {
353 let name_literal = call.name.region.literal(self.template.get_source());
354 let func = self.engine.get_filter(name_literal);
355 if func.is_none() {
356 return Err(Error::build(INVALID_FILTER)
357 .with_pointer(self.template.get_source(), call.name.region)
358 .with_help(format!(
359 "template wants to use the `{name_literal}` filter, but a filter with that \
360 name was not found in this engine, did you add the filter to the engine with \
361 `.add_filter` or `.add_filter_must`?"
362 )));
363 }
364
365 let arguments = if call.arguments.is_some() {
366 self.evaluate_arguments(call.arguments.as_ref().unwrap())?
367 } else {
368 HashMap::new()
369 };
370
371 let returned = func.unwrap().apply(&value, &arguments).or_else(|error| {
372 Err(error.with_pointer(self.template.get_source(), call.name.region))
373 })?;
374
375 value = Cow::Owned(returned);
376 }
377
378 Ok(value)
379 }
380
381 fn evaluate_raw(&self, region: &Region) -> &str {
386 region.literal(self.template.get_source())
387 }
388
389 fn evaluate_keys(&self, keys: &Vec<Identifier>) -> Result<Cow<Value>, Error> {
396 let first_region = keys
397 .first()
398 .expect("key vector should always have at least one key")
399 .region;
400
401 let first_value = first_region.literal(self.template.get_source());
402 let store_value = self.shadow.get(first_value);
403
404 let mut value: Cow<Value> = if store_value.is_some() {
405 Cow::Borrowed(store_value.unwrap())
406 } else {
407 return Err(Error::build("missing store value")
408 .with_pointer(self.template.get_source(), first_region)
409 .with_help(format!(
410 "unable to find `{first_value}` in store, \
411 ensure it exists or try wrapping with an `if` block",
412 )));
413 };
414
415 for key in keys.iter().skip(1) {
416 match value.as_object() {
417 Some(object) => {
418 let key_region = key.region;
419 let key_name = key_region.literal(self.template.get_source());
420 let next_object = object.get(key_name);
421
422 value = if next_object.is_some() {
423 Cow::Owned(next_object.unwrap().clone())
424 } else {
425 Cow::Owned(Value::Null)
426 };
427 }
428 None => return Ok(Cow::Owned(Value::Null)),
429 }
430 }
431
432 Ok(value)
433 }
434
435 fn evaluate_arguments(&self, arguments: &Arguments) -> Result<HashMap<String, Value>, Error> {
444 let mut buffer = HashMap::new();
445 let mut unnamed = 1;
446
447 for arg in &arguments.values {
448 let name = if arg.name.is_some() {
449 arg.name
450 .unwrap()
451 .literal(self.template.get_source())
452 .to_string()
453 } else {
454 let temp = unnamed;
455 unnamed += 1;
456 temp.to_string()
457 };
458
459 let value = self.evaluate_base(&arg.value)?;
460 buffer.insert(name, value.into_owned());
461 }
462
463 Ok(buffer)
464 }
465
466 fn evaluate_let(&mut self, le: &Let) -> Result<(), Error> {
473 let value = self.evaluate_expression(&le.right)?;
474 self.shadow_set(
475 &Set::Single(le.left.clone()),
476 (None::<Value>, value.into_owned()),
477 )?;
478
479 Ok(())
480 }
481
482 fn with_blocks(mut self, blocks: BlockMap<'source>) -> Self {
486 self.blocks = blocks;
487
488 self
489 }
490
491 fn collect_blocks(&mut self, scope: &'source Scope) {
494 let mut iterator = scope.data.iter();
495
496 while let Some(next) = iterator.next() {
497 match next {
498 Tree::Block(block) => {
499 let name = block.name.get_region().literal(self.template.get_source());
500 self.blocks.insert(
501 name.to_string(),
502 Named {
503 template: self.template,
504 block: block.clone(),
505 },
506 );
507 }
508 _ => {}
509 }
510 }
511 }
512
513 fn shadow_set<N, T>(&mut self, set: &Set, data: (Option<N>, T)) -> Result<(), Error>
524 where
525 N: Serialize + Display,
526 T: Serialize + Display,
527 {
528 let source = self.template.get_source();
529 match set {
530 Set::Single(si) => {
531 let key = si.region.literal(&source);
532 self.shadow.insert_must(key, data.1)
533 }
534 Set::Pair(pa) => {
535 let key = pa.key.region.literal(&source);
536 let value = pa.value.region.literal(&source);
537 self.shadow.insert_must(key, data.0.unwrap());
538 self.shadow.insert_must(value, data.1);
539 }
540 }
541
542 Ok(())
543 }
544}
545
546fn error_missing_template(name: &str) -> Error {
548 Error::build("missing template").with_help(format!(
549 "template `{}` not found in engine, add it with `.add_template`",
550 name
551 ))
552}
553
554fn error_write() -> Error {
558 Error::build("write failure")
559 .with_help("failed to write result of render, are you low on memory?")
560}
561
562type BlockMap<'source> = HashMap<String, Named<'source>>;
563
564struct Named<'source> {
567 template: &'source Template,
569 block: Block,
571}
572
573#[cfg(test)]
574mod tests {
575 use std::collections::HashMap;
576
577 use crate::{
578 compile::tree::{Argument, Arguments, Base, Literal},
579 filter::Error,
580 Engine, Store, Template,
581 };
582
583 use super::Renderer;
584
585 use serde_json::{json, Value};
586
587 #[test]
588 fn test_render_raw() {
589 let (template, engine) = get_template_with_engine("hello there");
590
591 assert_eq!(
592 engine.render(&template, &Store::new()).unwrap(),
593 "hello there"
594 );
595 }
596
597 #[test]
598 fn test_render_output() {
599 let (template, engine) = get_template_with_engine("hello there, (( name ))!");
600 let store = Store::new().with_must("name", "taylor");
601
602 assert_eq!(
603 engine.render(&template, &store).unwrap(),
604 "hello there, taylor!"
605 );
606 }
607
608 #[test]
609 fn test_render_output_whitespace() {
610 let (template, engine) = get_template_with_engine("hello there, ((- name -)) !");
611 let store = Store::new().with_must("name", "taylor");
612
613 assert_eq!(
614 engine.render(&template, &store).unwrap(),
615 "hello there,taylor!"
616 );
617 }
618
619 #[test]
620 fn test_render_if() {
621 let (template, engine) = get_template_with_engine(
622 "(* if left > 300 *)\
623 a\
624 (* else if name == \"taylor\" *)\
625 b\
626 (* else if not false *)\
627 c\
628 (* else *)\
629 d\
630 (* end *)",
631 );
632 let store = Store::new().with_must("left", 101).with_must("name", "");
633
634 assert_eq!(engine.render(&template, &store).unwrap(), "c");
635 }
636
637 #[test]
638 fn test_render_nested_for() {
639 let (template, engine) = get_template_with_engine(
640 "(* for value in first *)\
641 first loop: (( value )) \
642 (* for value in second *)\
643 second loop: (( value )) \
644 (* end *)\
645 (* end *)",
646 );
647 let store = Store::new()
648 .with_must("first", "ab")
649 .with_must("second", "cd");
650
651 assert_eq!(
652 engine.render(&template, &store).unwrap(),
653 "first loop: a second loop: c second loop: d \
654 first loop: b second loop: c second loop: d "
655 );
656 }
657
658 #[test]
659 fn test_render_for_array() {
660 let engine = Engine::default();
661 let single = engine
662 .compile(
663 "(* for value in data *)\
664 (( value )) \
665 (* end *)",
666 )
667 .unwrap();
668 let pair = engine
669 .compile(
670 "(* for index, value in data *)\
671 (( index )) - (( value )) \
672 (* end *)",
673 )
674 .unwrap();
675 let store = Store::new().with_must("data", json!(["one", "two"]));
676
677 assert_eq!(engine.render(&single, &store).unwrap(), "one two ");
678 assert_eq!(engine.render(&pair, &store).unwrap(), "0 - one 1 - two ");
679 }
680
681 #[test]
682 fn test_render_for_object_set() {
683 let engine = Engine::default();
684 let single = engine
685 .compile(
686 "(* for value in data *)\
687 (( value ))\
688 (* end *)",
689 )
690 .unwrap();
691 let pair = engine
692 .compile(
693 "(* for key, value in data *)\
694 (( key )) - (( value ))\
695 (* end *)",
696 )
697 .unwrap();
698 let store = Store::new().with_must("data", json!({"one": "two"}));
699
700 assert_eq!(engine.render(&single, &store).unwrap(), "two");
701 assert_eq!(engine.render(&pair, &store).unwrap(), "one - two");
702 }
703
704 #[test]
705 fn test_let_global_scope_if() {
706 let (template, mut engine) = get_template_with_engine(
707 "(* if is_admin *)\
708 (* let name = \"admin\" *)\
709 (* else *)\
710 (* let name = user.name | to_lowercase *)\
711 (* end *)\
712 Hello, (( name )).",
713 );
714 engine.add_filter_must("to_lowercase", to_lowercase);
715 let store = Store::new()
716 .with_must("is_admin", false)
717 .with_must("user", json!({"name": "Taylor"}));
718
719 assert_eq!(engine.render(&template, &store).unwrap(), "Hello, taylor.");
720 }
721
722 #[test]
723 fn test_let_pop_scoped() {
724 let (template, engine) = get_template_with_engine(
725 "(* for item in inventory *)\
726 (* let name = item.description.name *)\
727 Item: (( name ))\
728 (* end *)\
729 Last item name: (( name )).",
730 );
731 let store =
732 Store::new().with_must("inventory", json!([{"description": {"name": "sword"}}]));
733
734 assert!(engine.render(&template, &store).is_err());
735 }
736
737 #[test]
738 fn test_collect_blocks() {
739 let (template, engine) =
740 get_template_with_engine("(* block one *)one(* end *)(* block two *)two(* end *)");
741 let store = Store::new();
742
743 let mut renderer = Renderer::new(&engine, &template, &store);
744
745 assert!(renderer.blocks.get("one").is_none());
746 assert!(renderer.blocks.get("two").is_none());
747 renderer.collect_blocks(template.get_scope());
748
749 assert!(renderer.blocks.get("one").is_some());
750 assert!(renderer.blocks.get("two").is_some());
751 assert!(renderer.blocks.get("three").is_none());
752 }
753
754 #[test]
755 fn test_evaluate_arguments_increment() {
756 let (template, engine) = get_template_with_engine("");
757 let store = Store::new();
758 let renderer = Renderer::new(&engine, &template, &store);
759
760 let arguments = renderer
761 .evaluate_arguments(&Arguments {
762 values: vec![
763 Argument {
764 name: None,
765 value: Base::Literal(Literal {
766 value: json!("hello"),
767 region: (0..0).into(),
768 }),
769 },
770 Argument {
771 name: None,
772 value: Base::Literal(Literal {
773 value: json!("goodbye"),
774 region: (0..0).into(),
775 }),
776 },
777 ],
778 region: (0..0).into(),
779 })
780 .unwrap();
781
782 assert_eq!(arguments.get("1"), Some(&json!("hello")));
783 assert_eq!(arguments.get("2"), Some(&json!("goodbye")));
784 }
785
786 fn to_lowercase(value: &Value, _: &HashMap<String, Value>) -> Result<Value, Error> {
788 match value {
789 Value::String(string) => Ok(json!(string.to_owned().to_lowercase())),
790 _ => Err(Error::build("filter `to_lowercase` requires string input")
791 .with_help("use quotes to coerce data to string")),
792 }
793 }
794
795 fn get_template_with_engine(text: &str) -> (Template, Engine) {
801 let engine = Engine::default();
802 (engine.compile(text).unwrap(), engine)
803 }
804}