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}