enum_table/lib.rs
1#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
2
3#[cfg(feature = "derive")]
4pub use enum_table_derive::Enumable;
5
6pub mod builder;
7mod impls;
8mod macros;
9
10use core::mem::Discriminant;
11use dev_macros::*;
12
13/// A trait for enumerations that can be used with `EnumTable`.
14///
15/// This trait requires that the enumeration provides a static array of its variants
16/// and a constant representing the count of these variants.
17pub trait Enumable: Sized + 'static {
18 const VARIANTS: &'static [Self];
19 const COUNT: usize = Self::VARIANTS.len();
20}
21
22const fn to_usize<T>(t: T) -> usize {
23 #[inline(always)]
24 const fn cast<T, U>(t: T) -> U {
25 use core::mem::ManuallyDrop;
26 unsafe { core::mem::transmute_copy::<ManuallyDrop<T>, U>(&ManuallyDrop::new(t)) }
27 }
28
29 match const { core::mem::size_of::<T>() } {
30 1 => cast::<T, u8>(t) as usize,
31 2 => cast::<T, u16>(t) as usize,
32 4 => cast::<T, u32>(t) as usize,
33 8 => cast::<T, u64>(t) as usize,
34 _ => panic!("Unsupported size"),
35 }
36}
37
38/// A table that associates each variant of an enumeration with a value.
39///
40/// `EnumTable` is a generic struct that uses an enumeration as keys and stores
41/// associated values. It provides constant-time access to the values based on
42/// the enumeration variant.
43#[derive(Debug, Clone, Copy)]
44pub struct EnumTable<K: Enumable, V, const N: usize> {
45 table: [(Discriminant<K>, V); N],
46}
47
48impl<K: Enumable, V, const N: usize> EnumTable<K, V, N> {
49 /// Creates a new `EnumTable` with the given table of discriminants and values.
50 ///
51 /// # Arguments
52 ///
53 /// * `table` - An array of tuples where each tuple contains a discriminant of
54 /// an enumeration variant and its associated value.
55 pub const fn new(table: [(Discriminant<K>, V); N]) -> Self {
56 Self { table }
57 }
58
59 /// Create a new EnumTable with a function that takes a variant and returns a value.
60 /// If you want to define it in const, use [`crate::et`] macro
61 /// Creates a new `EnumTable` using a function to generate values for each variant.
62 ///
63 /// # Arguments
64 ///
65 /// * `f` - A function that takes a reference to an enumeration variant and returns
66 /// a value to be associated with that variant.
67 pub fn new_with_fn(mut f: impl FnMut(&K) -> V) -> Self {
68 let table = core::array::from_fn(|i| {
69 let k = &K::VARIANTS[i];
70 (core::mem::discriminant(k), f(k))
71 });
72
73 Self { table }
74 }
75
76 /// Returns a reference to the value associated with the given enumeration variant.
77 ///
78 /// # Arguments
79 ///
80 /// * `variant` - A reference to an enumeration variant.
81 pub const fn get(&self, variant: &K) -> &V {
82 use_variant_value!(self, variant, i, {
83 return &self.table[i].1;
84 });
85 }
86
87 /// Returns a mutable reference to the value associated with the given enumeration variant.
88 ///
89 /// # Arguments
90 ///
91 /// * `variant` - A reference to an enumeration variant.
92 pub const fn get_mut(&mut self, variant: &K) -> &mut V {
93 use_variant_value!(self, variant, i, {
94 return &mut self.table[i].1;
95 });
96 }
97
98 /// Sets the value associated with the given enumeration variant.
99 ///
100 /// # Arguments
101 ///
102 /// * `variant` - A reference to an enumeration variant.
103 /// * `value` - The new value to associate with the variant.
104 /// # Returns
105 /// The old value associated with the variant.
106 pub const fn set(&mut self, variant: &K, value: V) -> V {
107 use_variant_value!(self, variant, i, {
108 return core::mem::replace(&mut self.table[i].1, value);
109 });
110 }
111
112 /// Returns the number of generic N
113 pub const fn len(&self) -> usize {
114 N
115 }
116
117 /// Returns `false` since the table is never empty.
118 pub const fn is_empty(&self) -> bool {
119 false
120 }
121}
122
123mod dev_macros {
124 macro_rules! use_variant_value {
125 ($self:ident, $variant:ident, $i:ident,{$($tt:tt)+}) => {
126 let discriminant = core::mem::discriminant($variant);
127
128 let mut $i = 0;
129 while $i < $self.table.len() {
130 if to_usize($self.table[$i].0) == to_usize(discriminant) {
131 $($tt)+
132 }
133 $i += 1;
134 }
135 unreachable!();
136 };
137 }
138
139 pub(super) use use_variant_value;
140}