rustache/
build.rs

1use std::collections::HashMap;
2use std::convert::Into;
3
4use Data;
5use Data::{Hash, Vector};
6
7/// `HashBuilder` is a helper type that constructs `Data` types in a `HashMap`
8#[derive(Debug)]
9pub struct HashBuilder<'a> {
10    #[doc(hidden)]
11    pub data: HashMap<String, Data<'a>>,
12    #[doc(hidden)]
13    pub partials_path: &'a str,
14}
15
16impl<'a> HashBuilder<'a> {
17    /// Create a new `HashBuilder` instance
18    pub fn new() -> HashBuilder<'a> {
19        HashBuilder {
20            data: HashMap::new(),
21            partials_path: "",
22        }
23    }
24
25    /// Add a `Into<Data>` to the `HashBuilder`
26    ///
27    /// ```rust
28    /// use rustache::HashBuilder;
29    /// use std::convert::Into;
30    /// let data = HashBuilder::new()
31    ///     .insert("game", "Hearthstone: Heroes of Warcraft");
32    /// ```
33    /// ```rust
34    /// use rustache::{HashBuilder, VecBuilder};
35    /// let data = HashBuilder::new()
36    ///     .insert("classes",
37    ///         VecBuilder::new()
38    ///             .push("Mage".to_string())
39    ///             .push("Druid".to_string())
40    ///     );
41    /// ```
42    /// ```rust
43    /// use rustache::HashBuilder;
44    /// let data = HashBuilder::new()
45    ///     .insert("hero1",
46    ///         HashBuilder::new()
47    ///             .insert("first_name", "Anduin")
48    ///             .insert("last_name", "Wrynn")
49    ///     )
50    ///     .insert("hero2",
51    ///         HashBuilder::new()
52    ///             .insert("first_name", "Jaina")
53    ///             .insert("last_name", "Proudmoore")
54    ///     );
55    /// ```
56    pub fn insert<K, V>(mut self, key: K, value: V) -> HashBuilder<'a>
57        where K: ToString,
58              V: Into<Data<'a>>
59    {
60        self.data.insert(key.to_string(), value.into());
61        self
62    }
63
64    /// Add a `Lambda` that accepts a String and returns a String to the `HashBuilder`
65    ///
66    /// ```rust
67    /// use rustache::HashBuilder;
68    /// let mut f = |_| { "world".to_string() };
69    /// let data = HashBuilder::new()
70    ///     .insert_lambda("lambda", &mut f);
71    /// ```
72    pub fn insert_lambda<K: ToString>(self,
73                                      key: K,
74                                      f: &'a mut FnMut(String) -> String)
75                                      -> HashBuilder<'a> {
76        self.insert(key, f)
77    }
78
79    /// Set a path to partials data
80    pub fn set_partials_path(self, path: &'a str) -> HashBuilder<'a> {
81        HashBuilder {
82            data: self.data,
83            partials_path: path,
84        }
85    }
86
87    /// Return the built `Data`
88    fn build(self) -> Data<'a> {
89        Hash(self.data)
90    }
91}
92
93impl<'a> From<HashBuilder<'a>> for Data<'a> {
94    fn from(v: HashBuilder<'a>) -> Data<'a> {
95        v.build()
96    }
97}
98
99/// `VecBuilder` is a helper type that constructs `Data` types in a Vector
100pub struct VecBuilder<'a> {
101    data: Vec<Data<'a>>,
102}
103
104impl<'a> VecBuilder<'a> {
105    /// Create a new `VecBuilder` instance
106    pub fn new() -> VecBuilder<'a> {
107        VecBuilder { data: Vec::new() }
108    }
109
110    /// Add a `Into<Data>` to the `VecBuilder`
111    ///
112    /// ```rust
113    /// use rustache::VecBuilder;
114    /// use std::convert::Into;
115    /// let data = VecBuilder::new()
116    ///     .push("Mage")
117    ///     .push("Druid");
118    /// ```
119    /// ```rust
120    /// use rustache::VecBuilder;
121    /// let data = VecBuilder::new()
122    ///     .push(VecBuilder::new()
123    ///             .push("Anduin Wrynn".to_string())
124    ///             .push("Jaina Proudmoore".to_string())
125    ///     );
126    /// ```
127    /// ```rust
128    /// use rustache::{HashBuilder, VecBuilder};
129    /// let data = VecBuilder::new()
130    ///     .push(HashBuilder::new()
131    ///             .insert("first_name".to_string(), "Garrosh".to_string())
132    ///             .insert("last_name".to_string(), "Hellscream".to_string())
133    ///     )
134    ///     .push(HashBuilder::new()
135    ///             .insert("first_name".to_string(), "Malfurion".to_string())
136    ///             .insert("last_name".to_string(), "Stormrage".to_string())
137    ///     );
138    /// ```
139    pub fn push<V>(mut self, value: V) -> VecBuilder<'a>
140        where V: Into<Data<'a>>
141    {
142        self.data.push(value.into());
143        self
144    }
145
146    /// Add a `Lambda` to the `VecBuilder`
147    ///
148    /// ```rust
149    /// use rustache::VecBuilder;
150    /// let mut f = |_| { "world".to_string() };
151    /// let data = VecBuilder::new()
152    ///     .push_lambda(&mut f);
153    /// ```
154    pub fn push_lambda(self, f: &'a mut FnMut(String) -> String) -> VecBuilder<'a> {
155        self.push(f)
156    }
157
158    /// Return the built `Data`
159    fn build(self) -> Data<'a> {
160        Vector(self.data)
161    }
162}
163
164impl<'a> From<VecBuilder<'a>> for Data<'a> {
165    fn from(v: VecBuilder<'a>) -> Data<'a> {
166        v.build()
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use std::collections::HashMap;
173
174    use {HashBuilder, VecBuilder};
175    use Data::{Strng, Bool, Integer, Float, Vector, Hash, Lambda};
176
177    #[test]
178    fn test_new_builders() {
179        assert_eq!(HashBuilder::new().build(), Hash(HashMap::new()));
180        assert_eq!(VecBuilder::new().build(), Vector(Vec::new()));
181    }
182
183    #[test]
184    fn test_set_partials_path() {
185        let hash = HashBuilder::new().set_partials_path("/path");
186        assert_eq!(hash.partials_path, "/path");
187    }
188
189    #[test]
190    fn test_builders() {
191        let test_string = String::from("Conan the Sorcerian");
192
193        let mut hearthstone = HashMap::new();
194        hearthstone.insert("name".to_string(),
195                           Strng("Hearthstone: Heroes of Warcraft".to_string()));
196        hearthstone.insert("release_date".to_string(),
197                           Strng("December, 2014".to_string()));
198
199        let mut hash1 = HashMap::new();
200        hash1.insert("first_name".to_string(), Strng("Anduin".to_string()));
201        hash1.insert("last_name".to_string(), Strng("Wrynn".to_string()));
202        hash1.insert("age".to_string(), Integer(21i32));
203        hash1.insert("weight".to_string(), Float(120.16f64));
204        hash1.insert("class".to_string(), Strng("Priest".to_string()));
205        hash1.insert("died".to_string(), Bool(false));
206        hash1.insert("class_cards".to_string(),
207                     Vector(vec![Strng(test_string.clone()),
208                                 Strng("Prophet Velen".to_string()),
209                                 Hash(hearthstone)]));
210
211        let hash2 = HashBuilder::new()
212            .set_partials_path("/hearthstone")
213            .insert("first_name", "Anduin")
214            .insert("last_name", "Wrynn")
215            .insert("age", 21i32)
216            .insert("weight", 120.16f64)
217            .insert("class", "Priest")
218            .insert("died", false)
219            .insert("class_cards",
220                    VecBuilder::new()
221                        .push(test_string)
222                        .push("Prophet Velen")
223                        .push(HashBuilder::new()
224                            .insert("name", "Hearthstone: Heroes of Warcraft")
225                            .insert("release_date", "December, 2014")));
226
227        assert_eq!(Hash(hash1), Hash(hash2.data));
228        assert_eq!(hash2.partials_path, "/hearthstone");
229    }
230
231    // #[test]
232    // fn test_hash_lambda_builder() {
233    //     // Since we can't directly compare closures, just make
234    //     // sure we're threading through the builder
235
236    //     let mut num = 10u;
237    //     let data = HashBuilder::new()
238    //         .insert_lambda("double", |x| {
239    //             num *= 2u;
240    //             x + num.to_string()
241    //         })
242    //         .build();
243
244    //     match data {
245    //         Hash(m) => {
246    //             match *m.find_equiv(&("double")).unwrap() {
247    //                 Lambda(ref f) => {
248    //                     let f = &mut *f.borrow_mut();
249    //                     assert_eq!((*f)("double: ".to_string()), "double: 20".to_string());
250    //                     assert_eq!((*f)("double: ".to_string()), "double: 40".to_string());
251    //                     assert_eq!((*f)("double: ".to_string()), "double: 80".to_string());
252    //                 }
253    //                 _ => panic!(),
254    //             }
255    //         }
256    //         _ => panic!(),
257    //     }
258    // }
259
260    #[test]
261    fn test_vec_lambda_builder() {
262        // Since we can't directly compare closures, just make
263        // sure we're threading through the builder
264
265        let mut num = 10u32;
266        let mut f = |x: String| -> String {
267            num *= 2u32;
268            x + &num.to_string()[..]
269        };
270        let data = VecBuilder::new()
271            .push_lambda(&mut f)
272            .build();
273
274        match data {
275            Vector(m) => {
276                match m[0] {
277                    Lambda(ref f) => {
278                        let f = &mut *f.borrow_mut();
279                        assert_eq!((*f)("double: ".to_string()), "double: 20".to_string());
280                        assert_eq!((*f)("double: ".to_string()), "double: 40".to_string());
281                        assert_eq!((*f)("double: ".to_string()), "double: 80".to_string());
282                    }
283                    _ => panic!(),
284                }
285            }
286            _ => panic!(),
287        }
288    }
289}