]> git.lizzy.rs Git - enumset.git/commitdiff
Implement option to serialize enumsets as lists.
authorLymia Aluysia <lymia@lymiahugs.com>
Mon, 11 Mar 2019 19:46:59 +0000 (14:46 -0500)
committerLymia Aluysia <lymia@lymiahugs.com>
Mon, 11 Mar 2019 19:46:59 +0000 (14:46 -0500)
enumset/Cargo.toml
enumset/src/lib.rs
enumset_derive/Cargo.toml
enumset_derive/src/lib.rs

index 77b17f0f9beda333bdde6ed223515fe9cbc35d6e..21a25dcf6c54cb3a7d84b64c4760a2ca70deed19 100644 (file)
@@ -15,11 +15,13 @@ license = "MIT/Apache-2.0"
 
 [features]
 nightly = ["enumset_derive/nightly"]
+serde = ["serde2", "enumset_derive/serde"]
 
 [dependencies]
 enumset_derive = { version = "0.2", path = "../enumset_derive" }
 num-traits = { version = "0.2", default-features = false }
-serde = { version = "1.0", default-features = false, optional = true }
+serde2 = { package = "serde", version = "1.0", default-features = false, optional = true }
 
 [dev-dependencies]
 bincode = { version = "1.0", features = ["i128"] }
+serde_derive = { version = "1.0" }
index b3e8b039a9ecb2daa7fe23dcb5f7d00032656664..710ae260a485948a6bddef9233f4c61f44a05196 100644 (file)
@@ -76,7 +76,7 @@
 #[cfg(test)] extern crate core;
 extern crate enumset_derive;
 extern crate num_traits;
-#[cfg(feature = "serde")] extern crate serde;
+#[cfg(feature = "serde")] extern crate serde2 as serde;
 
 pub use enumset_derive::*;
 mod enumset { pub use super::*; }
@@ -93,7 +93,6 @@ use num_traits::*;
 pub mod internal {
     use super::*;
 
-    #[doc(hidden)]
     /// A struct used to type check [`enum_set!`].
     pub struct EnumSetSameTypeHack<'a, T: EnumSetType + 'static> {
         pub unified: &'a [T],
@@ -102,6 +101,9 @@ pub mod internal {
 
     /// A reexport of core to allow our macros to be generic to std vs core.
     pub extern crate core;
+
+    /// A reexport of serde so there is no requirement to depend on serde.
+    #[cfg(feature = "serde")] pub extern crate serde2 as serde;
 }
 
 mod private {
@@ -180,6 +182,11 @@ pub unsafe trait EnumSetType: Copy {
     #[doc(hidden)] const ALL_BITS: Self::Repr;
     #[doc(hidden)] fn enum_into_u8(self) -> u8;
     #[doc(hidden)] unsafe fn enum_from_u8(val: u8) -> Self;
+
+    #[cfg(feature = "serde")] #[doc(hidden)]
+    fn serialize<S: serde::Serializer>(set: EnumSet<Self>, ser: S) -> Result<S::Ok, S::Error>;
+    #[cfg(feature = "serde")] #[doc(hidden)]
+    fn deserialize<'de, D: serde::Deserializer<'de>>(de: D) -> Result<EnumSet<Self>, D::Error>;
 }
 
 /// An efficient set type for enums.
@@ -438,20 +445,16 @@ impl <T : EnumSetType + Debug> Debug for EnumSet<T> {
 }
 
 #[cfg(feature = "serde")]
-impl <T : EnumSetType> serde::Serialize for EnumSet<T>
-    where T::Repr: serde::Serialize
-{
+impl <T : EnumSetType> serde::Serialize for EnumSet<T> {
     fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
-        self.__enumset_underlying.serialize(serializer)
+        T::serialize(*self, serializer)
     }
 }
 
 #[cfg(feature = "serde")]
-impl <'de, T : EnumSetType> serde::Deserialize<'de> for EnumSet<T>
-    where T::Repr: serde::Deserialize<'de>
-{
+impl <'de, T : EnumSetType> serde::Deserialize<'de> for EnumSet<T> {
     fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
-        T::Repr::deserialize(deserializer).map(|x| EnumSet { __enumset_underlying: x })
+        T::deserialize(deserializer)
     }
 }
 
index 8023fed0dc6d8eb363d5e56483d6af8d07259b0d..d511eb37e9495d3b669e7e4460c0a2cd1604c30c 100644 (file)
@@ -15,6 +15,7 @@ proc-macro = true
 
 [features]
 nightly = ["proc-macro2/nightly"]
+serde = []
 
 [dependencies]
 syn = "0.15.18"
index db3434a0707c6a3318fb024ee5befb29f2d9c7fc..be02a6998331e79e14d3d5aefe8b43fff8d13136 100644 (file)
@@ -1,4 +1,4 @@
-#![recursion_limit="128"]
+#![recursion_limit="256"]
 #![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic))]
 
 extern crate syn;
@@ -25,10 +25,13 @@ fn error(_: Span, data: &str) -> TokenStream {
 }
 
 fn enum_set_type_impl(
-    name: &Ident, all_variants: u128, repr: Ident, no_ops: bool, no_derives: bool,
+    name: &Ident, all_variants: u128, repr: Ident,
+    no_ops: bool, no_derives: bool, _serialize_as_list: bool,
 ) -> SynTokenStream {
     let typed_enumset = quote!(::enumset::EnumSet<#name>);
     let core = quote!(::enumset::internal::core);
+    #[cfg(feature = "serde")]
+    let serde = quote!(::enumset::internal::serde);
 
     // proc_macro2 does not support creating u128 literals.
     let all_variants = Literal::u128_unsuffixed(all_variants);
@@ -109,6 +112,68 @@ fn enum_set_type_impl(
         }
     };
 
+    #[cfg(feature = "serde")]
+    let expecting_str = format!("a list of {}", name);
+
+    #[cfg(feature = "serde")]
+    let serde_ops = if _serialize_as_list {
+        quote! {
+            fn serialize<S: #serde::Serializer>(
+                set: ::enumset::EnumSet<#name>, ser: S,
+            ) -> #core::result::Result<S::Ok, S::Error> {
+                use #serde::ser::SerializeSeq;
+                let mut seq = ser.serialize_seq(#core::prelude::v1::Some(set.len()))?;
+                for bit in set {
+                    seq.serialize_element(&bit)?;
+                }
+                seq.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_seq<A>(
+                        mut self, mut seq: A,
+                    ) -> Result<Self::Value, A::Error> where A: #serde::de::SeqAccess<'de> {
+                        let mut accum = ::enumset::EnumSet::<#name>::new();
+                        while let #core::prelude::v1::Some(val) = seq.next_element::<#name>()? {
+                            accum |= val;
+                        }
+                        #core::prelude::v1::Ok(accum)
+                    }
+                }
+                de.deserialize_seq(Visitor)
+            }
+        }
+    } else {
+        quote! {
+            fn serialize<S: #serde::Serializer>(
+                set: ::enumset::EnumSet<#name>, ser: S,
+            ) -> #core::result::Result<S::Ok, S::Error> {
+                use #serde::Serialize;
+                <#name as ::enumset::EnumSetType>::Repr::serialize(&set.__enumset_underlying, ser)
+            }
+            fn deserialize<'de, D: #serde::Deserializer<'de>>(
+                de: D,
+            ) -> #core::result::Result<::enumset::EnumSet<#name>, D::Error> {
+                use #serde::Deserialize;
+                #core::prelude::v1::Ok(::enumset::EnumSet {
+                    __enumset_underlying: <#name as ::enumset::EnumSetType>::Repr::deserialize(de)?,
+                })
+            }
+        }
+    };
+
+    #[cfg(not(feature = "serde"))]
+    let serde_ops = quote! { };
+
     quote! {
         unsafe impl ::enumset::EnumSetType for #name {
             type Repr = #repr;
@@ -120,6 +185,8 @@ fn enum_set_type_impl(
             unsafe fn enum_from_u8(val: u8) -> Self {
                 #core::mem::transmute(val)
             }
+
+            #serde_ops
         }
 
         #ops
@@ -127,7 +194,8 @@ fn enum_set_type_impl(
     }
 }
 
