cute/
lib.rs

1#![doc(html_logo_url = "https://raw.githubusercontent.com/mattgathu/cute/master/C!.png")]
2//! A Macro for python-esque list and dictionary(hashmap) comprehensions in Rust
3//!
4//! The `c!` macro implements list and hashmap comprehensions similar to those found in Python,
5//! allowing for conditionals and nested comprehensions.
6//!
7//! # Python Syntax
8//! ```
9//! squares = [x*x for x in range(10)]
10//!
11//! even_squares = [x*x for x in range(10) if x % 2 == 0]
12//!
13//! squares_dict = {key:key*key for key in range(10)}
14//! ```
15//!
16//! # c! Syntax
17//! ```
18//! #[macro_use(c)]
19//! extern crate cute;
20//!
21//! let squares = c![x*x, for x in 0..10];
22//!
23//! let even_squares = c![x*x, for x in 0..10, if x % 2 == 0];
24//!
25//! let squares_hashmap = c!{key => key*key, for key in 0..10};
26//!
27//! ```
28//!
29//! `c!`'s has the comprehension's parts, comma-separated.
30//!
31//! # Examples
32//!
33//! Simple comprehension
34//!
35//! ```
36//! let v = [1,2,3,4];
37//! let v_squared = c![x*x, for x in v];
38//!
39//! ```
40//! Conditional filtering
41//!
42//! ```
43//! let squares = c![x*x, for x in 0..10, if x % 2 == 0];
44//! assert_eq!(squares, vec![0, 4, 16, 36, 64]);
45//! ```
46//!
47//! Nested Comprehensions
48//!
49//! ```
50//! let nested = vec![vec![1,2,3], vec![4,5,6], vec![7,8,9]];
51//! let flat: Vec<usize> = c![x, for x in y, for y in nested];
52//! assert_eq!(flat, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
53//! ```
54//!
55//! ```
56//! let nested = vec![vec![1,2,3], vec![4,5,6], vec![7,8,9]];
57//! let even_flat: Vec<usize> = c![x, for x in y, for y in nested, if x % 2 == 0];
58//! assert_eq!(even_flat, vec![2, 4, 6, 8]);
59//! ```
60//!
61//! Comprehensions over Iterators
62//!
63//! ```
64//! let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
65//! let output: Vec<i32> = c![x*2, for x in vec.iter()];
66//! assert_eq!(output, vec![-8, -4, 0, 4, 8]);
67//! ```
68//!
69//! ```
70//! let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
71//! let output: Vec<i32> = c![x, for x in vec.iter(), if *x >= 0i32];
72//! assert_eq!(output, vec![0, 2, 4]);
73//! ```
74//!
75//! Function Application
76//!
77//! ```
78//! let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
79//! let output: Vec<i32> = c![x.abs(), for x in vec.iter()];
80//! assert_eq!(output, vec![4, 2, 0, 2, 4]);
81//! ```
82//!
83//! ```
84//! fn square(x:i32) -> i32 {
85//!        x*x
86//! }
87//!
88//! let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
89//! let squares: Vec<i32> = c![square(x), for x in vec];
90//! assert_eq!(squares, vec![16, 4, 0, 4, 16]);
91//! ```
92//!
93//! Hashmap Comprehensions
94//!
95//! ```
96//! let v = vec!["one", "two", "three"];
97//! let map = c!{key => key.to_uppercase(), for key in v};
98//!
99//! let mut expected: HashMap<&str, String> = HashMap::new();
100//! expected.insert("one", String::from("ONE"));
101//! expected.insert("two", String::from("TWO"));
102//! expected.insert("three", String::from("THREE"));
103//!
104//! assert_eq!(map, expected);
105//! ```
106//!
107//! ```
108//! let v: Vec<(&str, i32)> = vec![("one", 1), ("two", 2), ("three", 3)];
109//! let map = c!{key => val, for (key, val) in v};
110//!
111//! let mut expected: HashMap<&str, i32> = HashMap::new();
112//! expected.insert("one", 1);
113//! expected.insert("two", 2);
114//! expected.insert("three", 3);
115//!
116//! assert_eq!(map, expected);
117//! ```
118//!
119//! ```
120//! // conditional hashmap comprehension
121//! let v: Vec<(&str, i32)> = vec![("one", 1), ("two", 2), ("three", 3)];
122//! let map = c! {key => val, for (key, val) in v, if val == 1 || val == 2};
123//!
124//! let mut expected: HashMap<&str, i32> = HashMap::new();
125//! expected.insert("one", 1);
126//! expected.insert("two", 2);
127//!
128//! assert_eq!(map, expected);
129//! ```
130//!
131//! ```
132//! // conditional hashmap comprehension from an Iterator
133//! let map = c! {*key => key*key, for key in vec![1,2].iter(), if *key % 2 == 0};
134//! let mut e: HashMap<i32, i32> = HashMap::new();
135//! e.insert(2, 4);
136//!
137//! assert_eq!(map, e);
138//! ```
139
140
141#[macro_export]
142macro_rules! c {
143
144    ($exp:expr, for $i:ident in $iter:expr) => (
145        {
146            let mut r = vec![];
147            for $i in $iter {
148                r.push($exp);
149            }
150            r
151        }
152    );
153
154    ($exp:expr, for $i:ident in $iter:expr, if $cond:expr) => (
155        {
156            let mut r = vec![];
157            for $i in $iter {
158                if $cond {
159                    r.push($exp.clone());
160                }
161            }
162            r
163        }
164    );
165
166    ($exp:expr, for $i:ident in $iter:expr, for $i2:ident in $iter2:expr) => (
167        {
168            let mut r = vec![];
169            for $i2 in $iter2 {
170                for $i in $iter {
171                    r.push($exp);
172                }
173            }
174            r
175        }
176    );
177
178    ($exp:expr, for $i:ident in $iter:expr, for $i2:ident in $iter2:expr, if $cond:expr) => (
179        {
180            let mut r = vec![];
181            for $i2 in $iter2 {
182                for $i in $iter {
183                    if $cond{
184                        r.push($exp);
185                    }
186                }
187            }
188            r
189        }
190    );
191
192    ($key:expr => $val:expr, for $p:pat in $iter:expr) => (
193        {
194            use std::collections::HashMap;
195            let mut map = HashMap::new();
196            for $p in $iter {
197                map.insert($key, $val);
198            }
199            map
200        }
201    );
202
203    ($key:expr => $val:expr, for $p:pat in $iter:expr, if $cond:expr) => (
204        {
205            use std::collections::HashMap;
206            let mut map = HashMap::new();
207            for $p in $iter {
208                if $cond {
209                    map.insert($key, $val);
210                }
211            }
212            map
213        }
214    );
215
216    ($key:expr => $val:expr, for $i:ident in $iter:expr) => (
217        {
218            use std::collections::HashMap;
219            let mut map = HashMap::new();
220            for $i in $iter {
221                map.insert($key, $val);
222            }
223            map
224        }
225    );
226
227    ($key:expr => $val:expr, for $i:ident in $iter:expr, if $cond:expr) => (
228        {
229            use std::collections::HashMap;
230            let mut map = HashMap::new();
231            for $i in $iter {
232                if $cond {
233                    map.insert($key, $val);
234                }
235            }
236            map
237        }
238    );
239}
240
241#[cfg(test)]
242mod tests {
243    use std::collections::HashMap;
244    #[test]
245    fn simple_comprehension() {
246        let squares: Vec<usize> = c![x*x, for x in 0..10];
247        assert_eq!(squares, vec![0, 1, 4, 9, 16, 25, 36, 49, 64, 81]);
248    }
249
250    #[test]
251    fn filter_comprehension() {
252        let squares = c![x*x, for x in 0..10, if x % 2 == 0];
253        assert_eq!(squares, vec![0, 4, 16, 36, 64]);
254    }
255
256    #[test]
257    fn simple_nested_comprehension() {
258        let nested = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
259        let flat: Vec<usize> = c![x, for x in y, for y in nested];
260        assert_eq!(flat, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
261    }
262
263    #[test]
264    fn filter_nested_comprehension() {
265        let nested = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
266        let even_flat: Vec<usize> = c![x, for x in y, for y in nested, if x % 2 == 0];
267        assert_eq!(even_flat, vec![2, 4, 6, 8]);
268    }
269
270
271    #[test]
272    fn vector_to_iter_comprehension() {
273        let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
274        let output: Vec<i32> = c![x*2, for x in vec.iter()];
275        assert_eq!(output, vec![-8, -4, 0, 4, 8]);
276    }
277
278    #[test]
279    fn filter_comprehension_two() {
280        let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
281        let output: Vec<i32> = c![x, for x in vec.iter(), if *x >= 0i32];
282        assert_eq!(output, vec![0, 2, 4]);
283    }
284
285    #[test]
286    fn apply_function_comprehension() {
287        let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
288        let output: Vec<i32> = c![x.abs(), for x in vec.iter()];
289        assert_eq!(output, vec![4, 2, 0, 2, 4]);
290    }
291
292    #[test]
293    fn apply_user_defined_function() {
294        fn square(x: i32) -> i32 {
295            x * x
296        }
297
298        let vec: Vec<i32> = vec![-4, -2, 0, 2, 4];
299        let squares: Vec<i32> = c![square(x), for x in vec];
300        assert_eq!(squares, vec![16, 4, 0, 4, 16]);
301    }
302
303    #[test]
304    fn hashmap_comprehension() {
305        let v = vec!["one", "two", "three"];
306        let map = c! {key => key.to_uppercase(), for key in v};
307        let mut expected: HashMap<&str, String> = HashMap::new();
308        expected.insert("one", String::from("ONE"));
309        expected.insert("two", String::from("TWO"));
310        expected.insert("three", String::from("THREE"));
311
312        assert_eq!(map, expected);
313    }
314
315    #[test]
316    fn hashmap_comprehension_two() {
317        let v = vec!["one", "two", "three"];
318        let map = c! {format!("{}-true", key) => key.to_uppercase(), for key in v};
319        let mut expected: HashMap<String, String> = HashMap::new();
320        expected.insert(String::from("one-true"), String::from("ONE"));
321        expected.insert(String::from("two-true"), String::from("TWO"));
322        expected.insert(String::from("three-true"), String::from("THREE"));
323
324        assert_eq!(map, expected);
325    }
326
327    #[test]
328    fn hashmap_comprehension_three() {
329        let v: Vec<(String, i32)> = vec![(String::from("one"), 1), (String::from("two"), 2), (String::from("three"), 3)];
330        let map = c! {key => val, for (key, val) in v};
331
332        let mut expected: HashMap<String, i32> = HashMap::new();
333        expected.insert(String::from("one"), 1);
334        expected.insert(String::from("two"), 2);
335        expected.insert(String::from("three"), 3);
336
337        assert_eq!(map, expected);
338    }
339
340
341    #[test]
342    fn hashmap_tuple_comprehension() {
343        let v: Vec<(&str, i32)> = vec![("one", 1), ("two", 2), ("three", 3)];
344        let map = c! {key => val, for (key, val) in v};
345
346        let mut expected: HashMap<&str, i32> = HashMap::new();
347        expected.insert("one", 1);
348        expected.insert("two", 2);
349        expected.insert("three", 3);
350
351        assert_eq!(map, expected);
352    }
353
354    #[test]
355    fn conditional_hashmap_tuple_comprehension() {
356        let v: Vec<(&str, i32)> = vec![("one", 1), ("two", 2), ("three", 3)];
357        let map = c! {key => val, for (key, val) in v, if val == 1 || val == 2};
358
359        let mut expected: HashMap<&str, i32> = HashMap::new();
360        expected.insert("one", 1);
361        expected.insert("two", 2);
362
363        assert_eq!(map, expected);
364    }
365
366    #[test]
367    fn hashmap_from_iter() {
368        let map = c! {*key => key*key, for key in vec![1,2].iter()};
369        let mut e: HashMap<i32, i32> = HashMap::new();
370        e.insert(1, 1);
371        e.insert(2, 4);
372
373        assert_eq!(map, e);
374    }
375
376    #[test]
377    fn conditional_hashmap_from_iter() {
378        let map = c! {*key => key*key, for key in vec![1,2].iter(), if *key % 2 == 0};
379        let mut e: HashMap<i32, i32> = HashMap::new();
380        e.insert(2, 4);
381
382        assert_eq!(map, e);
383    }
384
385    #[test]
386    fn hashmap_from_range() {
387        let map = c! {key => key*key, for key in 1..3};
388        let mut e: HashMap<i32, i32> = HashMap::new();
389        e.insert(1, 1);
390        e.insert(2, 4);
391
392        assert_eq!(map, e);
393    }
394
395    #[test]
396    fn conditional_hashmap_from_range() {
397        let map = c! {key => key*key, for key in 1..6, if key % 2 == 1};
398        let mut e: HashMap<i32, i32> = HashMap::new();
399        e.insert(1, 1);
400        e.insert(3, 9);
401        e.insert(5, 25);
402
403        assert_eq!(map, e);
404    }
405}