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