-#[proc_macro_derive(EnumSetType, attributes(enumset_no_ops, enumset_no_derives))]
+#[proc_macro_derive(EnumSetType,
+                    attributes(enumset_no_ops, enumset_no_derives, enumset_serialize_as_list))]
 pub fn derive_enum_set_type(input: TokenStream) -> TokenStream {
     let input: DeriveInput = parse_macro_input!(input);
     if let Data::Enum(data) = input.data {
@@ -192,27 +260,38 @@ pub fn derive_enum_set_type(input: TokenStream) -> TokenStream {
 
             let mut no_ops = false;
             let mut no_derives = false;
+            let mut serialize_as_list = false;
 
             for attr in &input.attrs {
                 let span = attr.span();
                 let Attribute { tts, path: Path { segments, ..}, .. } = attr;
 
-                if segments.len() == 1 && segments[0].ident.to_string() == "enumset_no_ops" {
-                    no_ops = true;
-                    if !tts.is_empty() {
-                        return error(span, "`#[enumset_no_ops]` takes no arguments.")
+                if segments.len() == 1 {
+                    let name = segments[0].ident.to_string();
+                    let mut is_attr = false;
+                    match name.as_str() {
+                        "enumset_no_ops" => {
+                            no_ops = true;
+                            is_attr = true;
+                        }
+                        "enumset_no_derives" => {
+                            no_derives = true;
+                            is_attr = true;
+                        }
+                        "enumset_serialize_as_list" => {
+                            serialize_as_list = true;
+                            is_attr = true;
+                        }
+                        _ => { }
                     }
-                }
-                if segments.len() == 1 && segments[0].ident.to_string() == "enumset_no_derives" {
-                    no_derives = true;
-                    if !tts.is_empty() {
-                        return error(span, "`#[enumset_no_derives]` takes no arguments.")
+                    if is_attr && !tts.is_empty() {
+                        return error(span, &format!("`#[{}]` takes no arguments.", name))
                     }
                 }
             }
 
             enum_set_type_impl(
-                &input.ident, all_variants, repr, no_ops, no_derives,
+                &input.ident, all_variants, repr, no_ops, no_derives, serialize_as_list,
             ).into()
         }
     } else {