1 | #![warn (missing_docs)] |
2 | #![warn (unused_results)] |
3 | #![doc (html_root_url="https://docs.rs/maplit/1/" )] |
4 | |
5 | //! Macros for container literals with specific type. |
6 | //! |
7 | //! ``` |
8 | //! #[macro_use] extern crate maplit; |
9 | //! |
10 | //! # fn main() { |
11 | //! let map = hashmap!{ |
12 | //! "a" => 1, |
13 | //! "b" => 2, |
14 | //! }; |
15 | //! # } |
16 | //! ``` |
17 | //! |
18 | //! The **maplit** 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 maplit; |
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 | /// ``` |
46 | macro_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 | /// Create a **HashSet** from a list of elements. |
64 | /// |
65 | /// ## Example |
66 | /// |
67 | /// ``` |
68 | /// #[macro_use] extern crate maplit; |
69 | /// # fn main() { |
70 | /// |
71 | /// let set = hashset!{"a" , "b" }; |
72 | /// assert!(set.contains("a" )); |
73 | /// assert!(set.contains("b" )); |
74 | /// assert!(!set.contains("c" )); |
75 | /// # } |
76 | /// ``` |
77 | #[macro_export (local_inner_macros)] |
78 | macro_rules! hashset { |
79 | (@single $($x:tt)*) => (()); |
80 | (@count $($rest:expr),*) => (<[()]>::len(&[$(hashset!(@single $rest)),*])); |
81 | |
82 | ($($key:expr,)+) => { hashset!($($key),+) }; |
83 | ($($key:expr),*) => { |
84 | { |
85 | let _cap = hashset!(@count $($key),*); |
86 | let mut _set = ::std::collections::HashSet::with_capacity(_cap); |
87 | $( |
88 | let _ = _set.insert($key); |
89 | )* |
90 | _set |
91 | } |
92 | }; |
93 | } |
94 | |
95 | #[macro_export (local_inner_macros)] |
96 | /// Create a **BTreeMap** from a list of key-value pairs |
97 | /// |
98 | /// ## Example |
99 | /// |
100 | /// ``` |
101 | /// #[macro_use] extern crate maplit; |
102 | /// # fn main() { |
103 | /// |
104 | /// let map = btreemap!{ |
105 | /// "a" => 1, |
106 | /// "b" => 2, |
107 | /// }; |
108 | /// assert_eq!(map["a" ], 1); |
109 | /// assert_eq!(map["b" ], 2); |
110 | /// assert_eq!(map.get("c" ), None); |
111 | /// # } |
112 | /// ``` |
113 | macro_rules! btreemap { |
114 | // trailing comma case |
115 | ($($key:expr => $value:expr,)+) => (btreemap!($($key => $value),+)); |
116 | |
117 | ( $($key:expr => $value:expr),* ) => { |
118 | { |
119 | let mut _map = ::std::collections::BTreeMap::new(); |
120 | $( |
121 | let _ = _map.insert($key, $value); |
122 | )* |
123 | _map |
124 | } |
125 | }; |
126 | } |
127 | |
128 | #[macro_export (local_inner_macros)] |
129 | /// Create a **BTreeSet** from a list of elements. |
130 | /// |
131 | /// ## Example |
132 | /// |
133 | /// ``` |
134 | /// #[macro_use] extern crate maplit; |
135 | /// # fn main() { |
136 | /// |
137 | /// let set = btreeset!{"a" , "b" }; |
138 | /// assert!(set.contains("a" )); |
139 | /// assert!(set.contains("b" )); |
140 | /// assert!(!set.contains("c" )); |
141 | /// # } |
142 | /// ``` |
143 | macro_rules! btreeset { |
144 | ($($key:expr,)+) => (btreeset!($($key),+)); |
145 | |
146 | ( $($key:expr),* ) => { |
147 | { |
148 | let mut _set = ::std::collections::BTreeSet::new(); |
149 | $( |
150 | _set.insert($key); |
151 | )* |
152 | _set |
153 | } |
154 | }; |
155 | } |
156 | |
157 | /// Identity function. Used as the fallback for conversion. |
158 | #[doc (hidden)] |
159 | pub fn __id<T>(t: T) -> T { t } |
160 | |
161 | /// Macro that converts the keys or key-value pairs passed to another maplit |
162 | /// macro. The default conversion is to use the [`Into`] trait, if no |
163 | /// custom conversion is passed. |
164 | /// |
165 | /// The syntax is: |
166 | /// |
167 | /// `convert_args!(` `keys=` *function* `,` `values=` *function* `,` |
168 | /// *macro_name* `!(` [ *key* => *value* [, *key* => *value* ... ] ] `))` |
169 | /// |
170 | /// Here *macro_name* is any other maplit macro and either or both of the |
171 | /// explicit `keys=` and `values=` parameters can be omitted. |
172 | /// |
173 | /// [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html |
174 | /// |
175 | /// **Note** To use `convert_args`, the macro that is being wrapped |
176 | /// must itself be brought into the current scope with `#[macro_use]` or `use`. |
177 | /// |
178 | /// # Examples |
179 | /// |
180 | /// ``` |
181 | /// #[macro_use] extern crate maplit; |
182 | /// # fn main() { |
183 | /// |
184 | /// use std::collections::HashMap; |
185 | /// use std::collections::BTreeSet; |
186 | /// |
187 | /// // a. Use the default conversion with the Into trait. |
188 | /// // Here this converts both the key and value string literals to `String`, |
189 | /// // but we need to specify the map type exactly! |
190 | /// |
191 | /// let map1: HashMap<String, String> = convert_args!(hashmap!( |
192 | /// "a" => "b" , |
193 | /// "c" => "d" , |
194 | /// )); |
195 | /// |
196 | /// // b. Specify an explicit custom conversion for the keys. If we don't specify |
197 | /// // a conversion for the values, they are not converted at all. |
198 | /// |
199 | /// let map2 = convert_args!(keys=String::from, hashmap!( |
200 | /// "a" => 1, |
201 | /// "c" => 2, |
202 | /// )); |
203 | /// |
204 | /// // Note: map2 is a HashMap<String, i32>, but we didn't need to specify the type |
205 | /// let _: HashMap<String, i32> = map2; |
206 | /// |
207 | /// // c. convert_args! works with all the maplit macros -- and macros from other |
208 | /// // crates that have the same "signature". |
209 | /// // For example, btreeset and conversion from &str to Vec<u8>. |
210 | /// |
211 | /// let set: BTreeSet<Vec<u8>> = convert_args!(btreeset!( |
212 | /// "a" , "b" , "c" , "d" , "a" , "e" , "f" , |
213 | /// )); |
214 | /// assert_eq!(set.len(), 6); |
215 | /// |
216 | /// |
217 | /// # } |
218 | /// ``` |
219 | #[macro_export (local_inner_macros)] |
220 | macro_rules! convert_args { |
221 | (keys=$kf:expr, $macro_name:ident !($($k:expr),* $(,)*)) => { |
222 | $macro_name! { $(($kf)($k)),* } |
223 | }; |
224 | (keys=$kf:expr, values=$vf:expr, $macro_name:ident !($($k:expr),* $(,)*)) => { |
225 | $macro_name! { $(($kf)($k)),* } |
226 | }; |
227 | (keys=$kf:expr, values=$vf:expr, $macro_name:ident !( $($k:expr => $v:expr),* $(,)*)) => { |
228 | $macro_name! { $(($kf)($k) => ($vf)($v)),* } |
229 | }; |
230 | (keys=$kf:expr, $macro_name:ident !($($rest:tt)*)) => { |
231 | convert_args! { |
232 | keys=$kf, values=$crate::__id, |
233 | $macro_name !( |
234 | $($rest)* |
235 | ) |
236 | } |
237 | }; |
238 | (values=$vf:expr, $macro_name:ident !($($rest:tt)*)) => { |
239 | convert_args! { |
240 | keys=$crate::__id, values=$vf, |
241 | $macro_name !( |
242 | $($rest)* |
243 | ) |
244 | } |
245 | }; |
246 | ($macro_name:ident ! $($rest:tt)*) => { |
247 | convert_args! { |
248 | keys=::std::convert::Into::into, values=::std::convert::Into::into, |
249 | $macro_name ! |
250 | $($rest)* |
251 | } |
252 | }; |
253 | } |
254 | |
255 | #[test ] |
256 | fn test_hashmap() { |
257 | use std::collections::HashMap; |
258 | use std::collections::HashSet; |
259 | let names = hashmap!{ |
260 | 1 => "one" , |
261 | 2 => "two" , |
262 | }; |
263 | assert_eq!(names.len(), 2); |
264 | assert_eq!(names[&1], "one" ); |
265 | assert_eq!(names[&2], "two" ); |
266 | assert_eq!(names.get(&3), None); |
267 | |
268 | let empty: HashMap<i32, i32> = hashmap!{}; |
269 | assert_eq!(empty.len(), 0); |
270 | |
271 | let _nested_compiles = hashmap!{ |
272 | 1 => hashmap!{0 => 1 + 2,}, |
273 | 2 => hashmap!{1 => 1,}, |
274 | }; |
275 | |
276 | let _: HashMap<String, i32> = convert_args!(keys=String::from, hashmap!( |
277 | "one" => 1, |
278 | "two" => 2, |
279 | )); |
280 | |
281 | let _: HashMap<String, i32> = convert_args!(keys=String::from, values=__id, hashmap!( |
282 | "one" => 1, |
283 | "two" => 2, |
284 | )); |
285 | |
286 | let names: HashSet<String> = convert_args!(hashset!( |
287 | "one" , |
288 | "two" , |
289 | )); |
290 | assert!(names.contains("one" )); |
291 | assert!(names.contains("two" )); |
292 | |
293 | let lengths: HashSet<usize> = convert_args!(keys=str::len, hashset!( |
294 | "one" , |
295 | "two" , |
296 | )); |
297 | assert_eq!(lengths.len(), 1); |
298 | |
299 | let _no_trailing: HashSet<usize> = convert_args!(keys=str::len, hashset!( |
300 | "one" , |
301 | "two" |
302 | )); |
303 | } |
304 | |
305 | #[test ] |
306 | fn test_btreemap() { |
307 | use std::collections::BTreeMap; |
308 | let names: BTreeMap = btreemap!{ |
309 | 1 => "one" , |
310 | 2 => "two" , |
311 | }; |
312 | assert_eq!(names.len(), 2); |
313 | assert_eq!(names[&1], "one" ); |
314 | assert_eq!(names[&2], "two" ); |
315 | assert_eq!(names.get(&3), None); |
316 | |
317 | let empty: BTreeMap<i32, i32> = btreemap!{}; |
318 | assert_eq!(empty.len(), 0); |
319 | |
320 | let _nested_compiles: BTreeMap> = btreemap!{ |
321 | 1 => btreemap!{0 => 1 + 2,}, |
322 | 2 => btreemap!{1 => 1,}, |
323 | }; |
324 | } |
325 | |