Skip to main content

enum_table/impls/
map.rs

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