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    /// The data contained within
102    pub data: Vec<Data<'a>>,
103}
104
105impl<'a> VecBuilder<'a> {
106    /// Create a new `VecBuilder` instance
107    pub fn new() -> VecBuilder<'a> {
108        VecBuilder { data: Vec::new() }
109    }
110
111    /// Add a `Into<Data>` to the `VecBuilder`
112    ///
113    /// ```rust
114    /// use rustache::VecBuilder;
115    /// use std::convert::Into;
116    /// let data = VecBuilder::new()
117    ///     .push("Mage")
118    ///     .push("Druid");
119    /// ```
120    /// ```rust
121    /// use rustache::VecBuilder;
122    /// let data = VecBuilder::new()
123    ///     .push(VecBuilder::new()
124    ///             .push("Anduin Wrynn".to_string())
125    ///             .push("Jaina Proudmoore".to_string())
126    ///     );
127    /// ```
128    /// ```rust
129    /// use rustache::{HashBuilder, VecBuilder};
130    /// let data = VecBuilder::new()
131    ///     .push(HashBuilder::new()
132    ///             .insert("first_name".to_string(), "Garrosh".to_string())
133    ///             .insert("last_name".to_string(), "Hellscream".to_string())
134    ///     )
135    ///     .push(HashBuilder::new()
136    ///             .insert("first_name".to_string(), "Malfurion".to_string())
137    ///             .insert("last_name".to_string(), "Stormrage".to_string())
138    ///     );
139    /// ```
140    pub fn push<V>(mut self, value: V) -> VecBuilder<'a>
141        where V: Into<Data<'a>>
142    {
143        self.data.push(value.into());
144        self
145    }
146
147    /// Add a `Lambda` to the `VecBuilder`
148    ///
149    /// ```rust
150    /// use rustache::VecBuilder;
151    /// let mut f = |_| { "world".to_string() };
152    /// let data = VecBuilder::new()
153    ///     .push_lambda(&mut f);
154    /// ```
155    pub fn push_lambda(self, f: &'a mut FnMut(String) -> String) -> VecBuilder<'a> {
156        self.push(f)
157    }
158
159    /// Return the built `Data`
160    fn build(self) -> Data<'a> {
161        Vector(self.data)
162    }
163}
164
165impl<'a> From<VecBuilder<'a>> for Data<'a> {
166    fn from(v: VecBuilder<'a>) -> Data<'a> {
167        v.build()
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use std::collections::HashMap;
174
175    use {HashBuilder, VecBuilder};
176    use Data;
177    use Data::{Bool, Integer, Float, Vector, Hash, Lambda};
178
179    #[test]
180    fn test_new_builders() {
181        assert_eq!(HashBuilder::new().build(), Hash(HashMap::new()));
182        assert_eq!(VecBuilder::new().build(), Vector(Vec::new()));
183    }
184
185    #[test]
186    fn test_set_partials_path() {
187        let hash = HashBuilder::new().set_partials_path("/path");
188        assert_eq!(hash.partials_path, "/path");
189    }
190
191    #[test]
192    fn test_builders() {
193        let test_string = String::from("Conan the Sorcerian");
194
195        let mut hearthstone = HashMap::new();
196        hearthstone.insert("name".to_string(),
197                           Data::String("Hearthstone: Heroes of Warcraft".to_string()));
198        hearthstone.insert("release_date".to_string(),
199                           Data::String("December, 2014".to_string()));
200
201        let mut hash1 = HashMap::new();
202        hash1.insert("first_name".to_string(), Data::String("Anduin".to_string()));
203        hash1.insert("last_name".to_string(), Data::String("Wrynn".to_string()));
204        hash1.insert("age".to_string(), Integer(21i32));
205        hash1.insert("weight".to_string(), Float(120.16f64));
206        hash1.insert("class".to_string(), Data::String("Priest".to_string()));
207        hash1.insert("died".to_string(), Bool(false));
208        hash1.insert("class_cards".to_string(),
209                     Vector(vec![Data::String(test_string.clone()),
210                                 Data::String("Prophet Velen".to_string()),
211                                 Hash(hearthstone)]));
212
213        let hash2 = HashBuilder::new()
214            .set_partials_path("/hearthstone")
215            .insert("first_name", "Anduin")
216            .insert("last_name", "Wrynn")
217            .insert("age", 21i32)
218            .insert("weight", 120.16f64)
219            .insert("class", "Priest")
220            .insert("died", false)
221            .insert("class_cards",
222                    VecBuilder::new()
223                        .push(test_string)
224                        .push("Prophet Velen")
225                        .push(HashBuilder::new()
226                            .insert("name", "Hearthstone: Heroes of Warcraft")
227                            .insert("release_date", "December, 2014")));
228
229        assert_eq!(Hash(hash1), Hash(hash2.data));
230        assert_eq!(hash2.partials_path, "/hearthstone");
231    }
232
233    // #[test]
234    // fn test_hash_lambda_builder() {
235    //     // Since we can't directly compare closures, just make
236    //     // sure we're threading through the builder
237
238    //     let mut num = 10u;
239    //     let data = HashBuilder::new()
240    //         .insert_lambda("double", |x| {
241    //             num *= 2u;
242    //             x + num.to_string()
243    //         })
244    //         .build();
245
246    //     match data {
247    //         Hash(m) => {
248    //             match *m.find_equiv(&("double")).unwrap() {
249    //                 Lambda(ref f) => {
250    //                     let f = &mut *f.borrow_mut();
251    //                     assert_eq!((*f)("double: ".to_string()), "double: 20".to_string());
252    //                     assert_eq!((*f)("double: ".to_string()), "double: 40".to_string());
253    //                     assert_eq!((*f)("double: ".to_string()), "double: 80".to_string());
254    //                 }
255    //                 _ => panic!(),
256    //             }
257    //         }
258    //         _ => panic!(),
259    //     }
260    // }
261
262    #[test]
263    fn test_vec_lambda_builder() {
264        // Since we can't directly compare closures, just make
265        // sure we're threading through the builder
266
267        let mut num = 10u32;
268        let mut f = |x: String| -> String {
269            num *= 2u32;
270            x + &num.to_string()[..]
271        };
272        let data = VecBuilder::new()
273            .push_lambda(&mut f)
274            .build();
275
276        match data {
277            Vector(m) => {
278                match m[0] {
279                    Lambda(ref f) => {
280                        let f = &mut *f.borrow_mut();
281                        assert_eq!((*f)("double: ".to_string()), "double: 20".to_string());
282                        assert_eq!((*f)("double: ".to_string()), "double: 40".to_string());
283                        assert_eq!((*f)("double: ".to_string()), "double: 80".to_string());
284                    }
285                    _ => panic!(),
286                }
287            }
288            _ => panic!(),
289        }
290    }
291}