use crate::{EnumTable, Enumable};
impl<K, V, const N: usize> serde::Serialize for EnumTable<K, V, N>
where
K: Enumable + serde::Serialize,
V: serde::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(N))?;
for (key, value) in self.iter() {
map.serialize_entry(key, value)?;
}
map.end()
}
}
impl<'de, K, V, const N: usize> serde::Deserialize<'de> for EnumTable<K, V, N>
where
K: Enumable + serde::Deserialize<'de> + core::fmt::Debug,
V: serde::Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use core::marker::PhantomData;
use serde::de::{MapAccess, Visitor};
struct EnumTableVisitor<K, V, const N: usize> {
_phantom: PhantomData<(K, V)>,
}
impl<'de, K, V, const N: usize> Visitor<'de> for EnumTableVisitor<K, V, N>
where
K: Enumable + serde::Deserialize<'de> + core::fmt::Debug,
V: serde::Deserialize<'de>,
{
type Value = EnumTable<K, V, N>;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("a map with all enum variants as keys")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut slots: [Option<V>; N] = core::array::from_fn(|_| None);
let mut count = 0;
while let Some((key, value)) = map.next_entry::<K, V>()? {
slots[key.variant_index()] = Some(value);
count += 1;
}
if count != N {
return Err(serde::de::Error::invalid_length(
count,
&format!("expected {N} entries").as_str(),
));
}
let table = crate::intrinsics::try_collect_array(|i| {
slots[i].take().ok_or_else(|| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&format!("{:?}", K::VARIANTS[i])),
&"all enum variants must be present",
)
})
});
match table {
Ok(arr) => Ok(EnumTable::new(arr)),
Err(e) => Err(e),
}
}
}
deserializer.deserialize_map(EnumTableVisitor::<K, V, N> {
_phantom: PhantomData,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, Copy, Enumable, serde::Serialize, serde::Deserialize)]
enum Color {
Red,
Green,
Blue,
}
const TABLES: EnumTable<Color, &'static str, { Color::COUNT }> =
crate::et!(Color, &'static str, |color| match color {
Color::Red => "Red",
Color::Green => "Green",
Color::Blue => "Blue",
});
#[test]
fn serde_serialize() {
let json = serde_json::to_string(&TABLES).unwrap();
assert!(json.contains(r#""Red":"Red""#));
assert!(json.contains(r#""Green":"Green""#));
assert!(json.contains(r#""Blue":"Blue""#));
}
#[test]
fn serde_deserialize() {
let json = r#"{"Red":"Red","Green":"Green","Blue":"Blue"}"#;
let table: EnumTable<Color, &str, { Color::COUNT }> = serde_json::from_str(json).unwrap();
assert_eq!(table.get(&Color::Red), &"Red");
assert_eq!(table.get(&Color::Green), &"Green");
assert_eq!(table.get(&Color::Blue), &"Blue");
}
#[test]
fn serde_roundtrip() {
let original = TABLES;
let json = serde_json::to_string(&original).unwrap();
let deserialized: EnumTable<Color, &str, { Color::COUNT }> =
serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn serde_missing_variant_error() {
let json = r#"{"Red":"Red","Green":"Green"}"#;
let result: Result<EnumTable<Color, &str, { Color::COUNT }>, _> =
serde_json::from_str(json);
assert!(result.is_err());
}
}