From: Lizzy Fleckenstein Date: Wed, 1 Feb 2023 14:19:44 +0000 (+0100) Subject: Add serialize_as_map X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;p=enumset.git Add serialize_as_map --- diff --git a/enumset/tests/serde.rs b/enumset/tests/serde.rs index c67ebf3..105d674 100644 --- a/enumset/tests/serde.rs +++ b/enumset/tests/serde.rs @@ -16,6 +16,13 @@ pub enum ListEnum { A, B, C, D, E, F, G, H, } +#[derive(Serialize, Deserialize, EnumSetType, Debug)] +#[enumset(serialize_as_map)] +#[serde(crate="serde2")] +pub enum MapEnum { + A, B, C, D, E, F, G, H, +} + #[derive(EnumSetType, Debug)] #[enumset(serialize_repr = "u128")] pub enum ReprEnum { @@ -77,14 +84,19 @@ fn test_deny_unknown() { fn test_json_reprs() { assert_eq!(ListEnum::A | ListEnum::C | ListEnum::F, serde_json::from_str::>(r#"["A","C","F"]"#).unwrap()); + assert_eq!(MapEnum::A | MapEnum::C | MapEnum::F, + serde_json::from_str::>(r#"{"A":true,"C":true,"F":true}"#).unwrap()); assert_eq!(ReprEnum::A | ReprEnum::C | ReprEnum::D, serde_json::from_str::>("13").unwrap()); assert_eq!(r#"["A","C","F"]"#, serde_json::to_string(&(ListEnum::A | ListEnum::C | ListEnum::F)).unwrap()); + assert_eq!(r#"{"A":true,"C":true,"F":true}"#, + serde_json::to_string(&(MapEnum::A | MapEnum::C | MapEnum::F)).unwrap()); assert_eq!("13", serde_json::to_string(&(ReprEnum::A | ReprEnum::C | ReprEnum::D)).unwrap()); } tests!(list_enum, serde_test_simple!(ListEnum, !0)); +tests!(map_enum, serde_test_simple!(MapEnum, !0)); tests!(repr_enum, serde_test!(ReprEnum, 16)); tests!(deny_unknown_enum, serde_test_simple!(DenyUnknownEnum, 16)); diff --git a/enumset_derive/src/lib.rs b/enumset_derive/src/lib.rs index 88ac367..04fc0df 100644 --- a/enumset_derive/src/lib.rs +++ b/enumset_derive/src/lib.rs @@ -24,6 +24,7 @@ struct EnumsetAttrs { #[darling(default)] repr: Option, serialize_as_list: bool, + serialize_as_map: bool, serialize_deny_unknown: bool, #[darling(default)] serialize_repr: Option, @@ -72,6 +73,8 @@ struct EnumSetInfo { no_super_impls: bool, /// Serialize the enum as a list. serialize_as_list: bool, + /// Serialize the enum as a map. + serialize_as_map: bool, /// Disallow unknown bits while deserializing the enum. serialize_deny_unknown: bool, } @@ -94,6 +97,7 @@ impl EnumSetInfo { no_ops: attrs.no_ops, no_super_impls: attrs.no_super_impls, serialize_as_list: attrs.serialize_as_list, + serialize_as_map: attrs.serialize_as_map, serialize_deny_unknown: attrs.serialize_deny_unknown, } } @@ -375,6 +379,45 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream { de.deserialize_seq(Visitor) } } + } else if info.serialize_as_map { + let expecting_str = format!("a map from {} to bool", name); + quote! { + fn serialize( + set: #enumset::EnumSet<#name>, ser: S, + ) -> #core::result::Result { + use #serde::ser::SerializeMap; + let mut map = ser.serialize_map(#core::prelude::v1::Some(set.len()))?; + for bit in set { + map.serialize_entry(&bit, &true)?; + } + map.end() + } + fn deserialize<'de, D: #serde::Deserializer<'de>>( + de: D, + ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { + struct Visitor; + impl <'de> #serde::de::Visitor<'de> for Visitor { + type Value = #enumset::EnumSet<#name>; + fn expecting( + &self, formatter: &mut #core::fmt::Formatter, + ) -> #core::fmt::Result { + write!(formatter, #expecting_str) + } + fn visit_map( + mut self, mut map: A, + ) -> #core::result::Result where + A: #serde::de::MapAccess<'de> + { + let mut accum = #enumset::EnumSet::<#name>::new(); + while let #core::prelude::v1::Some((val, true)) = map.next_entry::<#name, bool>()? { + accum |= val; + } + #core::prelude::v1::Ok(accum) + } + } + de.deserialize_map(Visitor) + } + } } else { let serialize_repr = info.serde_repr(); let check_unknown = if info.serialize_deny_unknown {