Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion enum-collections/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ eq = []
# Generates a static array of all variants for the EnumMap. This is required for the `Debug` implementation.
variants = ["enum-collections-macros/variants"]
# Implementations of the `Enumerated` trait for common data types.
ext=[]
ext = []
# Serde's `Serialize` and `Deserialize` trait implementations
serde = ["variants", "dep:serde"]

[dependencies]
enum-collections-macros = { path = "../enum-collections-macros", version = "1.0.1" }
serde = { version = "1.0", optional = true }

[dev-dependencies]
criterion = "0.5.1"
ron = "0.10.1"
serde = { version = "1.0", features = ["derive"] }

[[bench]]
name = "enummap"
Expand Down
53 changes: 50 additions & 3 deletions enum-collections/src/enummap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ mod macro_test {
///
/// ```
pub struct EnumMap<K: Enumerated, V, const N: usize> {
data: [V; N],
_key: PhantomData<K>,
pub(crate) data: [V; N],
pub(crate) _key: PhantomData<K>,
}

impl<K: Enumerated, V: Default, const N: usize> EnumMap<K, V, N> {
Expand Down Expand Up @@ -409,7 +409,7 @@ impl<K: Enumerated, V, const N: usize> EnumMap<K, V, N> {
///
/// ```
#[cfg(feature = "variants")]
pub fn new_inspect(default: fn(&K) -> V) -> Self {
pub fn new_inspect(mut default: impl FnMut(&K) -> V) -> Self {
let init_fn = |index| {
// Finds the enum variant by its index, as the array is sorted by discriminants in ascending order.
default(&K::VARIANTS[index])
Expand Down Expand Up @@ -691,4 +691,51 @@ mod tests {
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);
}
}
}
2 changes: 2 additions & 0 deletions enum-collections/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ mod enumerated;
mod enummap;
#[cfg(feature = "ext")]
mod ext;
#[cfg(feature = "serde")]
mod serde;

pub use crate::enumerated::Enumerated;
pub use crate::enummap::EnumMap;
Expand Down
76 changes: 76 additions & 0 deletions enum-collections/src/serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use serde::{de::Visitor, Deserialize, Serialize};
use std::{any::type_name, marker::PhantomData};

use crate::{EnumMap, Enumerated};

impl<K: Enumerated + Serialize, V: Serialize, const N: usize> Serialize for EnumMap<K, V, N> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_map(self.iter_kv())
}
}

struct EnumMapVisitor<
'de,
K: Enumerated + Deserialize<'de> + PartialEq,
V: Deserialize<'de> + Default,
const N: usize,
> {
marker: PhantomData<&'de (K, V)>,
}

impl<
'de,
K: Enumerated + Deserialize<'de> + PartialEq,
V: Deserialize<'de> + Default,
const N: usize,
> Visitor<'de> for EnumMapVisitor<'de, K, V, N>
{
type Value = EnumMap<K, V, N>;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
formatter,
"EnumMap<{}, {}>",
type_name::<K>(),
type_name::<V>()
)
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut data = std::array::from_fn(|_| V::default());
while let Some((k, v)) = map.next_entry::<K, V>()? {
data[k.position()] = v;
}

Ok(EnumMap {
data,
_key: PhantomData,
})
}
}

impl<
'de,
K: Enumerated + Deserialize<'de> + PartialEq,
V: Deserialize<'de> + Default + 'de,
const N: usize,
> Deserialize<'de> for EnumMap<K, V, N>
{
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let visitor = EnumMapVisitor {
marker: PhantomData,
};
deserializer.deserialize_map(visitor)
}
}