enumap 0.3.0

A HashMap and HashSet like interface for enums backed by an array
Documentation
use core::marker::PhantomData;

use serde::{
    de,
    ser::{SerializeMap, SerializeSeq},
    Deserialize, Serialize,
};

use crate::{Enum, EnumMap, EnumSet};

impl<'de, const LENGTH: usize, E: Enum<LENGTH>, V> Deserialize<'de> for EnumMap<LENGTH, E, V>
where
    E: Deserialize<'de>,
    V: Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Visitor<const LENGTH: usize, E: Enum<LENGTH>, V>(PhantomData<EnumMap<LENGTH, E, V>>);

        impl<'de, const LENGTH: usize, E: Enum<LENGTH>, V> de::Visitor<'de> for Visitor<LENGTH, E, V>
        where
            E: Deserialize<'de>,
            V: Deserialize<'de>,
        {
            type Value = EnumMap<LENGTH, E, V>;

            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
                formatter.write_str("a map")
            }

            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
            where
                A: de::MapAccess<'de>,
            {
                let mut result = EnumMap::new();
                while let Some((key, value)) = map.next_entry()? {
                    result.insert(key, value);
                }
                Ok(result)
            }
        }

        deserializer.deserialize_map(Visitor(PhantomData))
    }
}

impl<const LENGTH: usize, E: Enum<LENGTH>> Serialize for EnumSet<LENGTH, E>
where
    E: Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let mut seq = serializer.serialize_seq(Some(self.len()))?;
        for value in self {
            seq.serialize_element(&value)?;
        }
        seq.end()
    }
}

impl<'de, const LENGTH: usize, E: Enum<LENGTH>> Deserialize<'de> for EnumSet<LENGTH, E>
where
    E: Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct Visitor<const LENGTH: usize, E: Enum<LENGTH>>(PhantomData<EnumSet<LENGTH, E>>);

        impl<'de, const LENGTH: usize, E: Enum<LENGTH>> de::Visitor<'de> for Visitor<LENGTH, E>
        where
            E: Deserialize<'de>,
        {
            type Value = EnumSet<LENGTH, E>;

            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
                formatter.write_str("an array")
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: de::SeqAccess<'de>,
            {
                let mut result = EnumSet::new();
                while let Some(value) = seq.next_element()? {
                    result.insert(value);
                }
                Ok(result)
            }
        }

        deserializer.deserialize_seq(Visitor(PhantomData))
    }
}

impl<const LENGTH: usize, E: Enum<LENGTH>, V> Serialize for EnumMap<LENGTH, E, V>
where
    E: Serialize,
    V: Serialize,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let mut map = serializer.serialize_map(Some(self.len()))?;
        for (key, value) in self {
            map.serialize_entry(&key, value)?;
        }
        map.end()
    }
}

#[cfg(test)]
mod tests {
    use serde::{Deserialize, Serialize};

    use crate::{enumap, Enum, EnumMap, EnumSet};

    enumap! {
        #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
        #[serde(rename_all = "lowercase")]
        enum Foo {
            A,
            B,
            C,
            D,
        }
    }

    #[test]
    fn test_enum_map_serialize() {
        let map = EnumMap::from([(Foo::C, 3), (Foo::B, 2), (Foo::A, 1)]);

        let s = serde_json::to_string(&map).unwrap();
        assert_eq!(s, r#"{"a":1,"b":2,"c":3}"#);
    }

    #[test]
    fn test_enum_map_serialize_empty() {
        let map = EnumMap::<{ Foo::LENGTH }, Foo, i32>::new();

        let s = serde_json::to_string(&map).unwrap();
        assert_eq!(s, r#"{}"#);
    }

    #[test]
    fn test_enum_map_deserialize() {
        let m: EnumMap<{ Foo::LENGTH }, Foo, i32> =
            serde_json::from_str(r#"{"a":1,"b":2,"c":3}"#).unwrap();
        assert_eq!(m, EnumMap::from([(Foo::C, 3), (Foo::B, 2), (Foo::A, 1)]));
    }

    #[test]
    fn test_enum_map_deserialize_empty() {
        let m: EnumMap<{ Foo::LENGTH }, Foo, i32> = serde_json::from_str(r#"{}"#).unwrap();
        assert_eq!(m, EnumMap::new());
    }

    #[test]
    fn test_enum_set_serialize() {
        let set = EnumSet::from([Foo::C, Foo::B, Foo::A]);

        let s = serde_json::to_string(&set).unwrap();
        assert_eq!(s, r#"["a","b","c"]"#);
    }

    #[test]
    fn test_enum_set_serialize_empty() {
        let set = EnumSet::<{ Foo::LENGTH }, Foo>::new();

        let s = serde_json::to_string(&set).unwrap();
        assert_eq!(s, r#"[]"#);
    }

    #[test]
    fn test_enum_set_deserialize() {
        let m: EnumSet<{ Foo::LENGTH }, Foo> = serde_json::from_str(r#"["a","b","c"]"#).unwrap();
        assert_eq!(m, EnumSet::from([Foo::C, Foo::B, Foo::A]));
    }

    #[test]
    fn test_enum_set_deserialize_empty() {
        let m: EnumSet<{ Foo::LENGTH }, Foo> = serde_json::from_str(r#"[]"#).unwrap();
        assert_eq!(m, EnumSet::new());
    }
}