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
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum EnumTableFromVecError<K> {
33 InvalidSize { expected: usize, found: usize },
35 MissingVariant(K),
37}
38
39impl<K: core::fmt::Debug> core::fmt::Display for EnumTableFromVecError<K> {
40 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41 match self {
42 EnumTableFromVecError::InvalidSize { expected, found } => {
43 write!(
44 f,
45 "Invalid vector size: expected {}, found {}",
46 expected, found
47 )
48 }
49 EnumTableFromVecError::MissingVariant(variant) => {
50 write!(f, "Missing enum variant: {:?}", variant)
51 }
52 }
53 }
54}
55
56impl<K: core::fmt::Debug> core::error::Error for EnumTableFromVecError<K> {}
57
58pub struct EnumTable<K: Enumable, V, const N: usize> {
105 table: [(usize, V); N],
106 _phantom: core::marker::PhantomData<K>,
107}
108
109impl<K: Enumable, V, const N: usize> EnumTable<K, V, N> {
110 pub(crate) const fn new(table: [(usize, V); N]) -> Self {
113 Self {
114 table,
115 _phantom: core::marker::PhantomData,
116 }
117 }
118
119 pub fn new_with_fn(mut f: impl FnMut(&K) -> V) -> Self {
128 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
129
130 for variant in K::VARIANTS {
131 builder.push(variant, f(variant));
132 }
133
134 builder.build_to()
135 }
136
137 pub fn try_new_with_fn<E>(mut f: impl FnMut(&K) -> Result<V, E>) -> Result<Self, (K, E)> {
153 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
154
155 for variant in K::VARIANTS {
156 match f(variant) {
157 Ok(value) => builder.push(variant, value),
158 Err(e) => return Err((copy_variant(variant), e)),
159 }
160 }
161
162 Ok(builder.build_to())
163 }
164
165 pub fn checked_new_with_fn(mut f: impl FnMut(&K) -> Option<V>) -> Result<Self, K> {
181 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
182
183 for variant in K::VARIANTS {
184 if let Some(value) = f(variant) {
185 builder.push(variant, value);
186 } else {
187 return Err(copy_variant(variant));
188 }
189 }
190
191 Ok(builder.build_to())
192 }
193
194 pub(crate) const fn binary_search(&self, variant: &K) -> usize {
195 let discriminant = to_usize(variant);
196 let mut low = 0;
197 let mut high = N;
198
199 while low < high {
200 let mid = low + (high - low) / 2;
201 if self.table[mid].0 < discriminant {
202 low = mid + 1;
203 } else {
204 high = mid;
205 }
206 }
207
208 low
209 }
210
211 pub const fn get(&self, variant: &K) -> &V {
217 let idx = self.binary_search(variant);
218 &self.table[idx].1
219 }
220
221 pub const fn get_mut(&mut self, variant: &K) -> &mut V {
227 let idx = self.binary_search(variant);
228 &mut self.table[idx].1
229 }
230
231 pub const fn set(&mut self, variant: &K, value: V) -> V {
240 let idx = self.binary_search(variant);
241 core::mem::replace(&mut self.table[idx].1, value)
242 }
243
244 pub const fn len(&self) -> usize {
246 N
247 }
248
249 pub const fn is_empty(&self) -> bool {
251 false
252 }
253
254 pub fn keys(&self) -> impl Iterator<Item = &K> {
256 self.table
257 .iter()
258 .map(|(discriminant, _)| from_usize(discriminant))
259 }
260
261 pub fn values(&self) -> impl Iterator<Item = &V> {
263 self.table.iter().map(|(_, value)| value)
264 }
265
266 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> {
268 self.table.iter_mut().map(|(_, value)| value)
269 }
270
271 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
273 self.table
274 .iter()
275 .map(|(discriminant, value)| (from_usize(discriminant), value))
276 }
277
278 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
280 self.table
281 .iter_mut()
282 .map(|(discriminant, value)| (from_usize(discriminant), value))
283 }
284
285 pub fn map<U, F>(self, mut f: F) -> EnumTable<K, U, N>
315 where
316 F: FnMut(V) -> U,
317 {
318 let mut builder = builder::EnumTableBuilder::<K, U, N>::new();
319
320 for (discriminant, value) in self.table {
321 let key = from_usize(&discriminant);
322 builder.push(key, f(value));
323 }
324
325 builder.build_to()
326 }
327
328 pub fn into_vec(self) -> Vec<(K, V)> {
355 self.table
356 .into_iter()
357 .map(|(discriminant, value)| (copy_from_usize(&discriminant), value))
358 .collect()
359 }
360
361 pub fn try_from_vec(mut vec: Vec<(K, V)>) -> Result<Self, EnumTableFromVecError<K>> {
393 if vec.len() != N {
394 return Err(EnumTableFromVecError::InvalidSize {
395 expected: N,
396 found: vec.len(),
397 });
398 }
399
400 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
401
402 for variant in K::VARIANTS {
404 if let Some(pos) = vec
405 .iter()
406 .position(|(k, _)| to_usize(k) == to_usize(variant))
407 {
408 let (_, value) = vec.swap_remove(pos);
409 builder.push(variant, value);
410 } else {
411 return Err(EnumTableFromVecError::MissingVariant(copy_variant(variant)));
412 }
413 }
414
415 Ok(builder.build_to())
416 }
417}
418
419impl<K: Enumable, V, const N: usize> EnumTable<K, Option<V>, N> {
420 pub const fn new_fill_with_none() -> Self {
422 let mut builder = builder::EnumTableBuilder::<K, Option<V>, N>::new();
423
424 let mut i = 0;
425 while i < N {
426 let variant = &K::VARIANTS[i];
427 builder.push(variant, None);
428 i += 1;
429 }
430
431 builder.build_to()
432 }
433
434 pub fn clear_to_none(&mut self) {
436 for (_, value) in &mut self.table {
437 *value = None;
438 }
439 }
440}
441
442impl<K: Enumable, V: Copy, const N: usize> EnumTable<K, V, N> {
443 pub const fn new_fill_with_copy(value: V) -> Self {
444 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
445
446 let mut i = 0;
447 while i < N {
448 let variant = &K::VARIANTS[i];
449 builder.push(variant, value);
450 i += 1;
451 }
452
453 builder.build_to()
454 }
455}
456
457impl<K: Enumable, V: Default, const N: usize> EnumTable<K, V, N> {
458 pub fn new_fill_with_default() -> Self {
463 let mut builder = builder::EnumTableBuilder::<K, V, N>::new();
464
465 for variant in K::VARIANTS {
466 builder.push(variant, V::default());
467 }
468
469 builder.build_to()
470 }
471
472 pub fn clear_to_default(&mut self) {
474 for (_, value) in &mut self.table {
475 *value = V::default();
476 }
477 }
478}
479
480#[cfg(test)]
481mod tests {
482 use super::*;
483
484 #[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
485 enum Color {
486 Red = 33,
487 Green = 11,
488 Blue = 222,
489 }
490
491 const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
492 crate::et!(Color, &'static str, |color| match color {
493 Color::Red => "Red",
494 Color::Green => "Green",
495 Color::Blue => "Blue",
496 });
497
498 #[test]
499 fn new_with_fn() {
500 let table =
501 EnumTable::<Color, &'static str, { Color::COUNT }>::new_with_fn(|color| match color {
502 Color::Red => "Red",
503 Color::Green => "Green",
504 Color::Blue => "Blue",
505 });
506
507 assert_eq!(table.get(&Color::Red), &"Red");
508 assert_eq!(table.get(&Color::Green), &"Green");
509 assert_eq!(table.get(&Color::Blue), &"Blue");
510 }
511
512 #[test]
513 fn try_new_with_fn() {
514 let table =
515 EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
516 |color| match color {
517 Color::Red => Ok::<&'static str, core::convert::Infallible>("Red"),
518 Color::Green => Ok("Green"),
519 Color::Blue => Ok("Blue"),
520 },
521 );
522
523 assert!(table.is_ok());
524 let table = table.unwrap();
525
526 assert_eq!(table.get(&Color::Red), &"Red");
527 assert_eq!(table.get(&Color::Green), &"Green");
528 assert_eq!(table.get(&Color::Blue), &"Blue");
529
530 let error_table = EnumTable::<Color, &'static str, { Color::COUNT }>::try_new_with_fn(
531 |color| match color {
532 Color::Red => Ok("Red"),
533 Color::Green => Err("Error on Green"),
534 Color::Blue => Ok("Blue"),
535 },
536 );
537
538 assert!(error_table.is_err());
539 let (variant, error) = error_table.unwrap_err();
540
541 assert_eq!(variant, Color::Green);
542 assert_eq!(error, "Error on Green");
543 }
544
545 #[test]
546 fn checked_new_with_fn() {
547 let table =
548 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
549 match color {
550 Color::Red => Some("Red"),
551 Color::Green => Some("Green"),
552 Color::Blue => Some("Blue"),
553 }
554 });
555
556 assert!(table.is_ok());
557 let table = table.unwrap();
558
559 assert_eq!(table.get(&Color::Red), &"Red");
560 assert_eq!(table.get(&Color::Green), &"Green");
561 assert_eq!(table.get(&Color::Blue), &"Blue");
562
563 let error_table =
564 EnumTable::<Color, &'static str, { Color::COUNT }>::checked_new_with_fn(|color| {
565 match color {
566 Color::Red => Some("Red"),
567 Color::Green => None,
568 Color::Blue => Some("Blue"),
569 }
570 });
571
572 assert!(error_table.is_err());
573 let variant = error_table.unwrap_err();
574
575 assert_eq!(variant, Color::Green);
576 }
577
578 #[test]
579 fn get() {
580 assert_eq!(TABLES.get(&Color::Red), &"Red");
581 assert_eq!(TABLES.get(&Color::Green), &"Green");
582 assert_eq!(TABLES.get(&Color::Blue), &"Blue");
583 }
584
585 #[test]
586 fn get_mut() {
587 let mut table = TABLES;
588 assert_eq!(table.get_mut(&Color::Red), &mut "Red");
589 assert_eq!(table.get_mut(&Color::Green), &mut "Green");
590 assert_eq!(table.get_mut(&Color::Blue), &mut "Blue");
591
592 *table.get_mut(&Color::Red) = "Changed Red";
593 *table.get_mut(&Color::Green) = "Changed Green";
594 *table.get_mut(&Color::Blue) = "Changed Blue";
595
596 assert_eq!(table.get(&Color::Red), &"Changed Red");
597 assert_eq!(table.get(&Color::Green), &"Changed Green");
598 assert_eq!(table.get(&Color::Blue), &"Changed Blue");
599 }
600
601 #[test]
602 fn set() {
603 let mut table = TABLES;
604 assert_eq!(table.set(&Color::Red, "New Red"), "Red");
605 assert_eq!(table.set(&Color::Green, "New Green"), "Green");
606 assert_eq!(table.set(&Color::Blue, "New Blue"), "Blue");
607
608 assert_eq!(table.get(&Color::Red), &"New Red");
609 assert_eq!(table.get(&Color::Green), &"New Green");
610 assert_eq!(table.get(&Color::Blue), &"New Blue");
611 }
612
613 #[test]
614 fn keys() {
615 let keys: Vec<_> = TABLES.keys().collect();
616 assert_eq!(keys, vec![&Color::Green, &Color::Red, &Color::Blue]);
617 }
618
619 #[test]
620 fn values() {
621 let values: Vec<_> = TABLES.values().collect();
622 assert_eq!(values, vec![&"Green", &"Red", &"Blue"]);
623 }
624
625 #[test]
626 fn iter() {
627 let iter: Vec<_> = TABLES.iter().collect();
628 assert_eq!(
629 iter,
630 vec![
631 (&Color::Green, &"Green"),
632 (&Color::Red, &"Red"),
633 (&Color::Blue, &"Blue")
634 ]
635 );
636 }
637
638 #[test]
639 fn iter_mut() {
640 let mut table = TABLES;
641 for (key, value) in table.iter_mut() {
642 *value = match key {
643 Color::Red => "Changed Red",
644 Color::Green => "Changed Green",
645 Color::Blue => "Changed Blue",
646 };
647 }
648 let iter: Vec<_> = table.iter().collect();
649 assert_eq!(
650 iter,
651 vec![
652 (&Color::Green, &"Changed Green"),
653 (&Color::Red, &"Changed Red"),
654 (&Color::Blue, &"Changed Blue")
655 ]
656 );
657 }
658
659 #[test]
660 fn map() {
661 let table = EnumTable::<Color, i32, { Color::COUNT }>::new_with_fn(|color| match color {
662 Color::Red => 1,
663 Color::Green => 2,
664 Color::Blue => 3,
665 });
666
667 let doubled = table.map(|x| x * 2);
668 assert_eq!(doubled.get(&Color::Red), &2);
669 assert_eq!(doubled.get(&Color::Green), &4);
670 assert_eq!(doubled.get(&Color::Blue), &6);
671 }
672
673 #[test]
674 fn into_vec() {
675 let table = TABLES;
676 let vec = table.into_vec();
677
678 assert_eq!(vec.len(), 3);
679 assert!(vec.contains(&(Color::Red, "Red")));
680 assert!(vec.contains(&(Color::Green, "Green")));
681 assert!(vec.contains(&(Color::Blue, "Blue")));
682 }
683
684 #[test]
685 fn try_from_vec() {
686 let vec = vec![
687 (Color::Red, "Red"),
688 (Color::Green, "Green"),
689 (Color::Blue, "Blue"),
690 ];
691
692 let table = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
693 assert_eq!(table.get(&Color::Red), &"Red");
694 assert_eq!(table.get(&Color::Green), &"Green");
695 assert_eq!(table.get(&Color::Blue), &"Blue");
696 }
697
698 #[test]
699 fn try_from_vec_invalid_size() {
700 let vec = vec![
701 (Color::Red, "Red"),
702 (Color::Green, "Green"),
703 ];
705
706 let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
707 assert_eq!(
708 result,
709 Err(crate::EnumTableFromVecError::InvalidSize {
710 expected: 3,
711 found: 2
712 })
713 );
714 }
715
716 #[test]
717 fn try_from_vec_missing_variant() {
718 let vec = vec![
719 (Color::Red, "Red"),
720 (Color::Green, "Green"),
721 (Color::Red, "Duplicate Red"), ];
723
724 let result = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec);
725 assert_eq!(
726 result,
727 Err(crate::EnumTableFromVecError::MissingVariant(Color::Blue))
728 );
729 }
730
731 #[test]
732 fn conversion_roundtrip() {
733 let original = TABLES;
734 let vec = original.into_vec();
735 let reconstructed = EnumTable::<Color, &str, { Color::COUNT }>::try_from_vec(vec).unwrap();
736
737 assert_eq!(reconstructed.get(&Color::Red), &"Red");
738 assert_eq!(reconstructed.get(&Color::Green), &"Green");
739 assert_eq!(reconstructed.get(&Color::Blue), &"Blue");
740 }
741
742 #[test]
743 fn binary_search_correct() {
744 let table = TABLES;
745 assert_eq!(table.binary_search(&Color::Red), 1);
746 assert_eq!(table.binary_search(&Color::Green), 0);
747 assert_eq!(table.binary_search(&Color::Blue), 2);
748 }
749
750 macro_rules! run_variants_test {
751 ($($variant:ident),+) => {{
752 #[derive(Debug, Clone, Copy, PartialEq, Eq, Enumable)]
753 #[repr(u8)]
754 enum Test {
755 $($variant,)*
756 }
757
758 let map = EnumTable::<Test, &'static str, { Test::COUNT }>::new_with_fn(|t| match t {
759 $(Test::$variant => stringify!($variant),)*
760 });
761 $(
762 assert_eq!(map.get(&Test::$variant), &stringify!($variant));
763 )*
764 }};
765 }
766
767 #[test]
768 fn binary_search_correct_variants() {
769 run_variants_test!(A);
770 run_variants_test!(A, B);
771 run_variants_test!(A, B, C);
772 run_variants_test!(A, B, C, D);
773 run_variants_test!(A, B, C, D, E);
774 }
775
776 #[test]
777 fn binary_search_out_missing_order() {
778 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
779 enum MissingOrder {
780 Red = 1,
781 Green = 2,
782 Blue = 3,
783 Yellow = 4,
784 }
785
786 impl Enumable for MissingOrder {
787 const VARIANTS: &'static [Self] = &[Self::Yellow, Self::Blue, Self::Green, Self::Red];
789 const COUNT: usize = 4;
790 }
791
792 let table = EnumTable::<MissingOrder, &'static str, { MissingOrder::COUNT }>::new_with_fn(
793 |color| match color {
794 MissingOrder::Red => "Red",
795 MissingOrder::Green => "Green",
796 MissingOrder::Blue => "Blue",
797 MissingOrder::Yellow => "Yellow",
798 },
799 );
800
801 assert_ne!(table.binary_search(&MissingOrder::Red), 3);
802 assert_ne!(table.binary_search(&MissingOrder::Green), 2);
803 assert_ne!(table.binary_search(&MissingOrder::Blue), 1);
804 assert_ne!(table.binary_search(&MissingOrder::Yellow), 0);
805
806 assert_ne!(table.get(&MissingOrder::Red), &"Red");
807 }
808}