diff --git a/enum-collections/Cargo.toml b/enum-collections/Cargo.toml index 74696b0..1669401 100644 --- a/enum-collections/Cargo.toml +++ b/enum-collections/Cargo.toml @@ -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" diff --git a/enum-collections/src/enummap.rs b/enum-collections/src/enummap.rs index a5c6749..f4f0ac8 100644 --- a/enum-collections/src/enummap.rs +++ b/enum-collections/src/enummap.rs @@ -199,8 +199,8 @@ mod macro_test { /// /// ``` pub struct EnumMap { - data: [V; N], - _key: PhantomData, + pub(crate) data: [V; N], + pub(crate) _key: PhantomData, } impl EnumMap { @@ -409,7 +409,7 @@ impl EnumMap { /// /// ``` #[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]) @@ -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); + } + } } diff --git a/enum-collections/src/lib.rs b/enum-collections/src/lib.rs index 78089ef..5f1a915 100644 --- a/enum-collections/src/lib.rs +++ b/enum-collections/src/lib.rs @@ -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; diff --git a/enum-collections/src/serde.rs b/enum-collections/src/serde.rs new file mode 100644 index 0000000..d9b41aa --- /dev/null +++ b/enum-collections/src/serde.rs @@ -0,0 +1,76 @@ +use serde::{de::Visitor, Deserialize, Serialize}; +use std::{any::type_name, marker::PhantomData}; + +use crate::{EnumMap, Enumerated}; + +impl Serialize for EnumMap { + #[inline] + fn serialize(&self, serializer: S) -> Result + 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; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + formatter, + "EnumMap<{}, {}>", + type_name::(), + type_name::() + ) + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut data = std::array::from_fn(|_| V::default()); + while let Some((k, v)) = map.next_entry::()? { + 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 +{ + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let visitor = EnumMapVisitor { + marker: PhantomData, + }; + deserializer.deserialize_map(visitor) + } +}