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
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate nom;

mod errors;
pub use errors::{Result, Error};

mod parser;
use parser::Exp;

use std::collections::HashMap;

pub struct Tpl<'a> {
    template: Vec<Exp<'a>>,
    len: usize,
}

impl<'a> Tpl<'a> {
    pub fn new(tpl_str: &'a str) -> Result<Tpl<'a>> {
        Ok(Tpl {
            len: tpl_str.len(),
            template: parser::parse(tpl_str)?,
        })
    }

    pub fn render(&self, context: &HashMap<&'static str, String>) -> String {
        self.template.iter().fold(
            String::with_capacity(self.len),
            |mut buff, exp| {
                match *exp {
                    Exp::Literal(text) => buff.push_str(text),
                    Exp::Func { name } => {
                        match context.get(name) {
                            Some(value) => buff.push_str(value),
                            None => buff.push_str(name),
                        }
                    }
                }
                buff
            },
        )
    }
}

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

    #[test]
    fn render_template() {
        let tpl = Tpl::new("Hello %{name}!").unwrap();
        let mut context = HashMap::new();
        context.insert("name".into(), "world".into());
        assert_eq!(tpl.render(&context), "Hello world!");
    }

}