1#![doc = include_str!(concat!("../", core::env!("CARGO_PKG_README")))]
2
3#[cfg(test)]
4pub extern crate self as enum_table;
5
6#[cfg(feature = "derive")]
7pub use enum_table_derive::Enumable;
8
9pub mod builder;
10mod intrinsics;
11
12pub mod __private {
13 pub use crate::intrinsics::sort_variants;
14}
15
16mod impls;
17mod macros;
18
19use intrinsics::{copy_from_usize, copy_variant, from_usize, to_usize};
20
21pub trait Enumable: Sized + 'static {
26 const VARIANTS: &'static [Self];
27 const COUNT: usize = Self::VARIANTS.len();
28
29 const _IS_SORTED: () = const {
30 if !intrinsics::is_sorted(Self::VARIANTS) {
33 panic!("Enumable: variants are not sorted by discriminant. Use `enum_table::Enumable` derive macro to ensure correct ordering.");
34 }
35 };
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum EnumTableFromVecError<K> {
41 InvalidSize { expected: usize, found: usize },
43 MissingVariant(K),
46}
47
48impl<K: core::fmt::Debug> core::fmt::Display for EnumTableFromVecError<K> {
49 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50 match self {
51 EnumTableFromVecError::InvalidSize { expected, found } => {
52 write!(
53 f,
54 "Invalid vector size: expected {}, found {}",
55 expected, found
56 )
57 }
58 EnumTableFromVecError::MissingVariant(variant) => {
59 write!(f, "Missing enum variant: {:?}", variant)
60 }
61 }
62 }
63}
64
65impl<K: core::fmt::Debug> core::error::Error for EnumTableFromVecError<K> {}
66
67pub struct EnumTable<K: Enumable, V, const N: usize> {
114 table: [(usize, V); N],
115 _phantom: core::marker::PhantomData<K>,
116}
117
118impl<K: Enumable, V, const N: usize> EnumTable<K, V, N> {
119 pub(crate) const fn new(table: [(usize, V); N]) -> Self {
122 #[cfg(debug_assertions)]
123 let _: () = K::_IS_SORTED;
124 Self {
125 table,
126 _phantom: core::marker::PhantomData,
127 }
128 }
129
130 pub fn new_with_fn(mut f: impl FnMut(&K) -> V) -> Self {
139 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
140
141 for variant in K::VARIANTS {
142 builder.push(variant, f(variant));
143 }
144
145 builder.build_to()
146 }
147
148 pub fn try_new_with_fn<E>(mut f: impl FnMut(&K) -> Result<V, E>) -> Result<Self, (K, E)> {
164 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
165
166 for variant in K::VARIANTS {
167 match f(variant) {
168 Ok(value) => builder.push(variant, value),
169 Err(e) => return Err((copy_variant(variant), e)),
170 }
171 }
172
173 Ok(builder.build_to())
174 }
175
176 pub fn checked_new_with_fn(mut f: impl FnMut(&K) -> Option<V>) -> Result<Self, K> {
192 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
193
194 for variant in K::VARIANTS {
195 if let Some(value) = f(variant) {
196 builder.push(variant, value);
197 } else {
198 return Err(copy_variant(variant));
199 }
200 }
201
202 Ok(builder.build_to())
203 }
204
205 pub(crate) const fn binary_search(&self, variant: &K) -> usize {
206 let discriminant = to_usize(variant);
207 let mut low = 0;
208 let mut high = N;
209
210 while low < high {
211 let mid = low + (high - low) / 2;
212 if self.table[mid].0 < discriminant {
213 low = mid + 1;
214 } else {
215 high = mid;
216 }
217 }
218
219 low
220 }
221
222 pub const fn get(&self, variant: &K) -> &V {
228 let idx = self.binary_search(variant);
229 &self.table[idx].1
230 }
231
232 pub const fn get_mut(&mut self, variant: &K) -> &mut V {
238 let idx = self.binary_search(variant);
239 &mut self.table[idx].1
240 }
241
242 pub const fn set(&mut self, variant: &K, value: V) -> V {
251 let idx = self.binary_search(variant);
252 core::mem::replace(&mut self.table[idx].1, value)
253 }
254
255 pub const fn len(&self) -> usize {
257 N
258 }
259
260 pub const fn is_empty(&self) -> bool {
262 false
263 }
264
265 pub fn keys(&self) -> impl Iterator<Item = &K> {
267 self.table
268 .iter()
269 .map(|(discriminant, _)| from_usize(discriminant))
270 }
271
272 pub fn values(&self) -> impl Iterator<Item = &V> {
274 self.table.iter().map(|(_, value)| value)
275 }
276
277 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
279 self.table.iter_mut().map(|(_, value)| value)
280 }
281
282 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
284 self.table
285 .iter()
286 .map(|(discriminant, value)| (from_usize(discriminant), value))
287 }
288
289 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
291 self.table
292 .iter_mut()
293 .map(|(discriminant, value)| (from_usize(discriminant), value))
294 }
295
296 pub fn map<U, F>(self, mut f: F) -> EnumTable<K, U, N>
326 where
327 F: FnMut(V) -> U,
328 {
329 let mut builder = builder::EnumTableBuilder::<K, U, N>::new();
330
331 for (discriminant, value) in self.table {
332 let key = from_usize(&discriminant);
333 builder.push(key, f(value));
334 }
335
336 builder.build_to()
337 }
338
339 pub fn into_vec(self) -> Vec<(K, V)> {
366 self.table
367 .into_iter()
368 .map(|(discriminant, value)| (copy_from_usize(&discriminant), value))
369 .collect()
370 }
371
372 pub fn try_from_vec(mut vec: Vec<(K, V)>) -> Result<Self, EnumTableFromVecError<K>> {
404 if vec.len() != N {
405 return Err(EnumTableFromVecError::InvalidSize {
406 expected: N,
407 found: vec.len(),
408 });
409 }
410
411 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
412
413 for variant in K::VARIANTS {
415 if let Some(pos) = vec
416 .iter()
417 .position(|(k, _)| to_usize(k) == to_usize(variant))
418 {
419 let (_, value) = vec.swap_remove(pos);
420 builder.push(variant, value);
421 } else {
422 return Err(EnumTableFromVecError::MissingVariant(copy_variant(variant)));
423 }
424 }
425
426 Ok(builder.build_to())
427 }
428}
429
430impl<K: Enumable, V, const N: usize> EnumTable<K, Option<V>, N> {
431 pub const fn new_fill_with_none() -> Self {
433 let mut builder = builder::EnumTableBuilder::<K, Option<V>, N>::new();
434
435 let mut i = 0;
436 while i < N {
437 let variant = &K::VARIANTS[i];
438 builder.push(variant, None);
439 i += 1;
440 }
441
442 builder.build_to()
443 }
444
445 pub fn clear_to_none(&mut self) {
447 for (_, value) in &mut self.table {
448 *value = None;
449 }
450 }
451}
452
453impl<K: Enumable, V: Copy, const N: usize> EnumTable<K, V, N> {
454 pub const fn new_fill_with_copy(value: V) -> Self {
455 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
456
457 let mut i = 0;
458 while i < N {
459 let variant = &K::VARIANTS[i];
460 builder.push(variant, value);
461 i += 1;
462 }
463
464 builder.build_to()
465 }
466}
467
468impl<K: Enumable, V: Default, const N: usize> EnumTable<K, V, N> {
469 pub fn new_fill_with_default() -> Self {
474 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
475
476 for variant in K::VARIANTS {
477 builder.push(variant, V::default());
478 }
479
480 builder.build_to()
481 }
482
483 pub fn clear_to_default(&mut self) {
485 for (_, value) in &mut self.table {
486 *value = V::default();
487 }
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 #[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
496 enum Color {
497 Red = 33,
498 Green = 11,
499 Blue = 222,
500 }
501
502 const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
503 crate::et!(Color, &'static str, |color| match color {
504 Color::Red => "Red",
505 Color::Green => "Green",
506 Color::Blue => "Blue",
507 });
508
509 #[test]
510 fn new_with_fn() {
511 let table =
512 EnumTable::<Color, &'static str, { Color::COUNT }>::new_with_fn(|color| match color {
513 Color::Red => "Red",
514 Color::Green => "Green",
515 Color::Blue => "Blue",
516 });
517
518 assert_eq!(table.get(&Color::Red), &"Red");
519 assert_eq!(table.get(&Color::Green), &"Green");
520 assert_eq!(table.get(&Color::Blue), &"Blue");
521 }
522
523 #[test]
524 fn try_new_with_fn() {
525 let table =
526 EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
527 |color| match color {
528 Color::Red => Ok::<&'static str, core::convert::Infallible>("Red"),
529 Color::Green => Ok("Green"),
530 Color::Blue => Ok("Blue"),
531 },
532 );
533
534 assert!(table.is_ok());
535 let table = table.unwrap();
536
537 assert_eq!(table.get(&Color::Red), &"Red");
538 assert_eq!(table.get(&Color::Green), &"Green");
539 assert_eq!(table.get(&Color::Blue), &"Blue");
540
541 let error_table = EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
542 |color| match color {
543 Color::Red => Ok("Red"),
544 Color::Green => Err("Error on Green"),
545 Color::Blue => Ok("Blue"),
546 },
547 );
548
549 assert!(error_table.is_err());
550 let (variant, error) = error_table.unwrap_err();
551
552 assert_eq!(variant, Color::Green);
553 assert_eq!(error, "Error on Green");
554 }
555
556 #[test]
557 fn checked_new_with_fn() {
558 let table =
559 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
560 match color {
561 Color::Red => Some("Red"),
562 Color::Green => Some("Green"),
563 Color::Blue => Some("Blue"),
564 }
565 });
566
567 assert!(table.is_ok());
568 let table = table.unwrap();
569
570 assert_eq!(table.get(&Color::Red), &"Red");
571 assert_eq!(table.get(&Color::Green), &"Green");
572 assert_eq!(table.get(&Color::Blue), &"Blue");
573
574 let error_table =
575 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
576 match color {
577 Color::Red => Some("Red"),
578 Color::Green => None,
579 Color::Blue => Some("Blue"),
580 }
581 });
582
583 assert!(error_table.is_err());
584 let variant = error_table.unwrap_err();
585
586 assert_eq!(variant, Color::Green);
587 }
588
589 #[test]
590 fn get() {
591 assert_eq!(TABLES.get(&Color::Red), &"Red");
592 assert_eq!(TABLES.get(&Color::Green), &"Green");
593 assert_eq!(TABLES.get(&Color::Blue), &"Blue");
594 }
595
596 #[test]
597 fn get_mut() {
598 let mut table = TABLES;
599 assert_eq!(table.get_mut(&Color::Red), &mut "Red");
600 assert_eq!(table.get_mut(&Color::Green), &mut "Green");
601 assert_eq!(table.get_mut(&Color::Blue), &mut "Blue");
602
603 *table.get_mut(&Color::Red) = "Changed Red";
604 *table.get_mut(&Color::Green) = "Changed Green";
605 *table.get_mut(&Color::Blue) = "Changed Blue";
606
607 assert_eq!(table.get(&Color::Red), &"Changed Red");
608 assert_eq!(table.get(&Color::Green), &"Changed Green");
609 assert_eq!(table.get(&Color::Blue), &"Changed Blue");
610 }
611
612 #[test]
613 fn set() {
614 let mut table = TABLES;
615 assert_eq!(table.set(&Color::Red, "New Red"), "Red");
616 assert_eq!(table.set(&Color::Green, "New Green"), "Green");
617 assert_eq!(table.set(&Color::Blue, "New Blue"), "Blue");
618
619 assert_eq!(table.get(&Color::Red), &"New Red");
620 assert_eq!(table.get(&Color::Green), &"New Green");
621 assert_eq!(table.get(&Color::Blue), &"New Blue");
622 }
623
624 #[test]
625 fn keys() {
626 let keys: Vec<_> = TABLES.keys().collect();
627 assert_eq!(keys, vec![&Color::Green, &Color::Red, &Color::Blue]);
628 }
629
630 #[test]
631 fn values() {
632 let values: Vec<_> = TABLES.values().collect();
633 assert_eq!(values, vec![&"Green", &"Red", &"Blue"]);
634 }
635
636 #[test]
637 fn iter() {
638 let iter: Vec<_> = TABLES.iter().collect();
639 assert_eq!(
640 iter,
641 vec![
642 (&Color::Green, &"Green"),
643 (&Color::Red, &"Red"),
644 (&Color::Blue, &"Blue")
645 ]
646 );
647 }
648
649 #[test]
650 fn iter_mut() {
651 let mut table = TABLES;
652 for (key, value) in table.iter_mut() {
653 *value = match key {
654 Color::Red => "Changed Red",
655 Color::Green => "Changed Green",
656 Color::Blue => "Changed Blue",
657 };
658 }
659 let iter: Vec<_> = table.iter().collect();
660 assert_eq!(
661 iter,
662 vec![
663 (&Color::Green, &"Changed Green"),
664 (&Color::Red, &"Changed Red"),
665 (&Color::Blue, &"Changed Blue")
666 ]
667 );
668 }
669
670 #[test]
671 fn map() {
672 let table = EnumTable::<Color, i32, { Color::COUNT }>::new_with_fn(|color| match color {
673 Color::Red => 1,
674 Color::Green => 2,
675 Color::Blue => 3,
676 });
677
678 let doubled = table.map(|x| x * 2);
679 assert_eq!(doubled.get(&Color::Red), &2);
680 assert_eq!(doubled.get(&Color::Green), &4);
681 assert_eq!(doubled.get(&Color::Blue), &6);
682 }
683
684 #[test]
685 fn into_vec() {
686 let table = TABLES;
687 let vec = table.into_vec();
688
689 assert_eq!(vec.len(), 3);
690 assert!(vec.contains(&(Color::Red, "Red")));
691 assert!(vec.contains(&(Color::Green, "Green")));
692 assert!(vec.contains(&(Color::Blue, "Blue")));
693 }
694
695 #[test]
696 fn try_from_vec() {
697 let vec = vec![
698 (Color::Red, "Red"),
699 (Color::Green, "Green"),
700 (Color::Blue, "Blue"),
701 ];
702
703 let table = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
704 assert_eq!(table.get(&Color::Red), &"Red");
705 assert_eq!(table.get(&Color::Green), &"Green");
706 assert_eq!(table.get(&Color::Blue), &"Blue");
707 }
708
709 #[test]
710 fn try_from_vec_invalid_size() {
711 let vec = vec![
712 (Color::Red, "Red"),
713 (Color::Green, "Green"),
714 ];
716
717 let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
718 assert_eq!(
719 result,
720 Err(crate::EnumTableFromVecError::InvalidSize {
721 expected: 3,
722 found: 2
723 })
724 );
725 }
726
727 #[test]
728 fn try_from_vec_missing_variant() {
729 let vec = vec![
730 (Color::Red, "Red"),
731 (Color::Green, "Green"),
732 (Color::Red, "Duplicate Red"), ];
734
735 let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
736 assert_eq!(
737 result,
738 Err(crate::EnumTableFromVecError::MissingVariant(Color::Blue))
739 );
740 }
741
742 #[test]
743 fn conversion_roundtrip() {
744 let original = TABLES;
745 let vec = original.into_vec();
746 let reconstructed = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
747
748 assert_eq!(reconstructed.get(&Color::Red), &"Red");
749 assert_eq!(reconstructed.get(&Color::Green), &"Green");
750 assert_eq!(reconstructed.get(&Color::Blue), &"Blue");
751 }
752
753 macro_rules! run_variants_test {
754 ($($variant:ident),+) => {{
755 #[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
756 #[repr(u8)]
757 enum Test {
758 $($variant,)*
759 }
760
761 let map = EnumTable::<Test, &'static str, { Test::COUNT }>::new_with_fn(|t| match t {
762 $(Test::$variant => stringify!($variant),)*
763 });
764 $(
765 assert_eq!(map.get(&Test::$variant), &stringify!($variant));
766 )*
767 }};
768 }
769
770 #[test]
771 fn binary_search_correct_variants() {
772 run_variants_test!(A);
773 run_variants_test!(A, B);
774 run_variants_test!(A, B, C);
775 run_variants_test!(A, B, C, D);
776 run_variants_test!(A, B, C, D, E);
777 }
778}