ban/
render.rs

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
35/// Provides methods to render a set of [`Tree`] against some context data.
36pub struct Renderer<'source, 'store> {
37    /// An [`Engine`] containing any registered filters.
38    engine: &'source Engine,
39    /// The [`Template`] being rendered.
40    template: &'source Template,
41    /// Contains the [`Store`] and any shadowed data.
42    shadow: Shadow<'store>,
43    /// Blocks available for rendering.
44    blocks: BlockMap<'source>,
45}
46
47impl<'source, 'store> Renderer<'source, 'store> {
48    /// Create a new Renderer.
49    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    /// Render the [`Template`] stored inside the [`Renderer`].
59    ///
60    /// # Errors
61    ///
62    /// Returns an [`Error`] if rendering any [`Tree`] instance fails,
63    /// or writing to the [`Pipe`] fails.
64    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            // The `Error` might come from another `Template`, so don't change
71            // the name if it already has one.
72            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    /// Evaluate the [`Scope`] and collect all [`Block`] instances within.
83    ///
84    /// After all `Block` instances are collected, a new [`Renderer`] is created and used
85    /// to render the extended [`Template`].
86    ///
87    /// # Errors
88    ///
89    /// Returns an [`Error`] if the extended `Template` does not exist, or rendering any
90    /// [`Tree`] instance fails.
91    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    /// Render a [`Scope`].
108    ///
109    /// # Errors
110    ///
111    /// Returns an [`Error`] if rendering any [`Tree`] instance fails.
112    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    /// Render a [`Block`] within the [`Renderer`] that matches the name of the given `Block`.
147    ///
148    /// When no matching `Block` exists, renders the scope within the `Block` itself.
149    ///
150    /// # Errors
151    ///
152    /// Returns an [`Error`] if rendering any [`Tree`] instance fails.
153    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    /// Render an [`Include`].
164    ///
165    /// # Errors
166    ///
167    /// Returns an [`Error`] if the named [`Template`] is not found in the [`Engine`],
168    /// or rendering any [`Tree`] instance fails.
169    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            // Scoped include, create a new store that includes only the named values.
181            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            // Unscoped include, use the same store.
191            Renderer::new(self.engine, template, self.shadow.store).render(pipe)?
192        };
193
194        Ok(())
195    }
196
197    /// Render an [`If`].
198    ///
199    /// # Errors
200    ///
201    /// Returns an [`Error`] if a [`Scope`] is chosen to be rendered, but a [`Tree`]
202    /// instance within the `Scope` fails to render.
203    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    /// Render a [`For`].
217    ///
218    /// # Errors
219    ///
220    /// Returns an [`Error`] if the [`Base`] is not found in the [`Store`],
221    /// or rendering any [`Tree`] instance fails.
222    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    /// Return true if the entire [`IfBranch`] is truthy.
258    ///
259    /// # Errors
260    ///
261    /// Returns an [`Error`] if a [`Value`] that the `IfBranch` depends on does
262    /// not exist in the [`Store`].
263    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    /// Evaluate an [`Output`] to return a [`Value`].
303    ///
304    /// # Errors
305    ///
306    /// Returns an [`Error`] if rendering the `Output` fails.
307    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    /// Evaluate a [`Base`] to return a [`Value`].
315    ///
316    /// # Errors
317    ///
318    /// Returns an [`Error`] if a [`Value`] that the `Base` depends on does not exist
319    /// in the [`Store`].
320    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    /// Evaluate a [`Call`] to return a [`Value`].
328    ///
329    /// Determines the initial input to the first [`Filter`][`crate::filter::Filter`]
330    /// by following the receiver until a [`Base`] is found, and deriving the input
331    /// from that `Base`.
332    ///
333    /// The output of the final `Call` in the chain is the return value.
334    ///
335    /// # Errors
336    ///
337    /// Returns an [`Error`] when rendering the `Base` of the `Call` chain fails,
338    /// or a `Filter` returns an [`Error`].
339    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    /// Evaluate a [`Region`] to return a `&str`.
382    ///
383    /// The literal value of the `Region` within the source text is retrieved
384    /// and returned.
385    fn evaluate_raw(&self, region: &Region) -> &str {
386        region.literal(self.template.get_source())
387    }
388
389    /// Evaluate a set of [`Identifier`] instances to return a [`Value`] from the [`Store`].
390    ///
391    /// # Errors
392    ///
393    /// Returns an [`Error`] if a `Value` that an `Identifier` depends on does not exist in
394    /// the `Store`.
395    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    /// Evaluate an [`Arguments`] to return a [`HashMap`] that contains the same values.
436    ///
437    /// As described in the [`filter`][`crate::filter`] module, any argument without a name
438    /// will be automatically assigned a name.
439    ///
440    /// # Errors
441    ///
442    /// Returns an [`Error`] if rendering a [`Base`] fails.
443    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    /// Evaluate a [`Let`] to make an assignment to the current [`Shadow`] scope.
467    ///
468    /// # Errors
469    ///
470    /// Returns an [`Error`] if a [`Value`] that the [`Base`] depends on does not
471    /// exist in the [`Store`].
472    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    /// Set the blocks property on the [`Renderer`].
483    ///
484    /// Returns the `Renderer`, so additional methods may be chained.
485    fn with_blocks(mut self, blocks: BlockMap<'source>) -> Self {
486        self.blocks = blocks;
487
488        self
489    }
490
491    /// Clone all of the [`Block`] instances in the given [`Scope`] into
492    /// the Renderer.
493    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    /// Assign the given data to the [`Set`].
514    ///
515    /// # Errors
516    ///
517    /// Returns an [`Error`] when accessing the literal value of a [`Region`] fails.
518    ///
519    /// # Panics
520    ///
521    /// Panics when a `Set` of type `Pair` is received, but the .0 property in the
522    /// "pair" parameter is None.
523    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
546/// Return an [`Error`] describing a missing template.
547fn 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
554/// Return an [`Error`] explaining that the write operation failed.
555///
556/// This is likely caused by a failure during a `write!` macro operation.
557fn 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
564/// A wrapper for [`Block`] that includes a reference to the [`Template`]
565/// that the `Block` was found in.
566struct Named<'source> {
567    /// The [`Template`] that the [`Block`] was found in.
568    template: &'source Template,
569    /// A [`Block`] found in a [`Template`].
570    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    /// An example filter used for testing.
787    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    /// A helper function that returns a [`Template`] from the given text,
796    /// and the [`Engine`] that compiled it.
797    ///
798    /// This function will unwrap the result of the compilation, so it should
799    /// only be used on text that is expected to compile successfully.
800    fn get_template_with_engine(text: &str) -> (Template, Engine) {
801        let engine = Engine::default();
802        (engine.compile(text).unwrap(), engine)
803    }
804}