enum_table/
lib.rs

1#![doc = include_str!(concat!("../", core::env!("CARGO_PKG_README")))]
2
3#[cfg(test)]
4pub extern crate self as enum_table;
5
6#[cfg(feature = "derive")]
7pub use enum_table_derive::Enumable;
8
9pub mod builder;
10mod intrinsics;
11
12pub mod __private {
13    pub use crate::intrinsics::sort_variants;
14}
15
16mod impls;
17mod macros;
18
19use intrinsics::{copy_from_usize, copy_variant, from_usize, to_usize};
20
21/// A trait for enumerations that can be used with `EnumTable`.
22///
23/// This trait requires that the enumeration provides a static array of its variants
24/// and a constant representing the count of these variants.
25pub trait Enumable: Sized + 'static {
26    const VARIANTS: &'static [Self];
27    const COUNT: usize = Self::VARIANTS.len();
28
29    const _IS_SORTED: () = const {
30        // Ensure that the variants are sorted by their discriminants.
31        // This is a compile-time check to ensure that the variants are in the correct order.
32        if !intrinsics::is_sorted(Self::VARIANTS) {
33            panic!("Enumable: variants are not sorted by discriminant. Use `enum_table::Enumable` derive macro to ensure correct ordering.");
34        }
35    };
36}
37
38/// Error type for `EnumTable::try_from_vec`.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum EnumTableFromVecError<K> {
41    /// The vector has an invalid size.
42    InvalidSize { expected: usize, found: usize },
43    /// A required enum variant is missing from the vector.
44    /// This error happened meaning that the vector duplicated some variant
45    MissingVariant(K),
46}
47
48impl<K: core::fmt::Debug> core::fmt::Display for EnumTableFromVecError<K> {
49    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50        match self {
51            EnumTableFromVecError::InvalidSize { expected, found } => {
52                write!(
53                    f,
54                    "Invalid vector size: expected {}, found {}",
55                    expected, found
56                )
57            }
58            EnumTableFromVecError::MissingVariant(variant) => {
59                write!(f, "Missing enum variant: {:?}", variant)
60            }
61        }
62    }
63}
64
65impl<K: core::fmt::Debug> core::error::Error for EnumTableFromVecError<K> {}
66
67/// A table that associates each variant of an enumeration with a value.
68///
69/// `EnumTable` is a generic struct that uses an enumeration as keys and stores
70/// associated values. It provides constant-time access to the values based on
71/// the enumeration variant. This is particularly useful when you want to map
72/// enum variants to specific values without the overhead of a `HashMap`.
73///
74/// # Type Parameters
75///
76/// * `K`: The enumeration type that implements the `Enumable` trait. This trait
77///   ensures that the enum provides a static array of its variants and a count
78///   of these variants.
79/// * `V`: The type of values to be associated with each enum variant.
80/// * `N`: The number of variants in the enum, which should match the length of
81///   the static array of variants provided by the `Enumable` trait.
82///
83/// # Note
84/// The `new` method allows for the creation of an `EnumTable` in `const` contexts,
85/// but it does not perform compile-time checks. For enhanced compile-time safety
86/// and convenience, it is advisable to use the [`crate::et`] macro or
87/// [`crate::builder::EnumTableBuilder`], which provide these checks.
88///
89/// # Examples
90///
91/// ```rust
92/// use enum_table::{EnumTable, Enumable};
93///
94/// #[derive(Enumable)]
95/// enum Color {
96///     Red,
97///     Green,
98///     Blue,
99/// }
100///
101/// // Create an EnumTable using the new_with_fn method
102/// let table = EnumTable::<Color, &'static str, { Color::COUNT }>::new_with_fn(|color| match color {
103///     Color::Red => "Red",
104///     Color::Green => "Green",
105///     Color::Blue => "Blue",
106/// });
107///
108/// // Access values associated with enum variants
109/// assert_eq!(table.get(&Color::Red), &"Red");
110/// assert_eq!(table.get(&Color::Green), &"Green");
111/// assert_eq!(table.get(&Color::Blue), &"Blue");
112/// ```
113pub struct EnumTable<K: Enumable, V, const N: usize> {
114    table: [(usize, V); N],
115    _phantom: core::marker::PhantomData<K>,
116}
117
118impl<K: Enumable, V, const N: usize> EnumTable<K, V, N> {
119    /// Creates a new `EnumTable` with the given table of discriminants and values.
120    /// Typically, you would use the [`crate::et`] macro or [`crate::builder::EnumTableBuilder`] to create an `EnumTable`.
121    pub(crate) const fn new(table: [(usize, V); N]) -> Self {
122        #[cfg(debug_assertions)]
123        let _: () = K::_IS_SORTED;
124        Self {
125            table,
126            _phantom: core::marker::PhantomData,
127        }
128    }
129
130    /// Create a new EnumTable with a function that takes a variant and returns a value.
131    /// If you want to define it in const, use [`crate::et`] macro
132    /// Creates a new `EnumTable` using a function to generate values for each variant.
133    ///
134    /// # Arguments
135    ///
136    /// * `f` - A function that takes a reference to an enumeration variant and returns
137    ///   a value to be associated with that variant.
138    pub fn new_with_fn(mut f: impl FnMut(&K) -> V) -> Self {
139        let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
140
141        for variant in K::VARIANTS {
142            builder.push(variant, f(variant));
143        }
144
145        builder.build_to()
146    }
147
148    /// Creates a new `EnumTable` using a function that returns a `Result` for each variant.
149    ///
150    /// This method applies the provided closure to each variant of the enum. If the closure
151    /// returns `Ok(value)` for all variants, an `EnumTable` is constructed and returned as `Ok(Self)`.
152    /// If the closure returns `Err(e)` for any variant, the construction is aborted and
153    /// `Err((variant, e))` is returned, where `variant` is the enum variant that caused the error.
154    ///
155    /// # Arguments
156    ///
157    /// * `f` - A closure that takes a reference to an enum variant and returns a `Result<V, E>`.
158    ///
159    /// # Returns
160    ///
161    /// * `Ok(Self)` if all variants succeed.
162    /// * `Err((variant, e))` if any variant fails, containing the failing variant and the error.
163    pub fn try_new_with_fn<E>(mut f: impl FnMut(&K) -> Result<V, E>) -> Result<Self, (K, E)> {
164        let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
165
166        for variant in K::VARIANTS {
167            match f(variant) {
168                Ok(value) => builder.push(variant, value),
169                Err(e) => return Err((copy_variant(variant), e)),
170            }
171        }
172
173        Ok(builder.build_to())
174    }
175
176    /// Creates a new `EnumTable` using a function that returns an `Option` for each variant.
177    ///
178    /// This method applies the provided closure to each variant of the enum. If the closure
179    /// returns `Some(value)` for all variants, an `EnumTable` is constructed and returned as `Ok(Self)`.
180    /// If the closure returns `None` for any variant, the construction is aborted and
181    /// `Err(variant)` is returned, where `variant` is the enum variant that caused the failure.
182    ///
183    /// # Arguments
184    ///
185    /// * `f` - A closure that takes a reference to an enum variant and returns an `Option<V>`.
186    ///
187    /// # Returns
188    ///
189    /// * `Ok(Self)` if all variants succeed.
190    /// * `Err(variant)` if any variant fails, containing the failing variant.
191    pub fn checked_new_with_fn(mut f: impl FnMut(&K) -> Option<V>) -> Result<Self, K> {
192        let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
193
194        for variant in K::VARIANTS {
195            if let Some(value) = f(variant) {
196                builder.push(variant, value);
197            } else {
198                return Err(copy_variant(variant));
199            }
200        }
201
202        Ok(builder.build_to())
203    }
204
205    pub(crate) const fn binary_search(&self, variant: &K) -> usize {
206        let discriminant = to_usize(variant);
207        let mut low = 0;
208        let mut high = N;
209
210        while low < high {
211            let mid = low + (high - low) / 2;
212            if self.table[mid].0 < discriminant {
213                low = mid + 1;
214            } else {
215                high = mid;
216            }
217        }
218
219        low
220    }
221
222    /// Returns a reference to the value associated with the given enumeration variant.
223    ///
224    /// # Arguments
225    ///
226    /// * `variant` - A reference to an enumeration variant.
227    pub const fn get(&self, variant: &K) -> &V {
228        let idx = self.binary_search(variant);
229        &self.table[idx].1
230    }
231
232    /// Returns a mutable reference to the value associated with the given enumeration variant.
233    ///
234    /// # Arguments
235    ///
236    /// * `variant` - A reference to an enumeration variant.
237    pub const fn get_mut(&mut self, variant: &K) -> &mut V {
238        let idx = self.binary_search(variant);
239        &mut self.table[idx].1
240    }
241
242    /// Sets the value associated with the given enumeration variant.
243    ///
244    /// # Arguments
245    ///
246    /// * `variant` - A reference to an enumeration variant.
247    /// * `value` - The new value to associate with the variant.
248    /// # Returns
249    /// The old value associated with the variant.
250    pub const fn set(&mut self, variant: &K, value: V) -> V {
251        let idx = self.binary_search(variant);
252        core::mem::replace(&mut self.table[idx].1, value)
253    }
254
255    /// Returns the number of generic N
256    pub const fn len(&self) -> usize {
257        N
258    }
259
260    /// Returns `false` since the table is never empty.
261    pub const fn is_empty(&self) -> bool {
262        false
263    }
264
265    /// Returns an iterator over references to the keys in the table.
266    pub fn keys(&self) -> impl Iterator<Item = &K> {
267        self.table
268            .iter()
269            .map(|(discriminant, _)| from_usize(discriminant))
270    }
271
272    /// Returns an iterator over references to the values in the table.
273    pub fn values(&self) -> impl Iterator<Item = &V> {
274        self.table.iter().map(|(_, value)| value)
275    }
276
277    /// Returns an iterator over mutable references to the values in the table.
278    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
279        self.table.iter_mut().map(|(_, value)| value)
280    }
281
282    /// Returns an iterator over mutable references to the values in the table.
283    pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
284        self.table
285            .iter()
286            .map(|(discriminant, value)| (from_usize(discriminant), value))
287    }
288
289    /// Returns an iterator over mutable references to the values in the table.
290    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
291        self.table
292            .iter_mut()
293            .map(|(discriminant, value)| (from_usize(discriminant), value))
294    }
295
296    /// Maps the values of the table using a closure, creating a new `EnumTable` with the transformed values.
297    ///
298    /// # Arguments
299    ///
300    /// * `f` - A closure that takes a value and returns a transformed value.
301    ///
302    /// # Examples
303    ///
304    /// ```rust
305    /// use enum_table::{EnumTable, Enumable};
306    ///
307    /// #[derive(Enumable)]
308    /// enum Color {
309    ///     Red,
310    ///     Green,
311    ///     Blue,
312    /// }
313    ///
314    /// let table = EnumTable::<Color, i32, { Color::COUNT }>::new_with_fn(|color| match color {
315    ///     Color::Red => 1,
316    ///     Color::Green => 2,
317    ///     Color::Blue => 3,
318    /// });
319    ///
320    /// let doubled = table.map(|x| x * 2);
321    /// assert_eq!(doubled.get(&Color::Red), &2);
322    /// assert_eq!(doubled.get(&Color::Green), &4);
323    /// assert_eq!(doubled.get(&Color::Blue), &6);
324    /// ```
325    pub fn map<U, F>(self, mut f: F) -> EnumTable<K, U, N>
326    where
327        F: FnMut(V) -> U,
328    {
329        let mut builder = builder::EnumTableBuilder::<K, U, N>::new();
330
331        for (discriminant, value) in self.table {
332            let key = from_usize(&discriminant);
333            builder.push(key, f(value));
334        }
335
336        builder.build_to()
337    }
338
339    /// Converts the `EnumTable` into a `Vec` of key-value pairs.
340    ///
341    /// # Examples
342    ///
343    /// ```rust
344    /// use enum_table::{EnumTable, Enumable};
345    ///
346    /// #[derive(Enumable, Debug, PartialEq, Copy, Clone)]
347    /// enum Color {
348    ///     Red,
349    ///     Green,
350    ///     Blue,
351    /// }
352    ///
353    /// let table = EnumTable::<Color, &str, { Color::COUNT }>::new_with_fn(|color| match color {
354    ///     Color::Red => "red",
355    ///     Color::Green => "green",
356    ///     Color::Blue => "blue",
357    /// });
358    ///
359    /// let vec = table.into_vec();
360    /// assert!(vec.contains(&(Color::Red, "red")));
361    /// assert!(vec.contains(&(Color::Green, "green")));
362    /// assert!(vec.contains(&(Color::Blue, "blue")));
363    /// assert_eq!(vec.len(), 3);
364    /// ```
365    pub fn into_vec(self) -> Vec<(K, V)> {
366        self.table
367            .into_iter()
368            .map(|(discriminant, value)| (copy_from_usize(&discriminant), value))
369            .collect()
370    }
371
372    /// Creates an `EnumTable` from a `Vec` of key-value pairs.
373    ///
374    /// Returns an error if the vector doesn't contain exactly one entry for each enum variant.
375    ///
376    /// # Arguments
377    ///
378    /// * `vec` - A vector containing key-value pairs for each enum variant.
379    ///
380    /// # Examples
381    ///
382    /// ```rust
383    /// use enum_table::{EnumTable, Enumable};
384    ///
385    /// #[derive(Enumable, Debug, PartialEq, Copy, Clone)]
386    /// enum Color {
387    ///     Red,
388    ///     Green,
389    ///     Blue,
390    /// }
391    ///
392    /// let vec = vec![
393    ///     (Color::Red, "red"),
394    ///     (Color::Green, "green"),
395    ///     (Color::Blue, "blue"),
396    /// ];
397    ///
398    /// let table = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
399    /// assert_eq!(table.get(&Color::Red), &"red");
400    /// assert_eq!(table.get(&Color::Green), &"green");
401    /// assert_eq!(table.get(&Color::Blue), &"blue");
402    /// ```
403    pub fn try_from_vec(mut vec: Vec<(K, V)>) -> Result<Self, EnumTableFromVecError<K>> {
404        if vec.len() != N {
405            return Err(EnumTableFromVecError::InvalidSize {
406                expected: N,
407                found: vec.len(),
408            });
409        }
410
411        let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
412
413        // Check that all variants are present and move values out
414        for variant in K::VARIANTS {
415            if let Some(pos) = vec
416                .iter()
417                .position(|(k, _)| to_usize(k) == to_usize(variant))
418            {
419                let (_, value) = vec.swap_remove(pos);
420                builder.push(variant, value);
421            } else {
422                return Err(EnumTableFromVecError::MissingVariant(copy_variant(variant)));
423            }
424        }
425
426        Ok(builder.build_to())
427    }
428}
429
430impl<K: Enumable, V, const N: usize> EnumTable<K, Option<V>, N> {
431    /// Creates a new `EnumTable` with `None` values for each variant.
432    pub const fn new_fill_with_none() -> Self {
433        let mut builder = builder::EnumTableBuilder::<K, Option<V>, N>::new();
434
435        let mut i = 0;
436        while i < N {
437            let variant = &K::VARIANTS[i];
438            builder.push(variant, None);
439            i += 1;
440        }
441
442        builder.build_to()
443    }
444
445    /// Clears the table, setting each value to `None`.
446    pub fn clear_to_none(&mut self) {
447        for (_, value) in &mut self.table {
448            *value = None;
449        }
450    }
451}
452
453impl<K: Enumable, V: Copy, const N: usize> EnumTable<K, V, N> {
454    pub const fn new_fill_with_copy(value: V) -> Self {
455        let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
456
457        let mut i = 0;
458        while i < N {
459            let variant = &K::VARIANTS[i];
460            builder.push(variant, value);
461            i += 1;
462        }
463
464        builder.build_to()
465    }
466}
467
468impl<K: Enumable, V: Default, const N: usize> EnumTable<K, V, N> {
469    /// Creates a new `EnumTable` with default values for each variant.
470    ///
471    /// This method initializes the table with the default value of type `V` for each
472    /// variant of the enumeration.
473    pub fn new_fill_with_default() -> Self {
474        let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
475
476        for variant in K::VARIANTS {
477            builder.push(variant, V::default());
478        }
479
480        builder.build_to()
481    }
482
483    /// Clears the table, setting each value to its default.
484    pub fn clear_to_default(&mut self) {
485        for (_, value) in &mut self.table {
486            *value = V::default();
487        }
488    }
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494
495    #[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
496    enum Color {
497        Red = 33,
498        Green = 11,
499        Blue = 222,
500    }
501
502    const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
503        crate::et!(Color, &'static str, |color| match color {
504            Color::Red => "Red",
505            Color::Green => "Green",
506            Color::Blue => "Blue",
507        });
508
509    #[test]
510    fn new_with_fn() {
511        let table =
512            EnumTable::<Color, &'static str, { Color::COUNT }>::new_with_fn(|color| match color {
513                Color::Red => "Red",
514                Color::Green => "Green",
515                Color::Blue => "Blue",
516            });
517
518        assert_eq!(table.get(&Color::Red), &"Red");
519        assert_eq!(table.get(&Color::Green), &"Green");
520        assert_eq!(table.get(&Color::Blue), &"Blue");
521    }
522
523    #[test]
524    fn try_new_with_fn() {
525        let table =
526            EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
527                |color| match color {
528                    Color::Red => Ok::<&'static str, core::convert::Infallible>("Red"),
529                    Color::Green => Ok("Green"),
530                    Color::Blue => Ok("Blue"),
531                },
532            );
533
534        assert!(table.is_ok());
535        let table = table.unwrap();
536
537        assert_eq!(table.get(&Color::Red), &"Red");
538        assert_eq!(table.get(&Color::Green), &"Green");
539        assert_eq!(table.get(&Color::Blue), &"Blue");
540
541        let error_table = EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
542            |color| match color {
543                Color::Red => Ok("Red"),
544                Color::Green => Err("Error on Green"),
545                Color::Blue => Ok("Blue"),
546            },
547        );
548
549        assert!(error_table.is_err());
550        let (variant, error) = error_table.unwrap_err();
551
552        assert_eq!(variant, Color::Green);
553        assert_eq!(error, "Error on Green");
554    }
555
556    #[test]
557    fn checked_new_with_fn() {
558        let table =
559            EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
560                match color {
561                    Color::Red => Some("Red"),
562                    Color::Green => Some("Green"),
563                    Color::Blue => Some("Blue"),
564                }
565            });
566
567        assert!(table.is_ok());
568        let table = table.unwrap();
569
570        assert_eq!(table.get(&Color::Red), &"Red");
571        assert_eq!(table.get(&Color::Green), &"Green");
572        assert_eq!(table.get(&Color::Blue), &"Blue");
573
574        let error_table =
575            EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
576                match color {
577                    Color::Red => Some("Red"),
578                    Color::Green => None,
579                    Color::Blue => Some("Blue"),
580                }
581            });
582
583        assert!(error_table.is_err());
584        let variant = error_table.unwrap_err();
585
586        assert_eq!(variant, Color::Green);
587    }
588
589    #[test]
590    fn get() {
591        assert_eq!(TABLES.get(&Color::Red), &"Red");
592        assert_eq!(TABLES.get(&Color::Green), &"Green");
593        assert_eq!(TABLES.get(&Color::Blue), &"Blue");
594    }
595
596    #[test]
597    fn get_mut() {
598        let mut table = TABLES;
599        assert_eq!(table.get_mut(&Color::Red), &mut "Red");
600        assert_eq!(table.get_mut(&Color::Green), &mut "Green");
601        assert_eq!(table.get_mut(&Color::Blue), &mut "Blue");
602
603        *table.get_mut(&Color::Red) = "Changed Red";
604        *table.get_mut(&Color::Green) = "Changed Green";
605        *table.get_mut(&Color::Blue) = "Changed Blue";
606
607        assert_eq!(table.get(&Color::Red), &"Changed Red");
608        assert_eq!(table.get(&Color::Green), &"Changed Green");
609        assert_eq!(table.get(&Color::Blue), &"Changed Blue");
610    }
611
612    #[test]
613    fn set() {
614        let mut table = TABLES;
615        assert_eq!(table.set(&Color::Red, "New Red"), "Red");
616        assert_eq!(table.set(&Color::Green, "New Green"), "Green");
617        assert_eq!(table.set(&Color::Blue, "New Blue"), "Blue");
618
619        assert_eq!(table.get(&Color::Red), &"New Red");
620        assert_eq!(table.get(&Color::Green), &"New Green");
621        assert_eq!(table.get(&Color::Blue), &"New Blue");
622    }
623
624    #[test]
625    fn keys() {
626        let keys: Vec<_> = TABLES.keys().collect();
627        assert_eq!(keys, vec![&Color::Green, &Color::Red, &Color::Blue]);
628    }
629
630    #[test]
631    fn values() {
632        let values: Vec<_> = TABLES.values().collect();
633        assert_eq!(values, vec![&"Green", &"Red", &"Blue"]);
634    }
635
636    #[test]
637    fn iter() {
638        let iter: Vec<_> = TABLES.iter().collect();
639        assert_eq!(
640            iter,
641            vec![
642                (&Color::Green, &"Green"),
643                (&Color::Red, &"Red"),
644                (&Color::Blue, &"Blue")
645            ]
646        );
647    }
648
649    #[test]
650    fn iter_mut() {
651        let mut table = TABLES;
652        for (key, value) in table.iter_mut() {
653            *value = match key {
654                Color::Red => "Changed Red",
655                Color::Green => "Changed Green",
656                Color::Blue => "Changed Blue",
657            };
658        }
659        let iter: Vec<_> = table.iter().collect();
660        assert_eq!(
661            iter,
662            vec![
663                (&Color::Green, &"Changed Green"),
664                (&Color::Red, &"Changed Red"),
665                (&Color::Blue, &"Changed Blue")
666            ]
667        );
668    }
669
670    #[test]
671    fn map() {
672        let table = EnumTable::<Color, i32, { Color::COUNT }>::new_with_fn(|color| match color {
673            Color::Red => 1,
674            Color::Green => 2,
675            Color::Blue => 3,
676        });
677
678        let doubled = table.map(|x| x * 2);
679        assert_eq!(doubled.get(&Color::Red), &2);
680        assert_eq!(doubled.get(&Color::Green), &4);
681        assert_eq!(doubled.get(&Color::Blue), &6);
682    }
683
684    #[test]
685    fn into_vec() {
686        let table = TABLES;
687        let vec = table.into_vec();
688
689        assert_eq!(vec.len(), 3);
690        assert!(vec.contains(&(Color::Red, "Red")));
691        assert!(vec.contains(&(Color::Green, "Green")));
692        assert!(vec.contains(&(Color::Blue, "Blue")));
693    }
694
695    #[test]
696    fn try_from_vec() {
697        let vec = vec![
698            (Color::Red, "Red"),
699            (Color::Green, "Green"),
700            (Color::Blue, "Blue"),
701        ];
702
703        let table = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
704        assert_eq!(table.get(&Color::Red), &"Red");
705        assert_eq!(table.get(&Color::Green), &"Green");
706        assert_eq!(table.get(&Color::Blue), &"Blue");
707    }
708
709    #[test]
710    fn try_from_vec_invalid_size() {
711        let vec = vec![
712            (Color::Red, "Red"),
713            (Color::Green, "Green"),
714            // Missing Blue
715        ];
716
717        let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
718        assert_eq!(
719            result,
720            Err(crate::EnumTableFromVecError::InvalidSize {
721                expected: 3,
722                found: 2
723            })
724        );
725    }
726
727    #[test]
728    fn try_from_vec_missing_variant() {
729        let vec = vec![
730            (Color::Red, "Red"),
731            (Color::Green, "Green"),
732            (Color::Red, "Duplicate Red"), // Duplicate instead of Blue
733        ];
734
735        let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
736        assert_eq!(
737            result,
738            Err(crate::EnumTableFromVecError::MissingVariant(Color::Blue))
739        );
740    }
741
742    #[test]
743    fn conversion_roundtrip() {
744        let original = TABLES;
745        let vec = original.into_vec();
746        let reconstructed = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
747
748        assert_eq!(reconstructed.get(&Color::Red), &"Red");
749        assert_eq!(reconstructed.get(&Color::Green), &"Green");
750        assert_eq!(reconstructed.get(&Color::Blue), &"Blue");
751    }
752
753    macro_rules! run_variants_test {
754        ($($variant:ident),+) => {{
755            #[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
756            #[repr(u8)]
757            enum Test {
758                $($variant,)*
759            }
760
761            let map = EnumTable::<Test, &'static str, { Test::COUNT }>::new_with_fn(|t| match t {
762                $(Test::$variant => stringify!($variant),)*
763            });
764            $(
765                assert_eq!(map.get(&Test::$variant), &stringify!($variant));
766            )*
767        }};
768    }
769
770    #[test]
771    fn binary_search_correct_variants() {
772        run_variants_test!(A);
773        run_variants_test!(A, B);
774        run_variants_test!(A, B, C);
775        run_variants_test!(A, B, C, D);
776        run_variants_test!(A, B, C, D, E);
777    }
778}