use core::marker::PhantomData;
use core::mem::MaybeUninit;
use crate::{EnumTable, Enumable};
pub struct EnumTableBuilder<K: Enumable, V, const N: usize> {
idx: usize,
table: MaybeUninit<[V; N]>,
#[cfg(debug_assertions)]
keys: MaybeUninit<[K; N]>,
_phantom: PhantomData<K>,
}
impl<K: Enumable, V, const N: usize> EnumTableBuilder<K, V, N> {
pub const fn new() -> Self {
Self {
idx: 0,
table: MaybeUninit::uninit(),
#[cfg(debug_assertions)]
keys: MaybeUninit::uninit(),
_phantom: PhantomData,
}
}
pub const unsafe fn push_unchecked(&mut self, _variant: &K, value: V) {
debug_assert!(self.idx < N, "EnumTableBuilder: too many elements pushed");
#[cfg(debug_assertions)]
unsafe {
self.keys
.as_mut_ptr()
.cast::<K>()
.add(self.idx)
.write(*_variant);
}
unsafe {
self.table
.as_mut_ptr()
.cast::<V>()
.add(self.idx)
.write(value);
}
self.idx += 1;
}
pub const unsafe fn build_unchecked(self) -> [V; N] {
#[cfg(debug_assertions)]
assert!(
self.idx == N,
"EnumTableBuilder: not all elements have been pushed"
);
let table = unsafe { self.table.assume_init() };
#[cfg(debug_assertions)]
{
let keys = unsafe { self.keys.assume_init() };
assert!(
crate::intrinsics::is_sorted(&keys),
"EnumTableBuilder: elements are not sorted by discriminant. Ensure that the elements are pushed in the correct order."
);
}
table
}
pub const unsafe fn build_to_unchecked(self) -> EnumTable<K, V, N> {
EnumTable::new(unsafe { self.build_unchecked() })
}
pub const fn len(&self) -> usize {
self.idx
}
pub const fn capacity(&self) -> usize {
N
}
pub const fn is_empty(&self) -> bool {
self.idx == 0
}
}
impl<K: Enumable, V, const N: usize> Default for EnumTableBuilder<K, V, N> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder() {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
enum Test {
A,
B,
C,
}
const TABLE: EnumTable<Test, &'static str, { Test::COUNT }> = {
let mut builder = EnumTableBuilder::<Test, &'static str, { Test::COUNT }>::new();
let mut i = 0;
while i < builder.capacity() {
let t = &Test::VARIANTS[i];
unsafe {
builder.push_unchecked(
t,
match t {
Test::A => "A",
Test::B => "B",
Test::C => "C",
},
);
}
i += 1;
}
unsafe { builder.build_to_unchecked() }
};
assert_eq!(TABLE.get(&Test::A), &"A");
assert_eq!(TABLE.get(&Test::B), &"B");
assert_eq!(TABLE.get(&Test::C), &"C");
}
}