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}