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