From 9e7a3437d962d9a8b4e3ae7de7288eceba56f1e2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 6 Nov 2017 23:32:26 -0800 Subject: [PATCH] Allow internally tagged newtype variant containing unit struct --- serde/src/private/de.rs | 32 +++++++++++++++++++++-- serde/src/private/ser.rs | 6 ++--- test_suite/tests/test_macros.rs | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index 3d8a2ec3..d9382bc6 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -1114,10 +1114,38 @@ mod content { ) } + fn deserialize_unit_struct( + self, + _name: &'static str, + visitor: V + ) -> Result + where + V: Visitor<'de>, + { + match self.content { + // As a special case, allow deserializing untagged newtype + // variant containing unit struct. + // + // #[derive(Deserialize)] + // struct Info; + // + // #[derive(Deserialize)] + // #[serde(tag = "topic")] + // enum Message { + // Info(Info), + // } + // + // We want {"topic":"Info"} to deserialize even though + // ordinarily unit structs do not deserialize from empty map. + Content::Map(ref v) if v.is_empty() => visitor.visit_unit(), + _ => self.deserialize_any(visitor), + } + } + forward_to_deserialize_any! { bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes - byte_buf unit unit_struct seq tuple tuple_struct map struct - identifier ignored_any + byte_buf unit seq tuple tuple_struct map struct identifier + ignored_any } } diff --git a/serde/src/private/ser.rs b/serde/src/private/ser.rs index 51db594d..3f60d7cd 100644 --- a/serde/src/private/ser.rs +++ b/serde/src/private/ser.rs @@ -60,7 +60,6 @@ enum Unsupported { ByteArray, Optional, Unit, - UnitStruct, Sequence, Tuple, TupleStruct, @@ -79,7 +78,6 @@ impl Display for Unsupported { Unsupported::ByteArray => formatter.write_str("a byte array"), Unsupported::Optional => formatter.write_str("an optional"), Unsupported::Unit => formatter.write_str("unit"), - Unsupported::UnitStruct => formatter.write_str("a unit struct"), Unsupported::Sequence => formatter.write_str("a sequence"), Unsupported::Tuple => formatter.write_str("a tuple"), Unsupported::TupleStruct => formatter.write_str("a tuple struct"), @@ -199,7 +197,9 @@ where } fn serialize_unit_struct(self, _: &'static str) -> Result { - Err(self.bad_type(Unsupported::UnitStruct)) + let mut map = try!(self.delegate.serialize_map(Some(1))); + try!(map.serialize_entry(self.tag, self.variant_name)); + map.end() } fn serialize_unit_variant( diff --git a/test_suite/tests/test_macros.rs b/test_suite/tests/test_macros.rs index 0ea2578a..19c68382 100644 --- a/test_suite/tests/test_macros.rs +++ b/test_suite/tests/test_macros.rs @@ -1267,3 +1267,48 @@ fn test_rename_all() { ] ); } + +#[test] +fn test_untagged_newtype_variant_containing_unit_struct_not_map() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Unit; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(untagged)] + enum Message { + Unit(Unit), + Map(BTreeMap), + } + + assert_tokens( + &Message::Map(BTreeMap::new()), + &[ + Token::Map { len: Some(0) }, + Token::MapEnd, + ], + ); +} + +#[test] +fn test_internally_tagged_newtype_variant_containing_unit_struct() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Info; + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(tag = "topic")] + enum Message { + Info(Info), + } + + assert_tokens( + &Message::Info(Info), + &[ + Token::Map { len: Some(1) }, + + Token::Str("topic"), + Token::Str("Info"), + + Token::MapEnd, + ], + ); +}