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 dev_macros::*;
11
12pub trait Enumable: Sized + 'static {
17 const VARIANTS: &'static [Self];
18 const COUNT: usize = Self::VARIANTS.len();
19}
20
21const fn to_usize<T: Copy>(t: T) -> usize {
22 #[inline(always)]
23 const fn cast<U>(t: &impl Sized) -> &U {
24 unsafe { std::mem::transmute(t) }
26 }
27
28 let t = &t;
29
30 match const { core::mem::size_of::<T>() } {
31 1 => *cast::<u8>(t) as usize,
32 2 => *cast::<u16>(t) as usize,
33 4 => *cast::<u32>(t) as usize,
34 #[cfg(target_pointer_width = "64")]
35 8 => *cast::<u64>(t) as usize,
36 #[cfg(target_pointer_width = "32")]
37 8 => panic!("Unsupported size: 64-bit value found on a 32-bit architecture"),
38 _ => panic!("Values larger than u64 are not supported"),
39 }
40}
41
42const fn from_usize<T>(u: &usize) -> &T {
43 unsafe {
44 std::mem::transmute::<&usize, &T>(u)
46 }
47}
48
49pub struct EnumTable<K: Enumable, V, const N: usize> {
96 table: [(usize, V); N],
97 _phantom: core::marker::PhantomData<K>,
98}
99
100impl<K: Enumable, V, const N: usize> EnumTable<K, V, N> {
101 pub const fn new(table: [(usize, V); N]) -> Self {
110 Self {
111 table,
112 _phantom: core::marker::PhantomData,
113 }
114 }
115
116 pub fn new_with_fn(mut f: impl FnMut(&K) -> V) -> Self {
125 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
126
127 for variant in K::VARIANTS {
128 builder.push(variant, f(variant));
129 }
130
131 builder.build_to()
132 }
133
134 pub fn try_new_with_fn<E>(
150 mut f: impl FnMut(&K) -> Result<V, E>,
151 ) -> Result<Self, (&'static K, E)> {
152 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
153
154 for variant in K::VARIANTS {
155 match f(variant) {
156 Ok(value) => builder.push(variant, value),
157 Err(e) => return Err((variant, e)),
158 }
159 }
160
161 Ok(builder.build_to())
162 }
163
164 pub fn checked_new_with_fn(mut f: impl FnMut(&K) -> Option<V>) -> Result<Self, &'static K> {
180 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
181
182 for variant in K::VARIANTS {
183 if let Some(value) = f(variant) {
184 builder.push(variant, value);
185 } else {
186 return Err(variant);
187 }
188 }
189
190 Ok(builder.build_to())
191 }
192
193 pub const fn get(&self, variant: &K) -> &V {
199 use_variant_value!(self, variant, i, {
200 return &self.table[i].1;
201 });
202 }
203
204 pub const fn get_mut(&mut self, variant: &K) -> &mut V {
210 use_variant_value!(self, variant, i, {
211 return &mut self.table[i].1;
212 });
213 }
214
215 pub const fn set(&mut self, variant: &K, value: V) -> V {
224 use_variant_value!(self, variant, i, {
225 return core::mem::replace(&mut self.table[i].1, value);
226 });
227 }
228
229 pub const fn len(&self) -> usize {
231 N
232 }
233
234 pub const fn is_empty(&self) -> bool {
236 false
237 }
238
239 pub fn keys(&self) -> impl Iterator<Item = &K> {
241 self.table
242 .iter()
243 .map(|(discriminant, _)| from_usize(discriminant))
244 }
245
246 pub fn values(&self) -> impl Iterator<Item = &V> {
248 self.table.iter().map(|(_, value)| value)
249 }
250
251 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
253 self.table.iter_mut().map(|(_, value)| value)
254 }
255
256 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
258 self.table
259 .iter()
260 .map(|(discriminant, value)| (from_usize(discriminant), value))
261 }
262
263 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
265 self.table
266 .iter_mut()
267 .map(|(discriminant, value)| (from_usize(discriminant), value))
268 }
269}
270
271mod dev_macros {
272 macro_rules! use_variant_value {
273 ($self:ident, $variant:ident, $i:ident,{$($tt:tt)+}) => {
274 let discriminant = to_usize(core::mem::discriminant($variant));
275
276 let mut $i = 0;
277 while $i < $self.table.len() {
278 if $self.table[$i].0 == discriminant {
279 $($tt)+
280 }
281 $i += 1;
282 }
283 unreachable!();
284 };
285 }
286
287 pub(super) use use_variant_value;
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
295 enum Color {
296 Red,
297 Green,
298 Blue,
299 }
300
301 impl Enumable for Color {
302 const VARIANTS: &'static [Self] = &[Color::Red, Color::Green, Color::Blue];
303 }
304
305 const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
306 crate::et!(Color, &'static str, |color| match color {
307 Color::Red => "Red",
308 Color::Green => "Green",
309 Color::Blue => "Blue",
310 });
311
312 #[test]
313 fn new_with_fn() {
314 let table =
315 EnumTable::<Color, &'static str, { Color::COUNT }>::new_with_fn(|color| match color {
316 Color::Red => "Red",
317 Color::Green => "Green",
318 Color::Blue => "Blue",
319 });
320
321 assert_eq!(table.get(&Color::Red), &"Red");
322 assert_eq!(table.get(&Color::Green), &"Green");
323 assert_eq!(table.get(&Color::Blue), &"Blue");
324 }
325
326 #[test]
327 fn try_new_with_fn() {
328 let table =
329 EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
330 |color| match color {
331 Color::Red => Ok::<&'static str, std::convert::Infallible>("Red"),
332 Color::Green => Ok("Green"),
333 Color::Blue => Ok("Blue"),
334 },
335 );
336
337 assert!(table.is_ok());
338 let table = table.unwrap();
339
340 assert_eq!(table.get(&Color::Red), &"Red");
341 assert_eq!(table.get(&Color::Green), &"Green");
342 assert_eq!(table.get(&Color::Blue), &"Blue");
343
344 let error_table = EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
345 |color| match color {
346 Color::Red => Ok("Red"),
347 Color::Green => Err("Error on Green"),
348 Color::Blue => Ok("Blue"),
349 },
350 );
351
352 assert!(error_table.is_err());
353 let (variant, error) = error_table.unwrap_err();
354
355 assert_eq!(variant, &Color::Green);
356 assert_eq!(error, "Error on Green");
357 }
358
359 #[test]
360 fn checked_new_with_fn() {
361 let table =
362 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
363 match color {
364 Color::Red => Some("Red"),
365 Color::Green => Some("Green"),
366 Color::Blue => Some("Blue"),
367 }
368 });
369
370 assert!(table.is_ok());
371 let table = table.unwrap();
372
373 assert_eq!(table.get(&Color::Red), &"Red");
374 assert_eq!(table.get(&Color::Green), &"Green");
375 assert_eq!(table.get(&Color::Blue), &"Blue");
376
377 let error_table =
378 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
379 match color {
380 Color::Red => Some("Red"),
381 Color::Green => None,
382 Color::Blue => Some("Blue"),
383 }
384 });
385
386 assert!(error_table.is_err());
387 let variant = error_table.unwrap_err();
388
389 assert_eq!(variant, &Color::Green);
390 }
391
392 #[test]
393 fn get() {
394 assert_eq!(TABLES.get(&Color::Red), &"Red");
395 assert_eq!(TABLES.get(&Color::Green), &"Green");
396 assert_eq!(TABLES.get(&Color::Blue), &"Blue");
397 }
398
399 #[test]
400 fn get_mut() {
401 let mut table = TABLES;
402 assert_eq!(table.get_mut(&Color::Red), &mut "Red");
403 assert_eq!(table.get_mut(&Color::Green), &mut "Green");
404 assert_eq!(table.get_mut(&Color::Blue), &mut "Blue");
405
406 *table.get_mut(&Color::Red) = "Changed Red";
407 *table.get_mut(&Color::Green) = "Changed Green";
408 *table.get_mut(&Color::Blue) = "Changed Blue";
409
410 assert_eq!(table.get(&Color::Red), &"Changed Red");
411 assert_eq!(table.get(&Color::Green), &"Changed Green");
412 assert_eq!(table.get(&Color::Blue), &"Changed Blue");
413 }
414
415 #[test]
416 fn set() {
417 let mut table = TABLES;
418 assert_eq!(table.set(&Color::Red, "New Red"), "Red");
419 assert_eq!(table.set(&Color::Green, "New Green"), "Green");
420 assert_eq!(table.set(&Color::Blue, "New Blue"), "Blue");
421
422 assert_eq!(table.get(&Color::Red), &"New Red");
423 assert_eq!(table.get(&Color::Green), &"New Green");
424 assert_eq!(table.get(&Color::Blue), &"New Blue");
425 }
426
427 #[test]
428 fn keys() {
429 let keys: Vec<_> = TABLES.keys().collect();
430 assert_eq!(keys, vec![&Color::Red, &Color::Green, &Color::Blue]);
431 }
432
433 #[test]
434 fn values() {
435 let values: Vec<_> = TABLES.values().collect();
436 assert_eq!(values, vec![&"Red", &"Green", &"Blue"]);
437 }
438
439 #[test]
440 fn iter() {
441 let iter: Vec<_> = TABLES.iter().collect();
442 assert_eq!(
443 iter,
444 vec![
445 (&Color::Red, &"Red"),
446 (&Color::Green, &"Green"),
447 (&Color::Blue, &"Blue")
448 ]
449 );
450 }
451
452 #[test]
453 fn iter_mut() {
454 let mut table = TABLES;
455 for (key, value) in table.iter_mut() {
456 *value = match key {
457 Color::Red => "Changed Red",
458 Color::Green => "Changed Green",
459 Color::Blue => "Changed Blue",
460 };
461 }
462 let iter: Vec<_> = table.iter().collect();
463 assert_eq!(
464 iter,
465 vec![
466 (&Color::Red, &"Changed Red"),
467 (&Color::Green, &"Changed Green"),
468 (&Color::Blue, &"Changed Blue")
469 ]
470 );
471 }
472}