enum_table/builder.rs
1use std::mem::{Discriminant, MaybeUninit};
2
3use crate::{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.
9pub struct EnumTableBuilder<K: Enumable, V, const N: usize> {
10 idx: usize,
11 table: [MaybeUninit<(Discriminant<K>, V)>; N],
12}
13
14impl<K: Enumable, V, const N: usize> EnumTableBuilder<K, V, N> {
15 /// Creates a new `EnumTableBuilder` with an uninitialized table.
16 ///
17 /// # Returns
18 ///
19 /// A new instance of `EnumTableBuilder`.
20 pub const fn new() -> Self {
21 Self {
22 idx: 0,
23 table: [const { MaybeUninit::uninit() }; N],
24 }
25 }
26
27 /// Pushes a new element into the builder.
28 ///
29 /// # Arguments
30 ///
31 /// * `variant` - A reference to an enumeration variant.
32 /// * `value` - The value to associate with the variant.
33 pub const fn push(&mut self, variant: &K, value: V) {
34 self.table[self.idx] = MaybeUninit::new((core::mem::discriminant(variant), value));
35 self.idx += 1;
36 }
37
38 /// Builds the table from the pushed elements.
39 ///
40 /// # Returns
41 ///
42 /// An array of tuples where each tuple contains a discriminant of an enumeration
43 /// variant and its associated value.
44 pub const fn build(self) -> [(Discriminant<K>, V); N] {
45 if self.idx != N {
46 panic!("EnumTableBuilder: not enough elements");
47 }
48 unsafe { core::mem::transmute_copy(&self.table) }
49 }
50
51 /// Builds the `EnumTable` from the pushed elements.
52 ///
53 /// # Returns
54 ///
55 /// An `EnumTable` containing the elements pushed into the builder.
56 pub const fn build_to(self) -> EnumTable<K, V, N> {
57 EnumTable::new(self.build())
58 }
59
60 /// Returns the number of elements the builder is expected to hold.
61 ///
62 /// # Returns
63 ///
64 /// The number of elements `N`.
65 pub const fn len(&self) -> usize {
66 N
67 }
68
69 /// Returns `false` as the builder is expected to be filled completely.
70 ///
71 /// # Returns
72 ///
73 /// Always returns `false`.
74 pub const fn is_empty(&self) -> bool {
75 false
76 }
77}
78
79impl<K: Enumable, V, const N: usize> Default for EnumTableBuilder<K, V, N> {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn builder() {
91 enum Test {
92 A,
93 B,
94 C,
95 }
96
97 impl Enumable for Test {
98 const VARIANTS: &'static [Self] = &[Test::A, Test::B, Test::C];
99 }
100
101 const TABLE: EnumTable<Test, &'static str, { Test::COUNT }> = {
102 let mut builder = EnumTableBuilder::<Test, &'static str, { Test::COUNT }>::new();
103
104 let mut i = 0;
105 while i < builder.len() {
106 let t = &Test::VARIANTS[i];
107 builder.push(
108 t,
109 match t {
110 Test::A => "A",
111 Test::B => "B",
112 Test::C => "C",
113 },
114 );
115 i += 1;
116 }
117
118 builder.build_to()
119 };
120
121 assert_eq!(TABLE.get(&Test::A), &"A");
122 assert_eq!(TABLE.get(&Test::B), &"B");
123 assert_eq!(TABLE.get(&Test::C), &"C");
124 }
125}