go_template/template.rs
1use std::collections::HashMap;
2
3use crate::error::{ParseError, TemplateError};
4use crate::funcs::BUILTINS;
5use crate::parse::{parse, Tree};
6
7use gtmpl_value::Func;
8
9/// The main template structure.
10#[derive(Clone)]
11pub struct Template {
12 pub name: String,
13 pub text: String,
14 pub funcs: HashMap<String, Func>,
15 pub tree_set: HashMap<String, Tree>,
16}
17
18impl Default for Template {
19 fn default() -> Template {
20 Template {
21 name: String::default(),
22 text: String::from(""),
23 funcs: BUILTINS.iter().map(|&(k, v)| (k.to_owned(), v)).collect(),
24 tree_set: HashMap::default(),
25 }
26 }
27}
28
29impl Template {
30 /// Creates a new empty template with a given `name`.
31 pub fn with_name<T: Into<String>>(name: T) -> Template {
32 Template {
33 name: name.into(),
34 ..Default::default()
35 }
36 }
37
38 /// Adds a single custom function to the template.
39 ///
40 /// ## Example
41 ///
42 /// ```rust
43 /// use gtmpl::{Context, Func, FuncError, Value};
44 ///
45 /// fn hello_world(_args: &[Value]) -> Result<Value, FuncError> {
46 /// Ok(Value::from("Hello World!"))
47 /// }
48 ///
49 /// let mut tmpl = gtmpl::Template::default();
50 /// tmpl.add_func("helloWorld", hello_world);
51 /// tmpl.parse("{{ helloWorld }}").unwrap();
52 /// let output = tmpl.render(&Context::empty());
53 /// assert_eq!(&output.unwrap(), "Hello World!");
54 /// ```
55 pub fn add_func(&mut self, name: &str, func: Func) {
56 self.funcs.insert(name.to_owned(), func);
57 }
58
59 /// Adds custom functions to the template.
60 ///
61 /// ## Example
62 ///
63 /// ```rust
64 /// use std::collections::HashMap;
65 ///
66 /// use gtmpl::{Context, Func, FuncError, Value};
67 ///
68 /// fn hello_world(_args: &[Value]) -> Result<Value, FuncError> {
69 /// Ok(Value::from("Hello World!"))
70 /// }
71 ///
72 /// let funcs = vec![("helloWorld", hello_world as Func)];
73 /// let mut tmpl = gtmpl::Template::default();
74 /// tmpl.add_funcs(&funcs);
75 /// tmpl.parse("{{ helloWorld }}").unwrap();
76 /// let output = tmpl.render(&Context::empty());
77 /// assert_eq!(&output.unwrap(), "Hello World!");
78 /// ```
79 pub fn add_funcs<T: Into<String> + Clone>(&mut self, funcs: &[(T, Func)]) {
80 self.funcs
81 .extend(funcs.iter().cloned().map(|(k, v)| (k.into(), v)));
82 }
83
84 /// Parse the given `text` as template body.
85 ///
86 /// ## Example
87 ///
88 /// ```rust
89 /// let mut tmpl = gtmpl::Template::default();
90 /// tmpl.parse("Hello World!").unwrap();
91 /// ```
92 pub fn parse<T: Into<String>>(&mut self, text: T) -> Result<(), ParseError> {
93 let tree_set = parse(
94 self.name.clone(),
95 text.into(),
96 self.funcs.keys().cloned().collect(),
97 )?;
98 self.tree_set.extend(tree_set);
99 Ok(())
100 }
101
102 /// Add the given `text` as a template with a `name`.
103 ///
104 /// ## Example
105 ///
106 /// ```rust
107 /// use gtmpl::Context;
108 ///
109 /// let mut tmpl = gtmpl::Template::default();
110 /// tmpl.add_template("fancy", "{{ . }}");
111 /// tmpl.parse(r#"{{ template "fancy" . }}!"#).unwrap();
112 /// let output = tmpl.render(&Context::from("Hello World"));
113 /// assert_eq!(&output.unwrap(), "Hello World!");
114 /// ```
115 pub fn add_template<N: Into<String>, T: Into<String>>(
116 &mut self,
117 name: N,
118 text: T,
119 ) -> Result<(), TemplateError> {
120 let tree_set = parse(
121 name.into(),
122 text.into(),
123 self.funcs.keys().cloned().collect(),
124 )?;
125 self.tree_set.extend(tree_set);
126 Ok(())
127 }
128}
129
130#[cfg(test)]
131mod tests_mocked {
132 use super::*;
133
134 #[test]
135 fn test_parse() {
136 let mut t = Template::with_name("foo");
137 assert!(t.parse(r#"{{ if eq "bar" "bar" }} 2000 {{ end }}"#).is_ok());
138 assert!(t.tree_set.contains_key("foo"));
139 }
140}