use crate::Enumerated;
use std::{
array,
fmt::Debug,
marker::PhantomData,
ops::{Index, IndexMut},
};
#[macro_export]
macro_rules! em {
($ktp:ty, $vtp:ty, $($x:ident=>$y:expr),* ) => {
enum_collections::EnumMap::<$ktp, $vtp, {<$ktp>::SIZE}>::new_inspect(|letter| {
match letter {
$(<$ktp>::$x => $y,)*
}
})
};
}
#[macro_export]
macro_rules! em_default {
($ktp:ty, $vtp:ty, $($x:ident=>$y:expr),* ) => {
EnumMap::<$ktp, $vtp, {<$ktp>::SIZE}>::new_inspect(|letter| {
match letter {
$(<$ktp>::$x => $y,)*
_ => Default::default(),
}
})
};
}
#[macro_export]
macro_rules! em_option {
($ktp:ty, $vtp:ty, $($x:ident=>$y:expr),* ) => {
EnumMap::<$ktp, Option<$vtp>, {<$ktp>::SIZE}>::new_inspect(|letter| {
match letter {
$(<$ktp>::$x => Some($y),)*
_ => None,
}
})
};
}
#[cfg(test)]
mod macro_test {
use crate::{EnumMap, Enumerated};
#[derive(Enumerated)]
enum Letter {
A,
B,
}
#[test]
fn test_macro() {
let enum_map = em_default!(Letter, i32, A=>42);
assert_eq!(42, enum_map[Letter::A]);
assert_eq!(i32::default(), enum_map[Letter::B]);
}
}
pub struct EnumMap<K: Enumerated, V, const N: usize> {
pub(crate) data: [V; N],
pub(crate) _key: PhantomData<K>,
}
impl<K: Enumerated, V: Default, const N: usize> EnumMap<K, V, N> {
pub fn new_default() -> Self {
Self {
data: array::from_fn(|_| V::default()),
_key: PhantomData,
}
}
pub fn clear_set_default(&mut self) {
for idx in 0..self.data.len() {
self.data[idx] = V::default();
}
}
}
impl<K: Enumerated, V, const N: usize> EnumMap<K, Option<V>, N> {
pub fn new_option() -> Self {
Self {
data: array::from_fn(|_| None),
_key: PhantomData,
}
}
pub fn clear_set_none(&mut self) {
for idx in 0..self.data.len() {
self.data[idx] = None;
}
}
}
impl<K: Enumerated, V, const N: usize> EnumMap<K, V, N> {
pub fn new(default: fn() -> V) -> Self {
Self {
data: array::from_fn(|_| default()),
_key: PhantomData,
}
}
pub fn set_all(&mut self, val_provider: fn() -> V) {
for idx in 0..self.data.len() {
self.data[idx] = val_provider();
}
}
#[cfg(feature = "variants")]
pub fn iter_kv(&self) -> std::iter::Zip<std::slice::Iter<'_, K>, std::slice::Iter<'_, V>> {
K::VARIANTS.iter().zip(self.data.iter())
}
#[cfg(feature = "variants")]
pub fn iter_kv_mut(
&mut self,
) -> std::iter::Zip<std::slice::Iter<'_, K>, std::slice::IterMut<'_, V>> {
K::VARIANTS.iter().zip(self.data.iter_mut())
}
#[cfg(feature = "variants")]
pub fn iter(&self) -> std::slice::Iter<'_, V> {
self.data.iter()
}
#[cfg(feature = "variants")]
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, V> {
self.data.iter_mut()
}
#[cfg(feature = "variants")]
pub fn new_inspect(mut default: impl FnMut(&K) -> V) -> Self {
let init_fn = |index| {
default(&K::VARIANTS[index])
};
Self {
data: array::from_fn(init_fn),
_key: PhantomData,
}
}
}
impl<K: Enumerated, V: Copy, const N: usize> EnumMap<K, V, N> {
pub const fn new_with_all(value: V) -> Self {
Self {
data: [value; N],
_key: PhantomData,
}
}
}
impl<K: Enumerated, V, const N: usize> Index<K> for EnumMap<K, V, N> {
type Output = V;
fn index(&self, key: K) -> &Self::Output {
&self.data[key.position()]
}
}
impl<K: Enumerated, V, const N: usize> IndexMut<K> for EnumMap<K, V, N> {
fn index_mut(&mut self, key: K) -> &mut Self::Output {
&mut self.data[key.position()]
}
}
#[cfg(feature = "debug")]
impl<K: Enumerated + Debug, V: Debug, const N: usize> std::fmt::Debug for EnumMap<K, V, N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map()
.entries(
K::VARIANTS
.iter()
.enumerate()
.map(|(index, variant)| (variant, &self.data[index])),
)
.finish()
}
}
#[cfg(feature = "eq")]
mod eq {
use super::{EnumMap, Enumerated};
impl<K: Enumerated, V: PartialEq, const N: usize> PartialEq for EnumMap<K, V, N> {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<K: Enumerated, V: Eq, const N: usize> Eq for EnumMap<K, V, N> {}
}
impl<K: Enumerated, V: Clone, const N: usize> Clone for EnumMap<K, V, N> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
_key: PhantomData,
}
}
}
impl<K: Enumerated, V: Copy, const N: usize> Copy for EnumMap<K, V, N> {}
#[cfg(test)]
mod tests {
use crate::Enumerated;
use crate::enummap::EnumMap;
#[derive(Enumerated, Debug)]
pub(super) enum Letter {
A,
B,
}
#[test]
fn index() {
let mut enum_map = EnumMap::<Letter, i32, { Letter::SIZE }>::new_default();
assert_eq!(0, enum_map[Letter::A]);
enum_map[Letter::A] = 42;
assert_eq!(42, enum_map[Letter::A]);
assert_eq!(i32::default(), enum_map[Letter::B]);
}
#[test]
fn constructor_option() {
let mut enum_map = EnumMap::<Letter, Option<i32>, { Letter::SIZE }>::new_option();
assert_eq!(None, enum_map[Letter::A]);
assert_eq!(None, enum_map[Letter::B]);
enum_map[Letter::A] = Some(42);
assert_eq!(Some(42), enum_map[Letter::A]);
}
#[test]
fn non_default_type() {
#[derive(PartialEq, Eq, Debug)]
struct NonDefault;
let enum_map = EnumMap::<Letter, NonDefault, { Letter::SIZE }>::new(|| NonDefault);
assert_eq!(NonDefault, enum_map[Letter::A]);
assert_eq!(NonDefault, enum_map[Letter::B]);
}
#[test]
fn heap_allocation() {
let boxed_map = Box::new(EnumMap::<Letter, i32, { Letter::SIZE }>::new_default());
assert!(EnumMap::<Letter, i32, { Letter::SIZE }>::new_default() == *boxed_map);
}
#[cfg(feature = "variants")]
mod variants {
use super::*;
#[test]
fn variants() {
assert_eq!(2, Letter::VARIANTS.len());
Letter::VARIANTS
.iter()
.for_each(|letter| println!("{:?}", letter));
}
}
#[cfg(feature = "debug")]
mod debug {
use crate::{EnumMap, Enumerated};
#[derive(Enumerated, Debug)]
pub(super) enum LetterDebugDerived {
A,
B,
}
#[test]
fn debug() {
let enum_map =
EnumMap::<LetterDebugDerived, i32, { LetterDebugDerived::SIZE }>::new(|| 42);
assert_eq!("{A: 42, B: 42}", format!("{:?}", enum_map));
}
}
#[cfg(feature = "serde")]
mod serde {
use serde::{Deserialize, Serialize};
use crate::{EnumMap, Enumerated};
#[derive(Enumerated, Serialize, Deserialize, PartialEq, Debug)]
pub(super) enum LetterSerde {
A,
B,
}
#[test]
fn serialize() {
let mut enum_map: EnumMap<_, Option<_>, { LetterSerde::SIZE }> = EnumMap::new_option();
enum_map[LetterSerde::A] = Some(10);
enum_map[LetterSerde::B] = Some(11);
let serialized = ron::to_string(&enum_map).unwrap();
assert_eq!("{A:Some(10),B:Some(11)}", serialized);
}
#[test]
fn deserialize() {
let str = "{A:Some(10),B:Some(11)}";
let enum_map: EnumMap<_, Option<_>, { LetterSerde::SIZE }> =
ron::from_str(str).unwrap();
let mut correct_enum_map: EnumMap<_, Option<_>, { LetterSerde::SIZE }> =
EnumMap::new_option();
correct_enum_map[LetterSerde::A] = Some(10);
correct_enum_map[LetterSerde::B] = Some(11);
assert_eq!(enum_map, correct_enum_map);
}
#[test]
fn serde() {
let mut enum_map: EnumMap<_, Option<_>, { LetterSerde::SIZE }> = EnumMap::new_option();
enum_map[LetterSerde::A] = Some(10);
let serialized = ron::to_string(&enum_map).unwrap();
let new_enum_map: EnumMap<_, Option<_>, { LetterSerde::SIZE }> =
ron::from_str(&serialized).unwrap();
assert_eq!(enum_map, new_enum_map);
}
}
}