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}