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}