enum_table/impls/
map.rs

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