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}