Skip to main content

templar/templar/
template.rs

1use crate::*;
2use std::collections::BTreeMap;
3use std::convert::TryFrom;
4use std::sync::Arc;
5
6/// Template holds the prepared result of parsing a template. Because the template does not need to be
7/// reparsed, subsequent executions come at a low cost.
8#[derive(Debug, Clone, Default)]
9pub struct Template(Arc<Node>);
10
11impl Template {
12    /// Render a template as a string.
13    ///
14    /// # Usage
15    ///
16    /// ```
17    /// # use templar::*;
18    /// # let context = StandardContext::new();
19    ///
20    /// let t = Templar::global().parse_expression("5 + 5")?;
21    /// assert_eq!(t.render(&context)?, "10");
22    /// # Ok::<(), templar::TemplarError>(())
23    /// ```
24    pub fn render(&self, ctx: &impl Context) -> Result<String> {
25        //let local_ctx = ctx.create_scope();
26        self.0.render(ctx)
27    }
28
29    /// Execute a template, getting a `InnerData` from the `unstructured` crate as a result.
30    /// many of the native rust types implement into() on InnerData making direct comparisons
31    /// easy.
32    ///
33    /// # Usage
34    ///
35    /// ```
36    /// # use templar::*;
37    /// # let context = StandardContext::new();
38    ///
39    /// let t = Templar::global().parse_expression("5 + 5")?;
40    /// assert_eq!(*t.exec(&context), 10i64);
41    /// # Ok::<(), templar::TemplarError>(())
42    /// ```
43    pub fn exec(&self, ctx: &impl Context) -> Data {
44        //let local_ctx = ctx.create_scope();
45        self.0.exec(ctx)
46    }
47
48    pub(crate) fn root_node(&self) -> Arc<Node> {
49        self.0.clone()
50    }
51}
52
53/// TemplateTree holds the parsed result of a InnerData tree. This tree of templates
54/// can then be loaded directly into a context.
55#[derive(Debug, Clone)]
56pub enum TemplateTree {
57    /// This TemplateTree node contains a template. This node type can be converted into a `Template`
58    Template(Template),
59    /// This TemplateTree node contains a mapping
60    Mapping(Arc<BTreeMap<InnerData, TemplateTree>>),
61    /// This TemplateTree node contains a sequence
62    Sequence(Arc<Vec<TemplateTree>>),
63}
64
65impl Default for TemplateTree {
66    fn default() -> Self {
67        TemplateTree::Template(Default::default())
68    }
69}
70
71impl TemplateTree {
72    /// Attempt to walk the tree with the specified key
73    pub fn get(&self, key: &InnerData) -> Option<TemplateTree> {
74        match self {
75            TemplateTree::Mapping(v) => Some(v[key].clone()),
76            _ => None,
77        }
78    }
79
80    /// Attempt to walk the tree with the specified path
81    pub fn get_path<T: Into<InnerData> + Clone>(&self, path: &[T]) -> Option<TemplateTree> {
82        let mut result = self.clone();
83        for key in path.iter() {
84            result = result.get(&key.clone().into())?;
85        }
86        Some(result)
87    }
88
89    /// Attempt to retrieve an index from a sequence of the tree
90    pub fn get_index(&self, index: usize) -> Option<TemplateTree> {
91        match self {
92            TemplateTree::Sequence(v) => Some(v.get(index).cloned().unwrap_or_default()),
93            _ => None,
94        }
95    }
96}
97
98impl From<Node> for Template {
99    fn from(n: Node) -> Template {
100        Template(Arc::new(n))
101    }
102}
103
104impl From<Template> for TemplateTree {
105    fn from(t: Template) -> TemplateTree {
106        TemplateTree::Template(t)
107    }
108}
109
110impl TryFrom<TemplateTree> for Template {
111    type Error = TemplarError;
112
113    fn try_from(value: TemplateTree) -> Result<Self> {
114        match value {
115            TemplateTree::Template(t) => Ok(t),
116            _ => Err(TemplarError::ParseFailure("Not a template node".into())),
117        }
118    }
119}
120
121impl TryFrom<Option<TemplateTree>> for Template {
122    type Error = TemplarError;
123
124    fn try_from(value: Option<TemplateTree>) -> Result<Self> {
125        match value {
126            Some(TemplateTree::Template(t)) => Ok(t),
127            _ => Err(TemplarError::ParseFailure("Not a template node".into())),
128        }
129    }
130}