maplit2/
lib.rs

1#![warn(missing_docs)]
2#![warn(unused_results)]
3#![doc(html_root_url="https://docs.rs/maplit2/1/")]
4
5//! Macros for container literals with specific type.
6//!
7//! ```
8//! #[macro_use] extern crate maplit2;
9//!
10//! # fn main() {
11//! let map = hashmap!{
12//!     "a" => 1,
13//!     "b" => 2,
14//! };
15//! # }
16//! ```
17//!
18//! The **maplit2** crate uses `=>` syntax to separate the key and value for the
19//! mapping macros. (It was not possible to use `:` as separator due to syntactic
20//! restrictions in regular `macro_rules!` macros.)
21//!
22//! Note that rust macros are flexible in which brackets you use for the invocation.
23//! You can use them as `hashmap!{}` or `hashmap![]` or `hashmap!()`.
24//!
25//! Generic container macros already exist elsewhere, so those are not provided
26//! here at the moment.
27
28#[macro_export(local_inner_macros)]
29/// Create a **HashMap** from a list of key-value pairs
30///
31/// ## Example
32///
33/// ```
34/// #[macro_use] extern crate maplit2;
35/// # fn main() {
36///
37/// let map = hashmap!{
38///     "a" => 1,
39///     "b" => 2,
40/// };
41/// assert_eq!(map["a"], 1);
42/// assert_eq!(map["b"], 2);
43/// assert_eq!(map.get("c"), None);
44/// # }
45/// ```
46macro_rules! hashmap {
47    (@single $($x:tt)*) => (());
48    (@count $($rest:expr),*) => (<[()]>::len(&[$(hashmap!(@single $rest)),*]));
49
50    ($($key:expr => $value:expr,)+) => { hashmap!($($key => $value),+) };
51    ($($key:expr => $value:expr),*) => {
52        {
53            let _cap = hashmap!(@count $($key),*);
54            let mut _map = ::std::collections::HashMap::with_capacity(_cap);
55            $(
56                let _ = _map.insert($key, $value);
57            )*
58            _map
59        }
60    };
61}
62
63#[macro_export(local_inner_macros)]
64/// Create a **HashMap** with `specific type` from a list of key-value pairs
65///
66/// ## Example
67///
68/// ```
69/// #[macro_use] extern crate maplit;
70/// use std::collections::HashMap;
71/// struct Foo;
72/// struct Bar;
73///
74/// trait Zoo {}
75///
76/// impl Zoo for Foo {}
77/// impl Zoo for Bar {}
78///
79/// # fn main() {
80/// let map = hashmap_ex!(
81///     HashMap<_, Box<dyn Zoo>>,
82///     {
83///         "a" => Box::new(Foo {}),
84///         "b" => Box::new(Bar {}),
85///     }
86/// );
87/// # }
88/// ```
89macro_rules! hashmap_ex {
90    (@single $($x:tt)*) => (());
91    (@count $($rest:expr),*) => (<[()]>::len(&[$(hashmap_ex!(@single $rest)),*]));
92
93    ($t:ty, { $($key:expr => $value:expr,)+ } ) => { hashmap_ex!($t, { $($key => $value),+ }) };
94    ($t:ty, { $($key:expr => $value:expr),* } ) => {
95        {
96            let _cap = hashmap_ex!(@count $($key),*);
97            let mut _map: $t = ::std::collections::HashMap::with_capacity(_cap);
98            $(
99                let _ = _map.insert($key, $value);
100            )*
101            _map
102        }
103    };
104}
105
106#[macro_export(local_inner_macros)]
107/// A slightly more concise version of "hashmap!" for the concision-minded. Create a **HashMap** from a list of key-value pairs
108///
109/// ## Example
110///
111/// ```
112/// #[macro_use] extern crate maplit2;
113/// # fn main() {
114///
115/// let map = map!{
116///     "a": 1,
117///     "b": 2,
118/// };
119/// assert_eq!(map["a"], 1);
120/// assert_eq!(map["b"], 2);
121/// assert_eq!(map.get("c"), None);
122/// # }
123/// ```
124macro_rules! map{
125    ( $($key:tt : $val:expr),* $(,)? ) =>{{
126        #[allow(unused_mut)]
127        let mut map = ::std::collections::HashMap::with_capacity(hashmap!(@count $($key),* ));
128        $(
129            #[allow(unused_parens)]
130            let _ = map.insert($key, $val);
131        )*
132        map
133    }};
134    (@replace $_t:tt $e:expr ) => { $e };
135    (@count $($t:tt)*) => { <[()]>::len(&[$( map!(@replace $t ()) ),*]) }
136}
137
138#[macro_export(local_inner_macros)]
139/// Alias of "map!". Create a **HashMap** from a list of key-value pairs
140///
141/// ## Example
142///
143/// ```
144/// #[macro_use] extern crate maplit2;
145/// # fn main() {
146///
147/// let map = dict!{
148///     "a": 1,
149///     "b": 2,
150/// };
151/// assert_eq!(map["a"], 1);
152/// assert_eq!(map["b"], 2);
153/// assert_eq!(map.get("c"), None);
154/// # }
155/// ```
156macro_rules! dict{
157    ( $($key:tt : $val:expr),* $(,)? ) =>{{
158        #[allow(unused_mut)]
159        let mut map = ::std::collections::HashMap::with_capacity(hashmap!(@count $($key),* ));
160        $(
161            #[allow(unused_parens)]
162            let _ = map.insert($key, $val);
163        )*
164        map
165    }};
166    (@replace $_t:tt $e:expr ) => { $e };
167    (@count $($t:tt)*) => { <[()]>::len(&[$( map!(@replace $t ()) ),*]) }
168}
169
170/// Create a **HashSet** from a list of elements.
171///
172/// ## Example
173///
174/// ```
175/// #[macro_use] extern crate maplit2;
176/// # fn main() {
177///
178/// let set = hashset!{"a", "b"};
179/// assert!(set.contains("a"));
180/// assert!(set.contains("b"));
181/// assert!(!set.contains("c"));
182/// # }
183/// ```
184#[macro_export(local_inner_macros)]
185macro_rules! hashset {
186    (@single $($x:tt)*) => (());
187    (@count $($rest:expr),*) => (<[()]>::len(&[$(hashset!(@single $rest)),*]));
188
189    ($($key:expr,)+) => { hashset!($($key),+) };
190    ($($key:expr),*) => {
191        {
192            let _cap = hashset!(@count $($key),*);
193            let mut _set = ::std::collections::HashSet::with_capacity(_cap);
194            $(
195                let _ = _set.insert($key);
196            )*
197            _set
198        }
199    };
200}
201
202/// Alias of "hashset!". Create a **HashSet** from a list of elements.
203///
204/// ## Example
205///
206/// ```
207/// #[macro_use] extern crate maplit2;
208/// # fn main() {
209///
210/// let set = set!{"a", "b"};
211/// assert!(set.contains("a"));
212/// assert!(set.contains("b"));
213/// assert!(!set.contains("c"));
214/// # }
215/// ```
216#[macro_export(local_inner_macros)]
217macro_rules! set {
218    (@single $($x:tt)*) => (());
219    (@count $($rest:expr),*) => (<[()]>::len(&[$(hashset!(@single $rest)),*]));
220
221    ($($key:expr,)+) => { hashset!($($key),+) };
222    ($($key:expr),*) => {
223        {
224            let _cap = hashset!(@count $($key),*);
225            let mut _set = ::std::collections::HashSet::with_capacity(_cap);
226            $(
227                let _ = _set.insert($key);
228            )*
229            _set
230        }
231    };
232}
233
234#[macro_export(local_inner_macros)]
235/// Create a **BTreeMap** from a list of key-value pairs
236///
237/// ## Example
238///
239/// ```
240/// #[macro_use] extern crate maplit2;
241/// # fn main() {
242///
243/// let map = btreemap!{
244///     "a" => 1,
245///     "b" => 2,
246/// };
247/// assert_eq!(map["a"], 1);
248/// assert_eq!(map["b"], 2);
249/// assert_eq!(map.get("c"), None);
250/// # }
251/// ```
252macro_rules! btreemap {
253    // trailing comma case
254    ($($key:expr => $value:expr,)+) => (btreemap!($($key => $value),+));
255
256    ( $($key:expr => $value:expr),* ) => {
257        {
258            let mut _map = ::std::collections::BTreeMap::new();
259            $(
260                let _ = _map.insert($key, $value);
261            )*
262            _map
263        }
264    };
265}
266
267#[macro_export(local_inner_macros)]
268/// Create a **BTreeSet** from a list of elements.
269///
270/// ## Example
271///
272/// ```
273/// #[macro_use] extern crate maplit2;
274/// # fn main() {
275///
276/// let set = btreeset!{"a", "b"};
277/// assert!(set.contains("a"));
278/// assert!(set.contains("b"));
279/// assert!(!set.contains("c"));
280/// # }
281/// ```
282macro_rules! btreeset {
283    ($($key:expr,)+) => (btreeset!($($key),+));
284
285    ( $($key:expr),* ) => {
286        {
287            let mut _set = ::std::collections::BTreeSet::new();
288            $(
289                _set.insert($key);
290            )*
291            _set
292        }
293    };
294}
295
296/// Identity function. Used as the fallback for conversion.
297#[doc(hidden)]
298pub fn __id<T>(t: T) -> T { t }
299
300/// Macro that converts the keys or key-value pairs passed to another maplit2
301/// macro. The default conversion is to use the [`Into`] trait, if no
302/// custom conversion is passed.
303///
304/// The syntax is:
305///
306/// `convert_args!(` `keys=` *function* `,` `values=` *function* `,`
307///     *macro_name* `!(` [ *key* => *value* [, *key* => *value* ... ] ] `))`
308///
309/// Here *macro_name* is any other maplit2 macro and either or both of the
310/// explicit `keys=` and `values=` parameters can be omitted.
311///
312/// [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html
313///
314/// **Note** To use `convert_args`, the macro that is being wrapped
315/// must itself be brought into the current scope with `#[macro_use]` or `use`.
316///
317/// # Examples
318///
319/// ```
320/// #[macro_use] extern crate maplit2;
321/// # fn main() {
322///
323/// use std::collections::HashMap;
324/// use std::collections::BTreeSet;
325///
326/// // a. Use the default conversion with the Into trait.
327/// // Here this converts both the key and value string literals to `String`,
328/// // but we need to specify the map type exactly!
329///
330/// let map1: HashMap<String, String> = convert_args!(hashmap!(
331///     "a" => "b",
332///     "c" => "d",
333/// ));
334///
335/// // b. Specify an explicit custom conversion for the keys. If we don't specify
336/// // a conversion for the values, they are not converted at all.
337///
338/// let map2 = convert_args!(keys=String::from, hashmap!(
339///     "a" => 1,
340///     "c" => 2,
341/// ));
342///
343/// // Note: map2 is a HashMap<String, i32>, but we didn't need to specify the type
344/// let _: HashMap<String, i32> = map2;
345///
346/// // c. convert_args! works with all the maplit2 macros -- and macros from other
347/// // crates that have the same "signature".
348/// // For example, btreeset and conversion from &str to Vec<u8>.
349///
350/// let set: BTreeSet<Vec<u8>> = convert_args!(btreeset!(
351///     "a", "b", "c", "d", "a", "e", "f",
352/// ));
353/// assert_eq!(set.len(), 6);
354///
355///
356/// # }
357/// ```
358#[macro_export(local_inner_macros)]
359macro_rules! convert_args {
360    (keys=$kf:expr, $macro_name:ident !($($k:expr),* $(,)*)) => {
361        $macro_name! { $(($kf)($k)),* }
362    };
363    (keys=$kf:expr, values=$vf:expr, $macro_name:ident !($($k:expr),* $(,)*)) => {
364        $macro_name! { $(($kf)($k)),* }
365    };
366    (keys=$kf:expr, values=$vf:expr, $macro_name:ident !( $($k:expr => $v:expr),* $(,)*)) => {
367        $macro_name! { $(($kf)($k) => ($vf)($v)),* }
368    };
369    (keys=$kf:expr, $macro_name:ident !($($rest:tt)*)) => {
370        convert_args! {
371            keys=$kf, values=$crate::__id,
372            $macro_name !(
373                $($rest)*
374            )
375        }
376    };
377    (values=$vf:expr, $macro_name:ident !($($rest:tt)*)) => {
378        convert_args! {
379            keys=$crate::__id, values=$vf,
380            $macro_name !(
381                $($rest)*
382            )
383        }
384    };
385    ($macro_name:ident ! $($rest:tt)*) => {
386        convert_args! {
387            keys=::std::convert::Into::into, values=::std::convert::Into::into,
388            $macro_name !
389            $($rest)*
390        }
391    };
392}
393
394#[test]
395fn test_hashmap() {
396    use std::collections::HashMap;
397    use std::collections::HashSet;
398    let names = hashmap!{
399        1 => "one",
400        2 => "two",
401    };
402    assert_eq!(names.len(), 2);
403    assert_eq!(names[&1], "one");
404    assert_eq!(names[&2], "two");
405    assert_eq!(names.get(&3), None);
406
407    let empty: HashMap<i32, i32> = hashmap!{};
408    assert_eq!(empty.len(), 0);
409
410    let _nested_compiles = hashmap!{
411        1 => hashmap!{0 => 1 + 2,},
412        2 => hashmap!{1 => 1,},
413    };
414
415    let _: HashMap<String, i32> = convert_args!(keys=String::from, hashmap!(
416        "one" => 1,
417        "two" => 2,
418    ));
419
420    let _: HashMap<String, i32> = convert_args!(keys=String::from, values=__id, hashmap!(
421        "one" => 1,
422        "two" => 2,
423    ));
424
425    let names: HashSet<String> = convert_args!(hashset!(
426        "one",
427        "two",
428    ));
429    assert!(names.contains("one"));
430    assert!(names.contains("two"));
431
432    let lengths: HashSet<usize> = convert_args!(keys=str::len, hashset!(
433        "one",
434        "two",
435    ));
436    assert_eq!(lengths.len(), 1);
437
438    let _no_trailing: HashSet<usize> = convert_args!(keys=str::len, hashset!(
439        "one",
440        "two"
441    ));
442}
443
444#[test]
445fn test_hashmap_ex() {
446    use std::collections::HashMap;
447    let names: HashMap<i32, &str> = hashmap_ex!{
448        HashMap<i32, &str>,
449        {
450            1 => "one",
451            2 => "two",
452        }
453    };
454    assert_eq!(names.len(), 2);
455    assert_eq!(names[&1], "one");
456    assert_eq!(names[&2], "two");
457    assert_eq!(names.get(&3), None);
458
459    let empty: HashMap<i32, i32> = hashmap_ex!{
460        HashMap<i32, i32>, {}
461    };
462    assert_eq!(empty.len(), 0);
463
464    let _nested_compiles = hashmap_ex!{
465        HashMap<i32, _>,
466        {
467            1 => hashmap!{0 => 1 + 2,},
468            2 => hashmap!{1 => 1,},
469        }
470    };
471
472    struct Foo(i32);
473    struct Bar(i32);
474
475    trait Ret {
476        fn ret(&self) -> i32;
477    }
478
479    impl Ret for Foo {
480        fn ret(&self) -> i32 { self.0 }
481    }
482    impl Ret for Bar {
483        fn ret(&self) -> i32 { self.0 }
484    }
485
486    let func_map = hashmap_ex!(
487        HashMap<_, Box<dyn Ret>>,
488        {
489            "foo" => Box::new(Foo(1)),
490            "bar" => Box::new(Bar(2)),
491        }
492    );
493
494    assert_eq!(func_map["foo"].ret(), 1);
495    assert_eq!(func_map["bar"].ret(), 2);
496}
497
498#[test]
499fn test_btreemap() {
500    use std::collections::BTreeMap;
501    let names = btreemap!{
502        1 => "one",
503        2 => "two",
504    };
505    assert_eq!(names.len(), 2);
506    assert_eq!(names[&1], "one");
507    assert_eq!(names[&2], "two");
508    assert_eq!(names.get(&3), None);
509
510    let empty: BTreeMap<i32, i32> = btreemap!{};
511    assert_eq!(empty.len(), 0);
512
513    let _nested_compiles = btreemap!{
514        1 => btreemap!{0 => 1 + 2,},
515        2 => btreemap!{1 => 1,},
516    };
517}