diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 449a2490..53160416 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -255,77 +255,123 @@ fn serialize_variant( let variant_ident = variant.ident.clone(); let variant_name = variant.attrs.name().serialize_name(); + let skipped_err = quote! { + Err(_serde::ser::Error::invalid_value("The enum variant was skipped for serialization")) + }; - match variant.style { - Style::Unit => { - quote! { - #type_ident::#variant_ident => - _serde::ser::Serializer::serialize_unit_variant( - _serializer, - #type_name, - #variant_index, - #variant_name, - ), - } - }, - Style::Newtype => { - let block = serialize_newtype_variant( - type_name, - variant_index, - variant_name, - ty, - generics, - &variant.fields[0], - ); + if variant.attrs.skip_serializing() { + match variant.style { + Style::Unit => { + quote! { + #type_ident::#variant_ident => #skipped_err, + } + }, + Style::Newtype => { + quote! { + #type_ident::#variant_ident(ref __simple_value) => #skipped_err, + } + }, + Style::Tuple => { + let field_names: Vec = (0 .. variant.fields.len()) + .map(|i| { + let id = aster::id(format!("__field{}", i)); + quote!(ref #id) + }) + .collect(); - quote! { - #type_ident::#variant_ident(ref __simple_value) => #block, + let pat = quote!(#type_ident::#variant_ident(#(#field_names),*)); + + quote! { + #pat => #skipped_err, + } } - }, - Style::Tuple => { - let field_names: Vec = (0 .. variant.fields.len()) - .map(|i| { - let id = aster::id(format!("__field{}", i)); + Style::Struct => { + let fields = variant.fields.iter().map(|field| { + let id = match field.ident { + Some(ref name) => name.clone(), + None => panic!("struct variant has unnamed fields"), + }; quote!(ref #id) - }) - .collect(); + }); + let pat = quote!(#type_ident::#variant_ident { #(#fields),* }); - let pat = quote!(#type_ident::#variant_ident(#(#field_names),*)); - - let block = serialize_tuple_variant( - type_name, - variant_index, - variant_name, - generics, - ty, - &variant.fields, - ); - - quote! { - #pat => { #block } + quote! { + #pat => #skipped_err, + } } } - Style::Struct => { - let fields = variant.fields.iter().map(|field| { - let id = match field.ident { - Some(ref name) => name.clone(), - None => panic!("struct variant has unnamed fields"), - }; - quote!(ref #id) - }); - let pat = quote!(#type_ident::#variant_ident { #(#fields),* }); + } else { // variant wasn't skipped + match variant.style { + Style::Unit => { + quote! { + #type_ident::#variant_ident => + _serde::ser::Serializer::serialize_unit_variant( + _serializer, + #type_name, + #variant_index, + #variant_name, + ), + } + }, + Style::Newtype => { + let block = serialize_newtype_variant( + type_name, + variant_index, + variant_name, + ty, + generics, + &variant.fields[0], + ); - let block = serialize_struct_variant( - variant_index, - variant_name, - generics, - ty, - &variant.fields, - item_attrs, - ); + quote! { + #type_ident::#variant_ident(ref __simple_value) => #block, + } + }, + Style::Tuple => { + let field_names: Vec = (0 .. variant.fields.len()) + .map(|i| { + let id = aster::id(format!("__field{}", i)); + quote!(ref #id) + }) + .collect(); - quote! { - #pat => { #block } + let pat = quote!(#type_ident::#variant_ident(#(#field_names),*)); + + let block = serialize_tuple_variant( + type_name, + variant_index, + variant_name, + generics, + ty, + &variant.fields, + ); + + quote! { + #pat => { #block } + } + } + Style::Struct => { + let fields = variant.fields.iter().map(|field| { + let id = match field.ident { + Some(ref name) => name.clone(), + None => panic!("struct variant has unnamed fields"), + }; + quote!(ref #id) + }); + let pat = quote!(#type_ident::#variant_ident { #(#fields),* }); + + let block = serialize_struct_variant( + variant_index, + variant_name, + generics, + ty, + &variant.fields, + item_attrs, + ); + + quote! { + #pat => { #block } + } } } } diff --git a/serde_codegen_internals/src/attr.rs b/serde_codegen_internals/src/attr.rs index a9a69169..632dd508 100644 --- a/serde_codegen_internals/src/attr.rs +++ b/serde_codegen_internals/src/attr.rs @@ -188,6 +188,7 @@ impl Item { pub struct Variant { name: Name, skip_deserializing: bool, + skip_serializing: bool, } impl Variant { @@ -195,6 +196,7 @@ impl Variant { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing"); + let mut skip_serializing = BoolAttr::none(cx, "skip_serializing"); for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) { for meta_item in meta_items { @@ -218,6 +220,10 @@ impl Variant { MetaItem(Word(ref name)) if name == "skip_deserializing" => { skip_deserializing.set_true(); } + // Parse `#[serde(skip_serializing)]` + MetaItem(Word(ref name)) if name == "skip_serializing" => { + skip_serializing.set_true(); + } MetaItem(ref meta_item) => { cx.error(format!("unknown serde variant attribute `{}`", @@ -237,6 +243,7 @@ impl Variant { deserialize: de_name.get().unwrap_or_else(|| variant.ident.to_string()), }, skip_deserializing: skip_deserializing.get(), + skip_serializing: skip_serializing.get(), } } @@ -247,6 +254,10 @@ impl Variant { pub fn skip_deserializing(&self) -> bool { self.skip_deserializing } + + pub fn skip_serializing(&self) -> bool { + self.skip_serializing + } } /// Represents field attribute information diff --git a/testing/tests/test_ser.rs b/testing/tests/test_ser.rs index 52573533..afc85070 100644 --- a/testing/tests/test_ser.rs +++ b/testing/tests/test_ser.rs @@ -30,12 +30,20 @@ struct Struct { c: i32, } -#[derive(Serialize)] +#[derive(Serialize, PartialEq, Debug)] enum Enum { Unit, One(i32), Seq(i32, i32), Map { a: i32, b: i32 }, + #[serde(skip_serializing)] + SkippedUnit, + #[serde(skip_serializing)] + SkippedOne(i32), + #[serde(skip_serializing)] + SkippedSeq(i32, i32), + #[serde(skip_serializing)] + SkippedMap { _a: i32, _b: i32 }, } ////////////////////////////////////////////////////////////////////////// @@ -388,3 +396,23 @@ fn test_cannot_serialize_paths() { &[], Error::InvalidValue("Path contains invalid UTF-8 characters".to_owned())); } + +#[test] +fn test_enum_skipped() { + assert_ser_tokens_error( + &Enum::SkippedUnit, + &[], + Error::InvalidValue("The enum variant was skipped for serialization".to_owned())); + assert_ser_tokens_error( + &Enum::SkippedOne(42), + &[], + Error::InvalidValue("The enum variant was skipped for serialization".to_owned())); + assert_ser_tokens_error( + &Enum::SkippedSeq(1, 2), + &[], + Error::InvalidValue("The enum variant was skipped for serialization".to_owned())); + assert_ser_tokens_error( + &Enum::SkippedMap { _a: 1, _b: 2 }, + &[], + Error::InvalidValue("The enum variant was skipped for serialization".to_owned())); +}