stry_common/utils/fenn/hash.rs
1use core::{
2 borrow::Borrow,
3 hash::{BuildHasher, Hash},
4};
5
6/// Create a [`HashMap`](https://doc.rust-lang.org/nightly/std/collections/struct.HashMap.html)
7/// from a list of key-value pairs.
8///
9/// While based off of [maplit](https://github.com/bluss/maplit), this is an
10/// extended version with the ability to set the hasher and enable a `strict`
11/// mode.
12///
13/// # Examples
14///
15/// ```
16/// let map = fenn::hashmap! {
17/// "a" => 1,
18/// "b" => 2,
19/// };
20///
21/// assert_eq!(map["a"], 1);
22/// assert_eq!(map["b"], 2);
23/// assert_eq!(map.get("c"), None);
24/// ```
25///
26/// When `strict` mode is active, a duplicate key will cause a panic.
27///
28/// ```should_panic
29/// let map = fenn::hashmap! {
30/// strict,
31/// data = {
32/// "a" => 1,
33/// "a" => 2, // panics
34/// }
35/// };
36/// ```
37///
38/// To set the default hasher, pass in a hasher expression with the `hasher`,
39/// parameter.
40///
41/// ```
42/// use std::collections::hash_map::RandomState;
43///
44/// let map = fenn::hashmap! {
45/// hasher = RandomState::new(),
46/// data = {
47/// "a" => 1,
48/// "b" => 2,
49/// }
50/// };
51///
52/// assert_eq!(map["a"], 1);
53/// assert_eq!(map["b"], 2);
54/// assert_eq!(map.get("c"), None);
55/// ```
56///
57/// A custom hasher and strict mode can be used at the same time, the only
58/// requirement is that the hasher comes first.
59///
60/// ```should_panic
61/// use std::collections::hash_map::RandomState;
62///
63/// let map = fenn::hashmap! {
64/// hasher = RandomState::new(),
65/// strict,
66/// data = {
67/// "a" => 1,
68/// "a" => 2, // panics
69/// }
70/// };
71/// ```
72#[macro_export]
73macro_rules! hashmap {
74 (@single $($x:tt)*) => (());
75 (@count $($rest:expr),*) => (<[()]>::len(&[ $($crate::hashmap!(@single $rest)),* ]));
76
77 ( hasher = $hasher:expr, strict, data = { $( $key:expr => $value:expr, )+ } $( , )? ) => {{
78 $crate::hashmap!(hasher = $hasher, strict, data = { $( $key => $value ),+ })
79 }};
80 ( hasher = $hasher:expr, strict, data = { $( $key:expr => $value:expr ),* } $( , )? ) => {{
81 use $crate::{HashMapExt, Peep};
82 let _cap = $crate::hashmap!(@count $($key),*);
83 $crate::lib::HashMap::with_capacity_and_hasher(_cap, $hasher)
84 $(
85 .peep(|map| assert!(map.get(&$key).is_some()) )
86 .inserted($key, $value)
87 )*
88 }};
89
90 ( strict, data = { $( $key:expr => $value:expr, )+ } $( , )? ) => {{
91 $crate::hashmap!(strict, data = { $( $key => $value ),+ })
92 }};
93 ( strict, data = { $( $key:expr => $value:expr ),* } $( , )? ) => {{
94 use $crate::{HashMapExt, Peep};
95 let _cap = $crate::hashmap!(@count $($key),*);
96 $crate::lib::HashMap::with_capacity(_cap)
97 $(
98 .peep(|map| assert!(map.get(&$key).is_some()) )
99 .inserted($key, $value)
100 )*
101 }};
102
103 ( hasher = $hasher:expr, data = { $( $key:expr => $value:expr, )+ } $( , )? ) => {{
104 $crate::hashmap!(hasher = $hasher, data = { $( $key => $value ),+ })
105 }};
106 ( hasher = $hasher:expr, data = { $( $key:expr => $value:expr ),* } $( , )? ) => {{
107 use $crate::HashMapExt;
108 let _cap = $crate::hashmap!(@count $($key),*);
109 $crate::lib::HashMap::with_capacity_and_hasher(_cap, $hasher)
110 $(
111 .inserted($key, $value)
112 )*
113 }};
114
115 ( $( $key:expr => $value:expr, )+ ) => {{
116 $crate::hashmap!( $( $key => $value ),+ )
117 }};
118 ( $( $key:expr => $value:expr ),* ) => {{
119 use $crate::HashMapExt;
120 let _cap = $crate::hashmap!(@count $($key),*);
121 $crate::lib::HashMap::with_capacity(_cap)
122 $(
123 .inserted($key, $value)
124 )*
125 }};
126
127 () => {
128 $crate::lib::HashMap::new()
129 };
130}
131
132/// Extension trait that contains functions that allow for chaining of
133/// [`HashMap`](https://doc.rust-lang.org/nightly/std/collections/struct.HashMap.html)
134/// functions.
135///
136/// Before:
137///
138/// ```rust
139/// use std::collections::HashMap;
140///
141/// let mut map = HashMap::new();
142///
143/// map.insert(37, "a");
144/// map.insert(38, "b");
145///
146/// map.remove(&37);
147///
148/// assert_eq!(map.get(&37), None);
149/// assert_eq!(map.get(&38), Some(&"b"));
150/// ```
151///
152/// After:
153///
154/// ```rust
155/// use fenn::HashMapExt;
156/// use std::collections::HashMap;
157///
158/// let map = HashMap::new()
159/// .inserted(37, "a")
160/// .inserted(38, "b")
161/// .removed(&37);
162///
163/// assert_eq!(map.get(&37), None);
164/// assert_eq!(map.get(&38), Some(&"b"));
165/// ```
166pub trait HashMapExt<K, V, S> {
167 /// Clears the map, removing all key-value pairs. Keeps the allocated memory
168 /// for reuse.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use fenn::HashMapExt;
174 /// use std::collections::HashMap;
175 ///
176 /// let a = HashMap::new()
177 /// .inserted(1, "a")
178 /// .cleared();
179 ///
180 /// assert!(a.is_empty());
181 /// ```
182 fn cleared(self) -> Self;
183
184 /// Inserts a key-value pair into the map.
185 ///
186 /// If the map did have this key present, the value is updated. The key is
187 /// not updated, though; this matters for types that can be `==` without
188 /// being identical.
189 ///
190 /// # Warning
191 ///
192 /// Unlike the standard [`HashMap::insert`](https://doc.rust-lang.org/nightly/std/collections/struct.HashMap.html#method.insert)
193 /// that this wraps, this function ignores any returned values.
194 ///
195 /// # Examples
196 ///
197 /// ```
198 /// use fenn::HashMapExt;
199 /// use std::collections::HashMap;
200 ///
201 /// let map = HashMap::new()
202 /// .inserted(37, "a");
203 ///
204 /// assert_eq!(map[&37], "a");
205 /// assert_eq!(map.is_empty(), false);
206 /// ```
207 fn inserted(self, k: K, v: V) -> Self
208 where
209 K: Eq + Hash,
210 S: BuildHasher;
211
212 /// Removes a key from the map.
213 ///
214 /// The key may be any borrowed form of the map's key type, but
215 /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
216 /// the key type.
217 ///
218 /// [`Eq`]: https://doc.rust-lang.org/std/cmp/trait.Eq.html
219 /// [`Hash`]: https://doc.rust-lang.org/std/hash/trait.Hash.html
220 fn removed<Q>(self, k: &Q) -> Self
221 where
222 K: Eq + Hash + Borrow<Q>,
223 S: BuildHasher,
224 Q: Eq + Hash;
225
226 /// Retains only the elements specified by the predicate.
227 ///
228 /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)`
229 /// returns `false`.
230 ///
231 /// # Examples
232 ///
233 /// ```
234 /// use fenn::HashMapExt;
235 /// use std::collections::HashMap;
236 ///
237 /// let map = (0..8).map(|x|(x, x * 10)).collect::<HashMap<i32, i32>>()
238 /// .retained(|&k, _| k % 2 == 0);
239 ///
240 /// assert_eq!(map.len(), 4);
241 /// ```
242 fn retained<F>(self, f: F) -> Self
243 where
244 K: Eq + Hash,
245 S: BuildHasher,
246 F: FnMut(&K, &mut V) -> bool;
247
248 /// Shrinks the capacity of the map as much as possible. It will drop
249 /// down as much as possible while maintaining the internal rules
250 /// and possibly leaving some space in accordance with the resize policy.
251 ///
252 /// # Examples
253 ///
254 /// ```
255 /// use fenn::HashMapExt;
256 /// use std::collections::HashMap;
257 ///
258 /// let map: HashMap<i32, i32> = HashMap::with_capacity(100)
259 /// .inserted(1, 2)
260 /// .inserted(3, 4)
261 /// .shrinked_to_fit();
262 ///
263 /// assert!(map.capacity() >= 2);
264 /// ```
265 fn shrinked_to_fit(self) -> Self
266 where
267 K: Eq + Hash,
268 S: BuildHasher;
269}
270
271impl<K, V, S> HashMapExt<K, V, S> for super::lib::HashMap<K, V, S> {
272 fn cleared(mut self) -> Self {
273 self.clear();
274
275 self
276 }
277
278 fn inserted(mut self, k: K, v: V) -> Self
279 where
280 K: Eq + Hash,
281 S: BuildHasher,
282 {
283 let _ = self.insert(k, v);
284
285 self
286 }
287
288 fn removed<Q>(mut self, k: &Q) -> Self
289 where
290 K: Eq + Hash + Borrow<Q>,
291 S: BuildHasher,
292 Q: Eq + Hash,
293 {
294 let _ = self.remove(k);
295
296 self
297 }
298
299 fn retained<F>(mut self, f: F) -> Self
300 where
301 K: Eq + Hash,
302 S: BuildHasher,
303 F: FnMut(&K, &mut V) -> bool,
304 {
305 self.retain(f);
306
307 self
308 }
309
310 fn shrinked_to_fit(mut self) -> Self
311 where
312 K: Eq + Hash,
313 S: BuildHasher,
314 {
315 self.shrink_to_fit();
316
317 self
318 }
319}