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}