colmac/
lib.rs

1//! Macros to work with `std::collections` that are mostly straight-forward syntactic sugars.
2pub use std::collections::HashMap;
3pub use std::collections::HashSet;
4
5/// Counts the number of `args` passed to this macro invocation.
6///
7/// Returns the count as `usize`.
8///
9/// # Examples
10///
11/// ```
12/// #[macro_use] extern crate colmac;
13///
14/// assert_eq!(0, count_args!());
15/// assert_eq!(1, count_args!("one"));
16/// assert_eq!(3, count_args!("one", "two", "three"));
17/// ```
18#[macro_export]
19macro_rules! count_args {
20    // base cases
21    () => {
22        0usize
23    };
24    ( $arg:expr ) => {
25        1usize
26    };
27    // recurse
28    ( $arg:expr, $( $rest:expr ),* ) => {
29        1usize + count_args!( $( $rest ),* )
30    };
31}
32
33/// Sugar for `String::new` and `String::from`.
34///
35/// # Examples
36///
37/// ```
38/// #[macro_use] extern crate colmac;
39///
40/// // create an empty string
41/// assert_eq!(String::new(), string!());
42///
43/// // syntactically identical
44/// assert_eq!(String::from("abc"), string!("abc"));
45/// ```
46#[macro_export]
47macro_rules! string {
48    () => {
49        String::new()
50    };
51    ( $arg:expr ) => {
52        String::from($arg)
53    };
54}
55
56/// Concatenate multiple sliceable structs like `Vec` or `String`.
57///
58/// Does not mutate the input structs.
59///
60/// ```
61/// #[macro_use] extern crate colmac;
62///
63/// let a = vec![1, 2];
64/// let b = vec![3, 4];
65/// let c = vec![5, 6];
66///
67/// assert_eq!(concat_vec![a, b], vec![1, 2, 3, 4]);
68/// assert_eq!(concat_vec![a, c, b], vec![1, 2, 5, 6, 3, 4]);
69/// ```
70#[macro_export]
71macro_rules! concat_vec {
72    ( $( $sliceable:expr ),* ) => {{
73        [
74            $(
75                &$sliceable[..],
76            )*
77        ].concat()
78    }};
79}
80
81/// Just like `vec!`, but for `std::collections::HashMap`.
82///
83/// This macro uses `count_args!` to preallocate the exact amount of memory
84/// needed, so it's more efficient than simply iteratively inserting.
85///
86/// ```
87/// #[macro_use] extern crate colmac;
88///
89/// use std::collections::HashMap;
90///
91/// // create an empty one
92/// let empty: HashMap<u64, u64> = hashmap![];
93/// assert_eq!(0, empty.len());
94///
95/// // literal initialization
96/// let mut map_a = HashMap::new();
97/// map_a.insert("a", 123);
98/// map_a.insert("b", 456);
99///
100/// let map_b = hashmap!["a" => 123, "b" => 456];
101/// assert_eq!(map_a, map_b);
102/// ```
103#[macro_export]
104macro_rules! hashmap {
105    () => {
106        HashMap::new()
107    };
108    ( $( $key:expr => $value:expr ),* ) => {{
109        let size = count_args!( $( $key ),* );
110        let mut map = HashMap::with_capacity(size);
111        $(
112            map.insert($key, $value);
113        )*
114        map
115    }};
116}
117
118/// Just like `vec!`, but for `std::collections::HashSet`.
119///
120/// This macro uses `count_args!` to preallocate the exact amount of memory
121/// needed, so it's more efficient than simply iteratively inserting.
122///
123/// ```
124/// #[macro_use] extern crate colmac;
125///
126/// use std::collections::HashSet;
127///
128/// // create an empty one
129/// let empty: HashSet<u64> = hashset![];
130/// assert_eq!(0, empty.len());
131///
132/// // literal initialization
133/// let mut set_a = HashSet::new();
134/// set_a.insert(123);
135/// set_a.insert(456);
136///
137/// let set_b = hashset!(123, 456);
138/// assert_eq!(set_a, set_b);
139/// ```
140#[macro_export]
141macro_rules! hashset {
142    () => {
143        HashSet::new()
144    };
145    ( $( $elem:expr ),* ) => {{
146        let size = count_args!( $($elem),* );
147        let mut set = HashSet::with_capacity(size);
148        $(
149            set.insert($elem);
150        )*
151        set
152    }};
153}
154
155/// Sorts the input collection that impl's the trait `std::ops::IndexMut`.
156///
157/// There are two ways to invoke this macro:
158/// 1. with one argument, a mutable collection
159///     1. uses [`slice::sort_unstable`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable) to sort
160/// 1. with two arguments, a mutable collection followed by a closure
161///     1. passes the closure to [`slice::sort_unstable_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable_by) to sort
162///
163/// # Examples
164///
165/// ```
166/// #[macro_use] extern crate colmac;
167/// use std::cmp::Ordering::{Equal, Greater, Less};
168///
169/// // sort without a custom closure
170/// let mut v1 = vec![2, 4, -1];
171/// sort!(v1);
172/// assert_eq!(vec![-1, 2, 4], v1);
173///
174/// // sort with; sort in reverse order
175/// let mut v2 = vec![2, 4, -1];
176/// sort!(v2, |a, b| match a.cmp(b) {
177///     Less => Greater,
178///     Greater => Less,
179///     Equal => Equal,
180/// });
181/// assert_eq!(vec![4, 2, -1], v2);
182/// ```
183#[macro_export]
184macro_rules! sort {
185    ( $collection:expr ) => {
186        (&mut $collection[..]).sort_unstable();
187    };
188    ( $collection:expr, $compare_fn:expr ) => {
189        (&mut $collection[..]).sort_unstable_by($compare_fn);
190    };
191}
192
193/// Creates a sorted `Vec` with cloned elements of the input collection, leaving the original
194/// collection untouched.
195///
196/// The input collection should support `.iter()` method that returns an `Iterator` over its
197/// elemnts.
198///
199/// There are two ways to invoke this macro:
200/// 1. with one argument, a mutable collection
201///     1. uses [`slice::sort_unstable`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable) to sort
202/// 1. with two arguments, a mutable collection followed by a closure
203///     1. passes the closure to [`slice::sort_unstable_by`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_unstable_by) to sort
204///
205/// # Examples
206///
207/// ```
208/// #[macro_use] extern crate colmac;
209/// use std::cmp::Ordering::{Equal, Greater, Less};
210///
211/// // sort without a custom closure
212/// let v1 = vec![2, 4, -1];
213/// let v1_sorted = sorted!(v1);
214/// assert_eq!(vec![2, 4, -1], v1);  // v1 is not modified
215/// assert_eq!(vec![-1, 2, 4], v1_sorted);
216///
217/// // sort with; sort in reverse order
218/// let v2 = vec![2, 4, -1];
219/// let v2_sorted = sorted!(v2, |a, b| match a.cmp(b) {
220///     Less => Greater,
221///     Greater => Less,
222///     Equal => Equal,
223/// });
224/// assert_eq!(vec![2, 4, -1], v2);  // v2 is not modified
225/// assert_eq!(vec![4, 2, -1], v2_sorted);
226/// ```
227#[macro_export]
228macro_rules! sorted {
229    ( $collection:expr ) => {{
230        let mut clones: Vec<_> = $collection.iter().cloned().collect();
231        sort!(clones);
232        clones
233    }};
234    ( $collection:expr, $compare_fn:expr ) => {{
235        let mut clones: Vec<_> = $collection.iter().cloned().collect();
236        sort!(clones, $compare_fn);
237        clones
238    }};
239}
240
241/// Creates a sorted `Vec<f64>` from the input.
242///
243/// This is a syntactic sugar for calling `sorted!` with the closure
244/// `|a, b| a.partial_cmp(b).unwrap()`.
245///
246/// # Examples
247///
248/// ```
249/// #[macro_use] extern crate colmac;
250/// # use std::cmp::Ordering::{Equal, Greater, Less};
251///
252/// // sort some collection that impl's `IntoIterator`
253/// let vec = vec![2.0, 4.0, -1.0];
254///
255/// let sorted_vec = sorted_f64!(vec);
256/// let expected = vec![-1.0, 2.0, 4.0];
257///
258/// assert_eq!(expected, sorted_vec);
259/// ```
260#[macro_export]
261macro_rules! sorted_f64 {
262    ( $collection:expr ) => {
263        sorted!($collection, |a, b| a.partial_cmp(b).unwrap())
264    };
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270    use std::cmp::Ordering::{Equal, Greater, Less};
271
272    mod count_args {
273        use super::*;
274
275        #[test]
276        fn zero() {
277            let expected = 0;
278            let result = count_args!();
279            assert_eq!(expected, result);
280        }
281        #[test]
282        fn one() {
283            let expected = 1;
284            let result = count_args!(10);
285            assert_eq!(expected, result);
286        }
287        #[test]
288        fn many() {
289            let expected = 4;
290            let result = count_args!(10, 20, 30, 40);
291            assert_eq!(expected, result);
292        }
293    }
294
295    mod concat_vec {
296        #[test]
297        fn one() {
298            let a = vec![1, 2];
299
300            let expected = vec![1, 2];
301            let result = concat_vec![a];
302            assert_eq!(expected, result);
303        }
304
305        #[test]
306        fn two() {
307            let a = vec![1, 2];
308            let b = vec![3, 4];
309
310            let expected = vec![1, 2, 3, 4];
311            let result = concat_vec![a, b];
312            assert_eq!(expected, result);
313        }
314
315        #[test]
316        fn many() {
317            let a = vec![1, 2];
318            let b = vec![3, 4];
319            let c = vec![5, 6];
320
321            let expected = vec![1, 2, 5, 6, 3, 4];
322            let result = concat_vec![a, c, b];
323            assert_eq!(expected, result);
324        }
325    }
326
327    mod hashmap {
328        use super::*;
329
330        #[test]
331        fn zero() {
332            let expected: HashMap<usize, usize> = HashMap::new();
333            let result: HashMap<usize, usize> = hashmap!();
334            assert_eq!(expected, result);
335        }
336        #[test]
337        fn one() {
338            let key = "abcde";
339            let expected: HashMap<String, usize> =
340                [(string!(key), 3usize)].iter().cloned().collect();
341            let result: HashMap<String, usize> = hashmap!(string!(key) => 3usize);
342            assert_eq!(expected, result);
343        }
344        #[test]
345        fn many() {
346            let expected: HashMap<String, usize> = vec![
347                (string!("a"), 10usize),
348                (string!("ab"), 20usize),
349                (string!("abc"), 30usize),
350            ]
351            .into_iter()
352            .collect();
353            let result = hashmap!(
354                string!("a") => 10usize,
355                string!("ab") => 20usize,
356                string!("abc") => 30usize
357            );
358            assert_eq!(expected, result);
359        }
360    }
361
362    mod hashset {
363        use super::*;
364
365        #[test]
366        fn zero() {
367            let expected: HashSet<usize> = HashSet::new();
368            let result: HashSet<usize> = hashset!();
369            assert_eq!(expected, result);
370        }
371        #[test]
372        fn one() {
373            let name = string!("Jack");
374            let expected: HashSet<String> = vec![&name].into_iter().cloned().collect();
375            let result: HashSet<String> = hashset!(name);
376            assert_eq!(expected, result);
377        }
378        #[test]
379        fn many() {
380            let expected: HashSet<&str> = vec!["a", "b", "c"].into_iter().collect();
381            let result: HashSet<&str> = hashset!("a", "b", "c");
382            assert_eq!(expected, result);
383        }
384    }
385
386    mod sort {
387        use super::*;
388
389        #[test]
390        fn vec() {
391            let expected = vec![-14, -1, 0, 2, 3, 4, 8];
392
393            let mut v = vec![4, 2, 3, -1, -14, 0, 8];
394            sort!(v);
395            assert_eq!(expected, v);
396        }
397        #[test]
398        fn array() {
399            let expected = vec![-14, -1, 0, 2, 3, 4, 8];
400
401            let mut v = [4, 2, 3, -1, -14, 0, 8];
402            sort!(v);
403            assert_eq!(expected, v);
404        }
405        #[test]
406        fn vec_reverse_sort() {
407            let expected = vec![8, 4, 3, 2, 0, -1, -14];
408
409            let mut v = vec![4, 2, 3, -1, -14, 0, 8];
410            sort!(v, |a, b| match a.cmp(b) {
411                Less => Greater,
412                Greater => Less,
413                Equal => Equal,
414            });
415            assert_eq!(expected, v);
416        }
417    }
418
419    mod sorted {
420        use super::*;
421
422        #[test]
423        fn vec() {
424            let expected = vec![-14, -1, 0, 2, 3, 4, 8];
425
426            let v = vec![4, 2, 3, -1, -14, 0, 8];
427            let result = sorted!(v);
428            assert_eq!(expected, result);
429            assert_eq!(vec![-14, -1, 0, 2, 3, 4, 8], expected); // unmodified
430        }
431        #[test]
432        fn hashset() {
433            let expected = vec![-14, -1, 0, 2, 3, 4, 8];
434
435            let v = hashset![4, 2, 3, -1, -14, 0, 8];
436            let result = sorted!(v);
437            assert_eq!(expected, result);
438        }
439        #[test]
440        fn array() {
441            let expected = vec![-14, -1, 0, 2, 3, 4, 8];
442
443            let v = [4, 2, 3, -1, -14, 0, 8];
444            let result = sorted!(v);
445            assert_eq!(expected, result);
446        }
447        #[test]
448        fn vec_reverse_sort() {
449            let expected = vec![8, 4, 3, 2, 0, -1, -14];
450
451            let v = vec![4, 2, 3, -1, -14, 0, 8];
452            let result = sorted!(v, |a, b| match a.cmp(b) {
453                Less => Greater,
454                Greater => Less,
455                Equal => Equal,
456            });
457            assert_eq!(expected, result);
458            assert_eq!(vec![8, 4, 3, 2, 0, -1, -14], expected); // unmodified
459        }
460    }
461
462    mod sorted_f64 {
463        use super::*;
464
465        #[test]
466        fn vec() {
467            let expected = vec![-14.0, -1.0, 0.0, 2.0, 3.0, 4.0, 8.0];
468
469            let v = vec![4.0, 2.0, 3.0, -1.0, -14.0, 0.0, 8.0];
470            let result = sorted_f64!(v);
471            assert_eq!(expected, result);
472        }
473        #[test]
474        fn array() {
475            let expected = vec![-14.0, -1.0, 0.0, 2.0, 3.0, 4.0, 8.0];
476
477            let v = [4.0, 2.0, 3.0, -1.0, -14.0, 0.0, 8.0];
478            let result = sorted_f64!(v);
479            assert_eq!(expected, result);
480        }
481    }
482}