1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::*;
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::sync::Arc;

/// Template holds the prepared result of parsing a template. Because the template does not need to be
/// reparsed, subsequent executions come at a low cost.
#[derive(Debug, Clone, Default)]
pub struct Template(Arc<Node>);

impl Template {
    /// Render a template as a string.
    ///
    /// # Usage
    ///
    /// ```
    /// # use templar::*;
    /// # use unstructured::Document;
    /// # let context = StandardContext::new();
    ///
    /// let t = Templar::global().parse_expression("5 + 5")?;
    /// assert_eq!(t.render(&context)?, "10");
    /// # Ok::<(), templar::TemplarError>(())
    /// ```
    pub fn render(&self, ctx: &impl Context) -> Result<String> {
        //let local_ctx = ctx.create_scope();
        self.0.render(ctx)
    }

    /// Execute a template, getting a `Document` from the `unstructured` crate as a result.
    /// many of the native rust types implement into() on Document making direct comparisons
    /// easy.
    ///
    /// # Usage
    ///
    /// ```
    /// # use templar::*;
    /// # use unstructured::Document;
    /// # let context = StandardContext::new();
    ///
    /// let t = Templar::global().parse_expression("5 + 5")?;
    /// assert_eq!(t.exec(&context)?, 10i64);
    /// # Ok::<(), templar::TemplarError>(())
    /// ```
    pub fn exec(&self, ctx: &impl Context) -> Result<Document> {
        //let local_ctx = ctx.create_scope();
        self.0.exec(ctx).into_result()
    }

    pub(crate) fn root_node(&self) -> Arc<Node> {
        self.0.clone()
    }
}

/// TemplateTree holds the parsed result of a Document tree. This tree of templates
/// can then be loaded directly into a context.
#[derive(Debug, Clone)]
pub enum TemplateTree {
    /// This TemplateTree node contains a template. This node type can be converted into a `Template`
    Template(Template),
    /// This TemplateTree node contains a mapping
    Mapping(Arc<BTreeMap<Document, TemplateTree>>),
    /// This TemplateTree node contains a sequence
    Sequence(Arc<Vec<TemplateTree>>),
}

impl Default for TemplateTree {
    fn default() -> Self {
        TemplateTree::Template(Default::default())
    }
}

impl TemplateTree {
    /// Attempt to walk the tree with the specified key
    pub fn get(&self, key: &Document) -> Option<TemplateTree> {
        match self {
            TemplateTree::Mapping(v) => Some(v[key].clone()),
            _ => None,
        }
    }

    /// Attempt to walk the tree with the specified path
    pub fn get_path<T: Into<Document> + Clone>(&self, path: &[T]) -> Option<TemplateTree> {
        let mut result = self.clone();
        for key in path.iter() {
            result = result.get(&key.clone().into())?;
        }
        Some(result)
    }

    /// Attempt to retrieve an index from a sequence of the tree
    pub fn get_index(&self, index: usize) -> Option<TemplateTree> {
        match self {
            TemplateTree::Sequence(v) => Some(v.get(index).cloned().unwrap_or_default()),
            _ => None,
        }
    }
}

impl From<Node> for Template {
    fn from(n: Node) -> Template {
        Template(Arc::new(n))
    }
}

impl From<Template> for TemplateTree {
    fn from(t: Template) -> TemplateTree {
        TemplateTree::Template(t)
    }
}

impl TryFrom<TemplateTree> for Template {
    type Error = TemplarError;

    fn try_from(value: TemplateTree) -> Result<Self> {
        match value {
            TemplateTree::Template(t) => Ok(t),
            _ => Err(TemplarError::ParseFailure("Not a template node".into())),
        }
    }
}

impl TryFrom<Option<TemplateTree>> for Template {
    type Error = TemplarError;

    fn try_from(value: Option<TemplateTree>) -> Result<Self> {
        match value {
            Some(TemplateTree::Template(t)) => Ok(t),
            _ => Err(TemplarError::ParseFailure("Not a template node".into())),
        }
    }
}