enum_table/impls/
vec.rs

1use crate::{EnumTable, Enumable, builder::EnumTableBuilder, into_variant, to_usize};
2
3/// Error type for `EnumTable::try_from_vec`.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum EnumTableFromVecError<K> {
6    /// The vector has an invalid size.
7    InvalidSize { expected: usize, found: usize },
8    /// A required enum variant is missing from the vector.
9    /// This error happened meaning that the vector duplicated some variant
10    MissingVariant(K),
11}
12
13impl<K: core::fmt::Debug> core::fmt::Display for EnumTableFromVecError<K> {
14    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15        match self {
16            EnumTableFromVecError::InvalidSize { expected, found } => {
17                write!(f, "Invalid vector size: expected {expected}, found {found}")
18            }
19            EnumTableFromVecError::MissingVariant(variant) => {
20                write!(f, "Missing enum variant: {variant:?}")
21            }
22        }
23    }
24}
25
26impl<K: core::fmt::Debug> core::error::Error for EnumTableFromVecError<K> {}
27
28impl<K: Enumable, V, const N: usize> EnumTable<K, V, N> {
29    /// Converts the `EnumTable` into a `Vec` of key-value pairs.
30    ///
31    /// # Examples
32    ///
33    /// ```rust
34    /// use enum_table::{EnumTable, Enumable};
35    ///
36    /// #[derive(Enumable, Debug, PartialEq, Copy, Clone)]
37    /// enum Color {
38    ///     Red,
39    ///     Green,
40    ///     Blue,
41    /// }
42    ///
43    /// let table = EnumTable::<Color, &str, { Color::COUNT }>::new_with_fn(|color| match color {
44    ///     Color::Red => "red",
45    ///     Color::Green => "green",
46    ///     Color::Blue => "blue",
47    /// });
48    ///
49    /// let vec = table.into_vec();
50    /// assert!(vec.contains(&(Color::Red, "red")));
51    /// assert!(vec.contains(&(Color::Green, "green")));
52    /// assert!(vec.contains(&(Color::Blue, "blue")));
53    /// assert_eq!(vec.len(), 3);
54    /// ```
55    pub fn into_vec(self) -> Vec<(K, V)> {
56        self.table
57            .into_iter()
58            .map(|(discriminant, value)| (into_variant(discriminant), value))
59            .collect()
60    }
61
62    /// Creates an `EnumTable` from a `Vec` of key-value pairs.
63    ///
64    /// Returns an error if the vector doesn't contain exactly one entry for each enum variant.
65    ///
66    /// # Arguments
67    ///
68    /// * `vec` - A vector containing key-value pairs for each enum variant.
69    ///
70    /// # Examples
71    ///
72    /// ```rust
73    /// use enum_table::{EnumTable, Enumable};
74    ///
75    /// #[derive(Enumable, Debug, PartialEq, Copy, Clone)]
76    /// enum Color {
77    ///     Red,
78    ///     Green,
79    ///     Blue,
80    /// }
81    ///
82    /// let vec = vec![
83    ///     (Color::Red, "red"),
84    ///     (Color::Green, "green"),
85    ///     (Color::Blue, "blue"),
86    /// ];
87    ///
88    /// let table = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
89    /// assert_eq!(table.get(&Color::Red), &"red");
90    /// assert_eq!(table.get(&Color::Green), &"green");
91    /// assert_eq!(table.get(&Color::Blue), &"blue");
92    /// ```
93    pub fn try_from_vec(mut vec: Vec<(K, V)>) -> Result<Self, EnumTableFromVecError<K>> {
94        if vec.len() != N {
95            return Err(EnumTableFromVecError::InvalidSize {
96                expected: N,
97                found: vec.len(),
98            });
99        }
100
101        let mut builder = EnumTableBuilder::<K, V, N>::new();
102
103        // Check that all variants are present and move values out
104        for variant in K::VARIANTS {
105            if let Some(pos) = vec
106                .iter()
107                .position(|(k, _)| to_usize(k) == to_usize(variant))
108            {
109                let (_, value) = vec.swap_remove(pos);
110                unsafe {
111                    builder.push_unchecked(variant, value);
112                }
113            } else {
114                return Err(EnumTableFromVecError::MissingVariant(*variant));
115            }
116        }
117
118        Ok(unsafe { builder.build_to_unchecked() })
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
127    enum Color {
128        Red = 33,
129        Green = 11,
130        Blue = 222,
131    }
132
133    const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
134        crate::et!(Color, &'static str, |color| match color {
135            Color::Red => "Red",
136            Color::Green => "Green",
137            Color::Blue => "Blue",
138        });
139
140    #[test]
141    fn into_vec() {
142        let table = TABLES;
143        let vec = table.into_vec();
144
145        assert_eq!(vec.len(), 3);
146        assert!(vec.contains(&(Color::Red, "Red")));
147        assert!(vec.contains(&(Color::Green, "Green")));
148        assert!(vec.contains(&(Color::Blue, "Blue")));
149    }
150
151    #[test]
152    fn try_from_vec() {
153        let vec = vec![
154            (Color::Red, "Red"),
155            (Color::Green, "Green"),
156            (Color::Blue, "Blue"),
157        ];
158
159        let table = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
160        assert_eq!(table.get(&Color::Red), &"Red");
161        assert_eq!(table.get(&Color::Green), &"Green");
162        assert_eq!(table.get(&Color::Blue), &"Blue");
163    }
164
165    #[test]
166    fn try_from_vec_invalid_size() {
167        let vec = vec![
168            (Color::Red, "Red"),
169            (Color::Green, "Green"),
170            // Missing Blue
171        ];
172
173        let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
174        assert_eq!(
175            result,
176            Err(crate::EnumTableFromVecError::InvalidSize {
177                expected: 3,
178                found: 2
179            })
180        );
181    }
182
183    #[test]
184    fn try_from_vec_missing_variant() {
185        let vec = vec![
186            (Color::Red, "Red"),
187            (Color::Green, "Green"),
188            (Color::Red, "Duplicate Red"), // Duplicate instead of Blue
189        ];
190
191        let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
192        assert_eq!(
193            result,
194            Err(crate::EnumTableFromVecError::MissingVariant(Color::Blue))
195        );
196    }
197
198    #[test]
199    fn conversion_roundtrip() {
200        let original = TABLES;
201        let vec = original.into_vec();
202        let reconstructed = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
203
204        assert_eq!(reconstructed.get(&Color::Red), &"Red");
205        assert_eq!(reconstructed.get(&Color::Green), &"Green");
206        assert_eq!(reconstructed.get(&Color::Blue), &"Blue");
207    }
208}