struct_string_template/
templater.rs

1use std::{collections::HashMap};
2
3use crate::err::TemplateError;
4use crate::formatter::{TemplateElement, Formatter};
5
6pub struct Templater<T> {
7    mapping: HashMap<String, Box<dyn Fn(&T) -> Option<String> + 'static + Send + Sync>>
8}
9
10impl<T> Templater<T> {
11    pub fn new() -> Self {
12        Templater {
13            mapping: HashMap::new(),
14        }
15    }
16
17    pub fn insert<S, F>(&mut self, selector: S, accessor: F)
18        where
19            S: Into<String>,
20            F: Fn(&T) -> Option<String> + 'static + Send + Sync
21    {
22        self.mapping.insert(selector.into(), Box::new(accessor));
23    }
24
25    pub fn extend<I, S, U>(&mut self, selectors: I)
26        where
27            I: IntoIterator<Item = (S, U)>,
28            S: Into<String>,
29            U: Fn(&T) -> Option<String> + 'static + Send + Sync
30    {
31        for (selector, accessor) in selectors {
32            self.insert(selector, accessor);
33        }
34    }
35
36    pub fn remove<S>(&mut self, selector: S) -> bool
37        where S: Into<String>
38    {
39        self.mapping.remove(&selector.into()).is_some()
40    }
41
42    pub fn renderf(&self, obj: &T, formatter: &Formatter) -> Result<String, TemplateError> {
43        let strings: Vec<String> = formatter
44            .elements
45            .iter()
46            .map(|e| {
47                match e {
48                    TemplateElement::Part { literal } => {
49                        Ok(literal.clone())
50                    }
51                    TemplateElement::Replace { selector } => {
52                        let accessor = self.mapping.get(selector);
53
54                        // the lack of an accessor associated with a particular
55                        // selector is invalid, however the value accessed by the
56                        // accessor might not exist, in which case the placeholder
57                        // "NA" is used
58                        match accessor {
59                            Some(f) => {
60                                let out = f(&obj).or(Some("NA".to_owned())).unwrap();
61                                Ok(out)
62                            },
63                            None => Err(TemplateError::UnknownSelector {
64                                selector: selector.clone(),
65                            }),
66                        }
67                    }
68                }
69            })
70            .collect::<Result<Vec<String>, TemplateError>>()?;
71
72        Ok(strings
73            .into_iter()
74            .reduce(|a, b| a + &b)
75            .or(Some("".to_owned()))
76            .unwrap())
77    }
78
79    pub fn render<S>(&self, obj: &T, fmts: S) -> Result<String, TemplateError>
80        where S: Into<String>
81    {
82        let formatter = Formatter::build(fmts.into())?;
83        self.renderf(obj, &formatter)
84    }
85}