enum_table/
builder.rs

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