enum_table/
builder.rs

1use core::mem::MaybeUninit;
2
3use crate::{intrinsics::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: MaybeUninit::uninit(),
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        if self.idx >= N {
71            panic!("EnumTableBuilder: too many elements pushed");
72        }
73        let element = (to_usize(variant), value);
74
75        unsafe {
76            self.table
77                .as_mut_ptr()
78                .cast::<(usize, V)>()
79                .add(self.idx)
80                .write(element);
81        }
82
83        self.idx += 1;
84    }
85
86    /// Builds the table from the pushed elements.
87    ///
88    /// # Returns
89    ///
90    /// An array of tuples where each tuple contains a discriminant of an enumeration
91    /// variant and its associated value.
92    pub const fn build(self) -> [(usize, V); N] {
93        if self.idx != N {
94            panic!("EnumTableBuilder: not enough elements");
95        }
96
97        // SAFETY: The table is filled.
98        unsafe { self.table.assume_init() }
99    }
100
101    /// Builds the `EnumTable` from the pushed elements.
102    ///
103    /// # Returns
104    ///
105    /// An `EnumTable` containing the elements pushed into the builder.
106    pub const fn build_to(self) -> EnumTable<K, V, N> {
107        EnumTable::new(self.build())
108    }
109
110    /// Returns the number of elements the builder is expected to hold.
111    ///
112    /// # Returns
113    ///
114    /// The number of elements `N`.
115    pub const fn len(&self) -> usize {
116        N
117    }
118
119    /// Returns `false` as the builder is expected to be filled completely.
120    ///
121    /// # Returns
122    ///
123    /// Always returns `false`.
124    pub const fn is_empty(&self) -> bool {
125        false
126    }
127}
128
129impl<K: Enumable, V, const N: usize> Default for EnumTableBuilder<K, V, N> {
130    fn default() -> Self {
131        Self::new()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn builder() {
141        enum Test {
142            A,
143            B,
144            C,
145        }
146
147        impl Enumable for Test {
148            const VARIANTS: &'static [Self] = &[Test::A, Test::B, Test::C];
149        }
150
151        const TABLE: EnumTable<Test, &'static str, { Test::COUNT }> = {
152            let mut builder = EnumTableBuilder::<Test, &'static str, { Test::COUNT }>::new();
153
154            let mut i = 0;
155            while i < builder.len() {
156                let t = &Test::VARIANTS[i];
157                builder.push(
158                    t,
159                    match t {
160                        Test::A => "A",
161                        Test::B => "B",
162                        Test::C => "C",
163                    },
164                );
165                i += 1;
166            }
167
168            builder.build_to()
169        };
170
171        assert_eq!(TABLE.get(&Test::A), &"A");
172        assert_eq!(TABLE.get(&Test::B), &"B");
173        assert_eq!(TABLE.get(&Test::C), &"C");
174    }
175}