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
use std::collections::HashMap;

use parse::{parse, Parser, Tree};
use funcs::BUILTINS;
use node::TreeId;

use gtmpl_value::Func;

/// The main template structure.
#[derive(Default)]
pub struct Template<'a> {
    pub name: &'a str,
    pub text: &'a str,
    pub funcs: HashMap<&'a str, Func>,
    pub tree_ids: HashMap<TreeId, String>,
    pub tree_set: HashMap<String, Tree<'a>>,
}

impl<'a> Template<'a> {
    /// Creates a new empty template with a given `name`.
    pub fn with_name(name: &'a str) -> Template<'a> {
        Template {
            name: name,
            text: "",
            funcs: HashMap::default(),
            tree_ids: HashMap::default(),
            tree_set: HashMap::default(),
        }
    }

    /// Adds a single custom function to the template.
    ///
    /// ## Example
    ///
    /// ```rust
    /// use gtmpl::{Context, Func, Value};
    ///
    /// fn hello_world(_args: &[Value]) -> Result<Value, String> {
    ///   Ok(Value::from("Hello World!"))
    /// }
    ///
    /// let mut tmpl = gtmpl::Template::default();
    /// tmpl.add_func("helloWorld", hello_world);
    /// tmpl.parse("{{ helloWorld }}").unwrap();
    /// let output = tmpl.render(&Context::empty());
    /// assert_eq!(&output.unwrap(), "Hello World!");
    /// ```
    pub fn add_func(&mut self, name: &'a str, func: Func) {
        self.funcs.insert(name, func);
    }

    /// Adds custom functions to the template.
    ///
    /// ## Example
    ///
    /// ```rust
    /// use std::collections::HashMap;
    ///
    /// use gtmpl::{Context, Func, Value};
    ///
    /// fn hello_world(_args: &[Value]) -> Result<Value, String> {
    ///   Ok(Value::from("Hello World!"))
    /// }
    ///
    /// let funcs = vec![("helloWorld", hello_world as Func)];
    /// let mut tmpl = gtmpl::Template::default();
    /// tmpl.add_funcs(&funcs);
    /// tmpl.parse("{{ helloWorld }}").unwrap();
    /// let output = tmpl.render(&Context::empty());
    /// assert_eq!(&output.unwrap(), "Hello World!");
    /// ```
    pub fn add_funcs(&mut self, funcs: &[(&'a str, Func)]) {
        self.funcs.extend(funcs.iter().cloned());
    }

    /// Parse the given `text` as template body.
    ///
    /// ## Example
    ///
    /// ```rust
    /// let mut tmpl = gtmpl::Template::default();
    /// tmpl.parse("Hello World!").unwrap();
    /// ```
    pub fn parse(&mut self, text: &'a str) -> Result<(), String> {
        let mut funcs = HashMap::new();
        funcs.extend(BUILTINS.iter().cloned());
        funcs.extend(&self.funcs);
        let parser = parse(self.name, text, funcs)?;
        match parser {
            Parser {
                funcs,
                tree_ids,
                tree_set,
                ..
            } => {
                self.funcs = funcs;
                self.tree_set = tree_set;
                self.tree_ids = tree_ids;
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests_mocked {
    use super::*;

    #[test]
    fn test_parse() {
        let mut t = Template::with_name("foo");
        assert!(t.parse(r#"{{ if eq "bar" "bar" }} 2000 {{ end }}"#).is_ok());
        assert!(t.tree_set.contains_key("foo"));
        assert!(t.tree_ids.contains_key(&1usize));
    }
}