enum_table/
builder.rs

1use std::mem::MaybeUninit;
2
3use crate::{to_usize, 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.
9///
10/// # Note
11/// The builder is expected to be filled completely before building the table.
12/// If the builder is not filled completely, the `build` and `build_to` method will panic.
13/// For a clearer and more concise approach, consider using the [`crate::et`] macro.
14///
15/// # Example
16/// ```rust
17/// use enum_table::{EnumTable, Enumable, builder::EnumTableBuilder,};
18///
19/// #[derive(Debug)]
20/// enum Test {
21///     A,
22///     B,
23///     C,
24/// }
25///
26/// impl Enumable for Test {
27///     const VARIANTS: &'static [Self] = &[Test::A, Test::B, Test::C];
28/// }
29///
30/// const TABLE: EnumTable<Test, &'static str, { Test::COUNT }> = {
31///    let mut builder = EnumTableBuilder::<Test, &'static str, { Test::COUNT }>::new();
32///    builder.push(&Test::A, "A");
33///    builder.push(&Test::B, "B");
34///    builder.push(&Test::C, "C");
35///    builder.build_to()
36/// };
37///
38/// // Access values associated with enum variants
39/// assert_eq!(TABLE.get(&Test::A), &"A");
40/// assert_eq!(TABLE.get(&Test::B), &"B");
41/// assert_eq!(TABLE.get(&Test::C), &"C");
42/// ```
43pub struct EnumTableBuilder<K: Enumable, V, const N: usize> {
44    idx: usize,
45    table: [MaybeUninit<(usize, V)>; N],
46    _phantom: core::marker::PhantomData<K>,
47}
48
49impl<K: Enumable, V, const N: usize> EnumTableBuilder<K, V, N> {
50    /// Creates a new `EnumTableBuilder` with an uninitialized table.
51    ///
52    /// # Returns
53    ///
54    /// A new instance of `EnumTableBuilder`.
55    pub const fn new() -> Self {
56        Self {
57            idx: 0,
58            table: [const { MaybeUninit::uninit() }; N],
59            _phantom: core::marker::PhantomData,
60        }
61    }
62
63    /// Pushes a new element into the builder.
64    ///
65    /// # Arguments
66    ///
67    /// * `variant` - A reference to an enumeration variant.
68    /// * `value` - The value to associate with the variant.
69    pub const fn push(&mut self, variant: &K, value: V) {
70        self.table[self.idx] =
71            MaybeUninit::new((to_usize(core::mem::discriminant(variant)), value));
72
73        self.idx += 1;
74    }
75
76    /// Builds the table from the pushed elements.
77    ///
78    /// # Returns
79    ///
80    /// An array of tuples where each tuple contains a discriminant of an enumeration
81    /// variant and its associated value.
82    pub const fn build(self) -> [(usize, V); N] {
83        if self.idx != N {
84            panic!("EnumTableBuilder: not enough elements");
85        }
86        unsafe { core::mem::transmute_copy(&self.table) }
87    }
88
89    /// Builds the `EnumTable` from the pushed elements.
90    ///
91    /// # Returns
92    ///
93    /// An `EnumTable` containing the elements pushed into the builder.
94    pub const fn build_to(self) -> EnumTable<K, V, N> {
95        EnumTable::new(self.build())
96    }
97
98    /// Returns the number of elements the builder is expected to hold.
99    ///
100    /// # Returns
101    ///
102    /// The number of elements `N`.
103    pub const fn len(&self) -> usize {
104        N
105    }
106
107    /// Returns `false` as the builder is expected to be filled completely.
108    ///
109    /// # Returns
110    ///
111    /// Always returns `false`.
112    pub const fn is_empty(&self) -> bool {
113        false
114    }
115}
116
117impl<K: Enumable, V, const N: usize> Default for EnumTableBuilder<K, V, N> {
118    fn default() -> Self {
119        Self::new()
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn builder() {
129        enum Test {
130            A,
131            B,
132            C,
133        }
134
135        impl Enumable for Test {
136            const VARIANTS: &'static [Self] = &[Test::A, Test::B, Test::C];
137        }
138
139        const TABLE: EnumTable<Test, &'static str, { Test::COUNT }> = {
140            let mut builder = EnumTableBuilder::<Test, &'static str, { Test::COUNT }>::new();
141
142            let mut i = 0;
143            while i < builder.len() {
144                let t = &Test::VARIANTS[i];
145                builder.push(
146                    t,
147                    match t {
148                        Test::A => "A",
149                        Test::B => "B",
150                        Test::C => "C",
151                    },
152                );
153                i += 1;
154            }
155
156            builder.build_to()
157        };
158
159        assert_eq!(TABLE.get(&Test::A), &"A");
160        assert_eq!(TABLE.get(&Test::B), &"B");
161        assert_eq!(TABLE.get(&Test::C), &"C");
162    }
163}