enum_table/
builder.rs

1use std::mem::{Discriminant, MaybeUninit};
2
3use crate::{EnumTable, Enumable};
4
5/// A builder for creating an `EnumTable` with a specified number of elements.
6///
7/// `EnumTableBuilder` allows for the incremental construction of an `EnumTable`
8/// by pushing elements one by one and then building the final table.
9pub struct EnumTableBuilder<K: Enumable, V, const N: usize> {
10    idx: usize,
11    table: [MaybeUninit<(Discriminant<K>, V)>; N],
12}
13
14impl<K: Enumable, V, const N: usize> EnumTableBuilder<K, V, N> {
15    /// Creates a new `EnumTableBuilder` with an uninitialized table.
16    ///
17    /// # Returns
18    ///
19    /// A new instance of `EnumTableBuilder`.
20    pub const fn new() -> Self {
21        Self {
22            idx: 0,
23            table: [const { MaybeUninit::uninit() }; N],
24        }
25    }
26
27    /// Pushes a new element into the builder.
28    ///
29    /// # Arguments
30    ///
31    /// * `variant` - A reference to an enumeration variant.
32    /// * `value` - The value to associate with the variant.
33    pub const fn push(&mut self, variant: &K, value: V) {
34        self.table[self.idx] = MaybeUninit::new((core::mem::discriminant(variant), value));
35        self.idx += 1;
36    }
37
38    /// Builds the table from the pushed elements.
39    ///
40    /// # Returns
41    ///
42    /// An array of tuples where each tuple contains a discriminant of an enumeration
43    /// variant and its associated value.
44    pub const fn build(self) -> [(Discriminant<K>, V); N] {
45        if self.idx != N {
46            panic!("EnumTableBuilder: not enough elements");
47        }
48        unsafe { core::mem::transmute_copy(&self.table) }
49    }
50
51    /// Builds the `EnumTable` from the pushed elements.
52    ///
53    /// # Returns
54    ///
55    /// An `EnumTable` containing the elements pushed into the builder.
56    pub const fn build_to(self) -> EnumTable<K, V, N> {
57        EnumTable::new(self.build())
58    }
59
60    /// Returns the number of elements the builder is expected to hold.
61    ///
62    /// # Returns
63    ///
64    /// The number of elements `N`.
65    pub const fn len(&self) -> usize {
66        N
67    }
68
69    /// Returns `false` as the builder is expected to be filled completely.
70    ///
71    /// # Returns
72    ///
73    /// Always returns `false`.
74    pub const fn is_empty(&self) -> bool {
75        false
76    }
77}
78
79impl<K: Enumable, V, const N: usize> Default for EnumTableBuilder<K, V, N> {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn builder() {
91        enum Test {
92            A,
93            B,
94            C,
95        }
96
97        impl Enumable for Test {
98            const VARIANTS: &'static [Self] = &[Test::A, Test::B, Test::C];
99        }
100
101        const TABLE: EnumTable<Test, &'static str, { Test::COUNT }> = {
102            let mut builder = EnumTableBuilder::<Test, &'static str, { Test::COUNT }>::new();
103
104            let mut i = 0;
105            while i < builder.len() {
106                let t = &Test::VARIANTS[i];
107                builder.push(
108                    t,
109                    match t {
110                        Test::A => "A",
111                        Test::B => "B",
112                        Test::C => "C",
113                    },
114                );
115                i += 1;
116            }
117
118            builder.build_to()
119        };
120
121        assert_eq!(TABLE.get(&Test::A), &"A");
122        assert_eq!(TABLE.get(&Test::B), &"B");
123        assert_eq!(TABLE.get(&Test::C), &"C");
124    }
125}