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}