1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! HashMap and HashSet like interfaces for enumerations backed by an array.
//!
//! `enumap` is `no_std` compatible, dependency and proc macro free for blazingly fast compilation speeds.
//!
//! ```
//! use enumap::{EnumMap, EnumSet};
//!
//! enumap::enumap! {
//! /// A beautiful fruit, ready to be sold.
//! #[derive(Debug)]
//! enum Fruit {
//! Orange,
//! Banana,
//! Grape,
//! }
//! }
//!
//! // A fruit shop: fruit -> stock.
//! let mut shop = EnumMap::new();
//! let mut orders = EnumSet::new();
//!
//! shop.insert(Fruit::Orange, 100);
//! shop.insert(Fruit::Banana, 200);
//!
//! for (fruit, amount) in &shop {
//! println!("There are {amount} {fruit:?}s in stock!");
//! }
//!
//! if !shop.contains_key(Fruit::Grape) {
//! println!("Sorry no grapes in stock :(");
//! orders.insert(Fruit::Grape);
//! }
//!
//! for fruit in &orders {
//! println!("{fruit:?} needs to be ordered!");
//! }
//! ```
//!
//! # Differences and Alternatives
//!
//! Why would you want to use `enumap` over one of the alternatives:
//!
//! - `strum`'s `EnumTable`
//! - `array_map`
//! - `enum_map`
//!
//! All of these crates expect you to fully initialize the mapping on
//! construction of the map/table. `enumap` is always backed by an optional
//! storage and therefor can provide a much better API which follows the `HashMap` API.
//!
//! `enumap` does not rely on a proc macro and is dependency free
//! which makes it overall a more lightweight and faster compiling dependency.
//!
//! These crates do not have support for sets.
//!
//! # Use Niches
//!
//! The map is backed by an array of options `[Option<V>; N]`,
//! consider using values with a gurantueed niche to optimize the size of the map:
//!
//! ```
//! use enumap::EnumMap;
//! use std::num::NonZeroUsize;
//!
//! enumap::enumap! {
//! #[derive(Debug)]
//! enum Fruit {
//! Orange,
//! Banana
//! }
//! }
//!
//! assert_eq!(std::mem::size_of::<EnumMap<2, Fruit, usize>>(), 32);
//! assert_eq!(std::mem::size_of::<EnumMap<2, Fruit, NonZeroUsize>>(), 16);
//! ```
//!
//! # Advanced: Implementing Enum
//!
//! While the crate was built with enums in mind, it is just a generic map
//! implementation backed by an array which only requires a bijective mapping
//! of items to an array index.
//!
//! ```
//! use enumap::{Enum, EnumMap};
//!
//! #[derive(Copy, Clone)]
//! struct ZeroToTen(u8);
//!
//! impl ZeroToTen {
//! fn new(num: u8) -> Option<Self> {
//! matches!(num, 0..=9).then_some(Self(num))
//! }
//! }
//!
//! impl Enum<10> for ZeroToTen {
//! fn from_index(index: usize) -> Option<Self> {
//! index.try_into().ok().and_then(Self::new)
//! }
//!
//! fn to_index(value: Self) -> usize {
//! value.0 as usize
//! }
//! }
//!
//! let zero = ZeroToTen::new(0).unwrap();
//! let five = ZeroToTen::new(5).unwrap();
//! let nine = ZeroToTen::new(9).unwrap();
//!
//! let mut map = EnumMap::from([
//! (zero, "foo"),
//! (nine, "bar"),
//! ]);
//!
//! assert_eq!(map[zero], "foo");
//! assert_eq!(map[nine], "bar");
//! assert_eq!(map.get(five), None);
//! ```
//!
//! Of course this is also possible for enums with attached data:
//!
//! ```
//! use enumap::{Enum, EnumMap};
//!
//! #[derive(Copy, Clone)]
//! enum Foo {
//! Always,
//! Maybe(bool),
//! }
//!
//! impl Enum<3> for Foo {
//! fn from_index(index: usize) -> Option<Self> {
//! match index {
//! 0 => Some(Self::Always),
//! 1 => Some(Self::Maybe(true)),
//! 2 => Some(Self::Maybe(false)),
//! _ => None,
//! }
//! }
//!
//! fn to_index(value: Self) -> usize {
//! match value {
//! Self::Always => 0,
//! Self::Maybe(true) => 1,
//! Self::Maybe(false) => 2,
//! }
//! }
//! }
//!
//! let mut map = EnumMap::from([
//! (Foo::Always, "foo"),
//! (Foo::Maybe(true), "bar"),
//! ]);
//!
//! assert_eq!(map[Foo::Always], "foo");
//! assert_eq!(map[Foo::Maybe(true)], "bar");
//! assert_eq!(map.get(Foo::Maybe(false)), None);
//! ```
#![no_std]
mod enum_macro;
#[cfg(feature = "serde")]
mod serde;
pub mod map;
pub mod set;
pub use self::map::EnumMap;
pub use self::set::EnumSet;
/// Enum type, usually implemented using the [`enumap`] macro.
///
/// Any enumeration used by [`EnumMap`] must implement this trait.
///
/// Failures in implementing the trait will not result in undefined behaviour
/// but may result in panics and invalid results.
pub trait Enum<const LENGTH: usize>: Copy + Sized {
/// Length of the enum.
///
/// Equivalent to the const generic length.
const LENGTH: usize = LENGTH;
/// Converts an index to an enum variant.
///
/// Passed `index` must be in range `0..LENGTH`.
fn from_index(index: usize) -> Option<Self>;
/// Converts an enum variant to an index.
///
/// Returned index must be in range `0..LENGTH`.
fn to_index(value: Self) -> usize;
}