enum_table/impls/
map.rs

1use std::{
2    collections::{BTreeMap, HashMap},
3    hash::Hash,
4};
5
6use crate::{EnumTable, Enumable, et, intrinsics::into_variant};
7
8impl<K: Enumable + Eq + Hash + core::fmt::Debug, V, const N: usize> EnumTable<K, V, N> {
9    /// Converts the `EnumTable` into a `HashMap`.
10    ///
11    /// This method consumes the `EnumTable` and creates a new `HashMap` with the same
12    /// key-value pairs. Each enum variant is mapped to its associated value.
13    ///
14    /// # Examples
15    ///
16    /// ```rust
17    /// use enum_table::{EnumTable, Enumable};
18    /// use std::collections::HashMap;
19    ///
20    /// #[derive(Enumable, Debug, PartialEq, Eq, Hash, Copy, Clone)]
21    /// enum Status {
22    ///     Active,
23    ///     Inactive,
24    ///     Pending,
25    /// }
26    ///
27    /// let table = EnumTable::<Status, &str, { Status::COUNT }>::new_with_fn(|status| match status {
28    ///     Status::Active => "running",
29    ///     Status::Inactive => "stopped",
30    ///     Status::Pending => "waiting",
31    /// });
32    ///
33    /// let hash_map = table.into_hash_map();
34    /// assert_eq!(hash_map.get(&Status::Active), Some(&"running"));
35    /// assert_eq!(hash_map.get(&Status::Inactive), Some(&"stopped"));
36    /// assert_eq!(hash_map.get(&Status::Pending), Some(&"waiting"));
37    /// assert_eq!(hash_map.len(), 3);
38    /// ```
39    pub fn into_hash_map(self) -> HashMap<K, V> {
40        self.table
41            .into_iter()
42            .map(|(discriminant, value)| (into_variant(discriminant), value))
43            .collect()
44    }
45
46    /// Creates an `EnumTable` from a `HashMap`.
47    ///
48    /// Returns `None` if the `HashMap` doesn't contain exactly one entry for each enum variant.
49    /// The HashMap must have exactly `N` entries where `N` is the number of enum variants.
50    ///
51    /// # Arguments
52    ///
53    /// * `map` - A `HashMap` containing key-value pairs for each enum variant.
54    ///
55    /// # Examples
56    ///
57    /// ```rust
58    /// use enum_table::{EnumTable, Enumable};
59    /// use std::collections::HashMap;
60    ///
61    /// #[derive(Enumable, Debug, PartialEq, Eq, Hash, Copy, Clone)]
62    /// enum Priority {
63    ///     Low,
64    ///     Medium,
65    ///     High,
66    /// }
67    ///
68    /// let mut hash_map = HashMap::new();
69    /// hash_map.insert(Priority::Low, 1);
70    /// hash_map.insert(Priority::Medium, 5);
71    /// hash_map.insert(Priority::High, 10);
72    ///
73    /// let table = EnumTable::<Priority, i32, { Priority::COUNT }>::try_from_hash_map(hash_map)
74    ///     .expect("HashMap should contain all variants");
75    ///
76    /// assert_eq!(table.get(&Priority::Low), &1);
77    /// assert_eq!(table.get(&Priority::Medium), &5);
78    /// assert_eq!(table.get(&Priority::High), &10);
79    /// ```
80    ///
81    /// ```rust
82    /// # use enum_table::{EnumTable, Enumable};
83    /// # use std::collections::HashMap;
84    /// #
85    /// # #[derive(Enumable, Debug, PartialEq, Eq, Hash, Copy, Clone)]
86    /// # enum Priority {
87    /// #     Low,
88    /// #     Medium,
89    /// #     High,
90    /// # }
91    /// // Example with missing variant
92    /// let mut incomplete_map = HashMap::new();
93    /// incomplete_map.insert(Priority::Low, 1);
94    /// incomplete_map.insert(Priority::Medium, 5);
95    /// // Missing Priority::High
96    ///
97    /// let result = EnumTable::<Priority, i32, { Priority::COUNT }>::try_from_hash_map(incomplete_map);
98    /// assert!(result.is_none());
99    /// ```
100    pub fn try_from_hash_map(mut map: HashMap<K, V>) -> Option<EnumTable<K, V, N>> {
101        if map.len() != N {
102            return None;
103        }
104
105        Some(et!(K, V, { N }, |key| {
106            unsafe { map.remove(key).unwrap_unchecked() }
107        }))
108    }
109}
110
111impl<K: Enumable + Ord, V, const N: usize> EnumTable<K, V, N> {
112    /// Converts the `EnumTable` into a `BTreeMap`.
113    ///
114    /// This method consumes the `EnumTable` and creates a new `BTreeMap` with the same
115    /// key-value pairs. The resulting map will have keys sorted according to their `Ord` implementation.
116    ///
117    /// # Examples
118    ///
119    /// ```rust
120    /// use enum_table::{EnumTable, Enumable};
121    /// use std::collections::BTreeMap;
122    ///
123    /// #[derive(Enumable, Debug, PartialEq, Eq, Ord, PartialOrd, Copy, Clone)]
124    /// enum Level {
125    ///     Beginner,
126    ///     Intermediate,
127    ///     Advanced,
128    /// }
129    ///
130    /// let table = EnumTable::<Level, u32, { Level::COUNT }>::new_with_fn(|level| match level {
131    ///     Level::Beginner => 100,
132    ///     Level::Intermediate => 500,
133    ///     Level::Advanced => 1000,
134    /// });
135    ///
136    /// let btree_map = table.into_btree_map();
137    /// assert_eq!(btree_map.get(&Level::Beginner), Some(&100));
138    /// assert_eq!(btree_map.get(&Level::Intermediate), Some(&500));
139    /// assert_eq!(btree_map.get(&Level::Advanced), Some(&1000));
140    /// assert_eq!(btree_map.len(), 3);
141    /// ```
142    pub fn into_btree_map(self) -> BTreeMap<K, V> {
143        self.table
144            .into_iter()
145            .map(|(discriminant, value)| (into_variant(discriminant), value))
146            .collect()
147    }
148
149    /// Creates an `EnumTable` from a `BTreeMap`.
150    ///
151    /// Returns `None` if the `BTreeMap` doesn't contain exactly one entry for each enum variant.
152    /// The BTreeMap must have exactly `N` entries where `N` is the number of enum variants.
153    ///
154    /// # Arguments
155    ///
156    /// * `map` - A `BTreeMap` containing key-value pairs for each enum variant.
157    ///
158    /// # Examples
159    ///
160    /// ```rust
161    /// use enum_table::{EnumTable, Enumable};
162    /// use std::collections::BTreeMap;
163    ///
164    /// #[derive(Enumable, Debug, PartialEq, Eq, Ord, PartialOrd, Copy, Clone)]
165    /// enum Grade {
166    ///     A,
167    ///     B,
168    ///     C,
169    /// }
170    ///
171    /// let mut btree_map = BTreeMap::new();
172    /// btree_map.insert(Grade::A, 90.0);
173    /// btree_map.insert(Grade::B, 80.0);
174    /// btree_map.insert(Grade::C, 70.0);
175    ///
176    /// let table = EnumTable::<Grade, f64, { Grade::COUNT }>::try_from_btree_map(btree_map)
177    ///     .expect("BTreeMap should contain all variants");
178    ///
179    /// assert_eq!(table.get(&Grade::A), &90.0);
180    /// assert_eq!(table.get(&Grade::B), &80.0);
181    /// assert_eq!(table.get(&Grade::C), &70.0);
182    /// ```
183    ///
184    /// ```rust
185    /// # use enum_table::{EnumTable, Enumable};
186    /// # use std::collections::BTreeMap;
187    /// #
188    /// # #[derive(Enumable, Debug, PartialEq, Eq, Ord, PartialOrd, Copy, Clone)]
189    /// # enum Grade {
190    /// #     A,
191    /// #     B,
192    /// #     C,
193    /// # }
194    /// // Example with missing variant
195    /// let mut incomplete_map = BTreeMap::new();
196    /// incomplete_map.insert(Grade::A, 90.0);
197    /// incomplete_map.insert(Grade::B, 80.0);
198    /// // Missing Grade::C
199    ///
200    /// let result = EnumTable::<Grade, f64, { Grade::COUNT }>::try_from_btree_map(incomplete_map);
201    /// assert!(result.is_none());
202    /// ```
203    pub fn try_from_btree_map(mut map: BTreeMap<K, V>) -> Option<EnumTable<K, V, N>> {
204        if map.len() != N {
205            return None;
206        }
207
208        Some(et!(K, V, { N }, |key| {
209            unsafe { map.remove(key).unwrap_unchecked() }
210        }))
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217
218    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Enumable, Ord, PartialOrd)]
219    enum Color {
220        Red,
221        Green,
222        Blue,
223    }
224
225    const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
226        crate::et!(Color, &'static str, |color| match color {
227            Color::Red => "Red",
228            Color::Green => "Green",
229            Color::Blue => "Blue",
230        });
231
232    #[test]
233    fn into_hash_map() {
234        let table = TABLES;
235        let hash_map = table.into_hash_map();
236
237        assert_eq!(hash_map.len(), 3);
238        assert_eq!(hash_map.get(&Color::Red), Some(&"Red"));
239        assert_eq!(hash_map.get(&Color::Green), Some(&"Green"));
240        assert_eq!(hash_map.get(&Color::Blue), Some(&"Blue"));
241    }
242
243    #[test]
244    fn try_from_hash_map() {
245        let hash_map: HashMap<_, _> = [
246            (Color::Red, "Red"),
247            (Color::Green, "Green"),
248            (Color::Blue, "Blue"),
249        ]
250        .into_iter()
251        .collect();
252
253        let table =
254            EnumTable::<Color, &str, { Color::COUNT }>::try_from_hash_map(hash_map).unwrap();
255        assert_eq!(table.get(&Color::Red), &"Red");
256        assert_eq!(table.get(&Color::Green), &"Green");
257        assert_eq!(table.get(&Color::Blue), &"Blue");
258    }
259
260    #[test]
261    fn try_from_hash_map_invalid_size() {
262        let hash_map: HashMap<_, _> = [
263            (Color::Red, "Red"),
264            (Color::Green, "Green"), // Missing Blue
265        ]
266        .into_iter()
267        .collect();
268
269        let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_hash_map(hash_map);
270        assert!(result.is_none());
271    }
272
273    #[test]
274    fn into_btree_map() {
275        let table = TABLES;
276        let btree_map = table.into_btree_map();
277
278        assert_eq!(btree_map.len(), 3);
279        assert_eq!(btree_map.get(&Color::Red), Some(&"Red"));
280        assert_eq!(btree_map.get(&Color::Green), Some(&"Green"));
281        assert_eq!(btree_map.get(&Color::Blue), Some(&"Blue"));
282    }
283
284    #[test]
285    fn try_from_btree_map() {
286        let btree_map: BTreeMap<_, _> = [
287            (Color::Red, "Red"),
288            (Color::Green, "Green"),
289            (Color::Blue, "Blue"),
290        ]
291        .into_iter()
292        .collect();
293
294        let table =
295            EnumTable::<Color, &str, { Color::COUNT }>::try_from_btree_map(btree_map).unwrap();
296        assert_eq!(table.get(&Color::Red), &"Red");
297        assert_eq!(table.get(&Color::Green), &"Green");
298        assert_eq!(table.get(&Color::Blue), &"Blue");
299    }
300
301    #[test]
302    fn try_from_btree_map_invalid_size() {
303        let btree_map: BTreeMap<_, _> = [
304            (Color::Red, "Red"),
305            (Color::Green, "Green"), // Missing Blue
306        ]
307        .into_iter()
308        .collect();
309
310        let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_btree_map(btree_map);
311        assert!(result.is_none());
312    }
313}