diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index 63cb3dd1..6864bf96 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -1607,426 +1607,6 @@ fn test_collect_other() { ); } -#[test] -fn test_unknown_field_in_flatten() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - #[serde(deny_unknown_fields)] - struct Outer { - dummy: String, - #[serde(flatten)] - inner: Inner, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Inner { - foo: HashMap, - } - - assert_de_tokens_error::( - &[ - Token::Struct { - name: "Outer", - len: 1, - }, - Token::Str("dummy"), - Token::Str("23"), - Token::Str("foo"), - Token::Map { len: None }, - Token::Str("a"), - Token::U32(1), - Token::Str("b"), - Token::U32(2), - Token::MapEnd, - Token::Str("bar"), - Token::U32(23), - Token::StructEnd, - ], - "unknown field `bar`", - ); -} - -#[test] -fn test_complex_flatten() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Outer { - y: u32, - #[serde(flatten)] - first: First, - #[serde(flatten)] - second: Second, - z: u32, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct First { - a: u32, - b: bool, - c: Vec, - d: String, - e: Option, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Second { - f: u32, - } - - assert_de_tokens( - &Outer { - y: 0, - first: First { - a: 1, - b: true, - c: vec!["a".into(), "b".into()], - d: "c".into(), - e: Some(2), - }, - second: Second { f: 3 }, - z: 4, - }, - &[ - Token::Map { len: None }, - Token::Str("y"), - Token::U32(0), - Token::Str("a"), - Token::U32(1), - Token::Str("b"), - Token::Bool(true), - Token::Str("c"), - Token::Seq { len: Some(2) }, - Token::Str("a"), - Token::Str("b"), - Token::SeqEnd, - Token::Str("d"), - Token::Str("c"), - Token::Str("e"), - Token::U64(2), - Token::Str("f"), - Token::U32(3), - Token::Str("z"), - Token::U32(4), - Token::MapEnd, - ], - ); - - assert_ser_tokens( - &Outer { - y: 0, - first: First { - a: 1, - b: true, - c: vec!["a".into(), "b".into()], - d: "c".into(), - e: Some(2), - }, - second: Second { f: 3 }, - z: 4, - }, - &[ - Token::Map { len: None }, - Token::Str("y"), - Token::U32(0), - Token::Str("a"), - Token::U32(1), - Token::Str("b"), - Token::Bool(true), - Token::Str("c"), - Token::Seq { len: Some(2) }, - Token::Str("a"), - Token::Str("b"), - Token::SeqEnd, - Token::Str("d"), - Token::Str("c"), - Token::Str("e"), - Token::Some, - Token::U64(2), - Token::Str("f"), - Token::U32(3), - Token::Str("z"), - Token::U32(4), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_map_twice() { - #[derive(Debug, PartialEq, Deserialize)] - struct Outer { - #[serde(flatten)] - first: BTreeMap, - #[serde(flatten)] - between: Inner, - #[serde(flatten)] - second: BTreeMap, - } - - #[derive(Debug, PartialEq, Deserialize)] - struct Inner { - y: String, - } - - assert_de_tokens( - &Outer { - first: { - let mut first = BTreeMap::new(); - first.insert("x".to_owned(), "X".to_owned()); - first.insert("y".to_owned(), "Y".to_owned()); - first - }, - between: Inner { y: "Y".to_owned() }, - second: { - let mut second = BTreeMap::new(); - second.insert("x".to_owned(), "X".to_owned()); - second - }, - }, - &[ - Token::Map { len: None }, - Token::Str("x"), - Token::Str("X"), - Token::Str("y"), - Token::Str("Y"), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_unit() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Response { - #[serde(flatten)] - data: T, - status: usize, - } - - assert_tokens( - &Response { - data: (), - status: 0, - }, - &[ - Token::Map { len: None }, - Token::Str("status"), - Token::U64(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_unit_struct() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Response { - #[serde(flatten)] - data: T, - status: usize, - } - - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Unit; - - assert_tokens( - &Response { - data: Unit, - status: 0, - }, - &[ - Token::Map { len: None }, - Token::Str("status"), - Token::U64(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_flatten_unsupported_type() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct Outer { - outer: String, - #[serde(flatten)] - inner: String, - } - - assert_ser_tokens_error( - &Outer { - outer: "foo".into(), - inner: "bar".into(), - }, - &[ - Token::Map { len: None }, - Token::Str("outer"), - Token::Str("foo"), - ], - "can only flatten structs and maps (got a string)", - ); - assert_de_tokens_error::( - &[ - Token::Map { len: None }, - Token::Str("outer"), - Token::Str("foo"), - Token::Str("a"), - Token::Str("b"), - Token::MapEnd, - ], - "can only flatten structs and maps", - ); -} - -#[test] -fn test_non_string_keys() { - #[derive(Debug, PartialEq, Serialize, Deserialize)] - struct TestStruct { - name: String, - age: u32, - #[serde(flatten)] - mapping: HashMap, - } - - let mut mapping = HashMap::new(); - mapping.insert(0, 42); - assert_tokens( - &TestStruct { - name: "peter".into(), - age: 3, - mapping, - }, - &[ - Token::Map { len: None }, - Token::Str("name"), - Token::Str("peter"), - Token::Str("age"), - Token::U32(3), - Token::U32(0), - Token::U32(42), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_lifetime_propagation_for_flatten() { - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct A { - #[serde(flatten)] - t: T, - } - - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct B<'a> { - #[serde(flatten, borrow)] - t: HashMap<&'a str, u32>, - } - - #[derive(Deserialize, Serialize, Debug, PartialEq)] - struct C<'a> { - #[serde(flatten, borrow)] - t: HashMap<&'a [u8], u32>, - } - - let mut owned_map = HashMap::new(); - owned_map.insert("x".to_string(), 42u32); - assert_tokens( - &A { t: owned_map }, - &[ - Token::Map { len: None }, - Token::Str("x"), - Token::U32(42), - Token::MapEnd, - ], - ); - - let mut borrowed_map = HashMap::new(); - borrowed_map.insert("x", 42u32); - assert_ser_tokens( - &B { - t: borrowed_map.clone(), - }, - &[ - Token::Map { len: None }, - Token::BorrowedStr("x"), - Token::U32(42), - Token::MapEnd, - ], - ); - - assert_de_tokens( - &B { t: borrowed_map }, - &[ - Token::Map { len: None }, - Token::BorrowedStr("x"), - Token::U32(42), - Token::MapEnd, - ], - ); - - let mut borrowed_map = HashMap::new(); - borrowed_map.insert(&b"x"[..], 42u32); - assert_ser_tokens( - &C { - t: borrowed_map.clone(), - }, - &[ - Token::Map { len: None }, - Token::Seq { len: Some(1) }, - Token::U8(120), - Token::SeqEnd, - Token::U32(42), - Token::MapEnd, - ], - ); - - assert_de_tokens( - &C { t: borrowed_map }, - &[ - Token::Map { len: None }, - Token::BorrowedBytes(b"x"), - Token::U32(42), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_externally_tagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::NewtypeVariant { - name: "Data", - variant: "A", - }, - Token::Map { len: None }, - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - #[test] fn test_internally_tagged_enum_with_skipped_conflict() { #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -2060,43 +1640,6 @@ fn test_internally_tagged_enum_with_skipped_conflict() { ); } -#[test] -fn test_internally_tagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(tag = "t")] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::Map { len: None }, - Token::Str("t"), - Token::Str("A"), - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - #[test] fn test_internally_tagged_enum_new_type_with_unit() { #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -2169,87 +1712,6 @@ fn test_adjacently_tagged_enum_bytes() { ); } -#[test] -fn test_adjacently_tagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(tag = "t", content = "c")] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::Struct { - name: "Data", - len: 2, - }, - Token::Str("t"), - Token::UnitVariant { - name: "Data", - variant: "A", - }, - Token::Str("c"), - Token::Map { len: None }, - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - Token::StructEnd, - ], - ); -} - -#[test] -fn test_untagged_enum_containing_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - #[serde(untagged)] - enum Data { - A { - a: i32, - #[serde(flatten)] - flat: Flat, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Flat { - b: i32, - } - - let data = Data::A { - a: 0, - flat: Flat { b: 0 }, - }; - - assert_tokens( - &data, - &[ - Token::Map { len: None }, - Token::Str("a"), - Token::I32(0), - Token::Str("b"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - #[test] fn test_partially_untagged_enum() { #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -2406,56 +1868,6 @@ fn test_partially_untagged_enum_desugared() { ); } -// Regression test for https://github.com/serde-rs/serde/issues/1904 -#[test] -fn test_enum_tuple_and_struct_with_flatten() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - enum Outer { - Tuple(f64, i32), - Flatten { - #[serde(flatten)] - nested: Nested, - }, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Nested { - a: i32, - b: i32, - } - - assert_tokens( - &Outer::Tuple(1.2, 3), - &[ - Token::TupleVariant { - name: "Outer", - variant: "Tuple", - len: 2, - }, - Token::F64(1.2), - Token::I32(3), - Token::TupleVariantEnd, - ], - ); - assert_tokens( - &Outer::Flatten { - nested: Nested { a: 1, b: 2 }, - }, - &[ - Token::NewtypeVariant { - name: "Outer", - variant: "Flatten", - }, - Token::Map { len: None }, - Token::Str("a"), - Token::I32(1), - Token::Str("b"), - Token::I32(2), - Token::MapEnd, - ], - ); -} - #[test] fn test_partially_untagged_internally_tagged_enum() { #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -2518,101 +1930,6 @@ fn test_partially_untagged_adjacently_tagged_enum() { // TODO test error output } -#[test] -fn test_flatten_option() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Outer { - #[serde(flatten)] - inner1: Option, - #[serde(flatten)] - inner2: Option, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Inner1 { - inner1: i32, - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct Inner2 { - inner2: i32, - } - - assert_tokens( - &Outer { - inner1: Some(Inner1 { inner1: 1 }), - inner2: Some(Inner2 { inner2: 2 }), - }, - &[ - Token::Map { len: None }, - Token::Str("inner1"), - Token::I32(1), - Token::Str("inner2"), - Token::I32(2), - Token::MapEnd, - ], - ); - - assert_tokens( - &Outer { - inner1: Some(Inner1 { inner1: 1 }), - inner2: None, - }, - &[ - Token::Map { len: None }, - Token::Str("inner1"), - Token::I32(1), - Token::MapEnd, - ], - ); - - assert_tokens( - &Outer { - inner1: None, - inner2: Some(Inner2 { inner2: 2 }), - }, - &[ - Token::Map { len: None }, - Token::Str("inner2"), - Token::I32(2), - Token::MapEnd, - ], - ); - - assert_tokens( - &Outer { - inner1: None, - inner2: None, - }, - &[Token::Map { len: None }, Token::MapEnd], - ); -} - -#[test] -fn test_flatten_ignored_any() { - #[derive(Deserialize, PartialEq, Debug)] - struct Outer { - #[serde(flatten)] - inner: IgnoredAny, - } - - assert_de_tokens( - &Outer { inner: IgnoredAny }, - &[Token::Map { len: None }, Token::MapEnd], - ); - - assert_de_tokens( - &Outer { inner: IgnoredAny }, - &[ - Token::Struct { - name: "DoNotMatter", - len: 0, - }, - Token::StructEnd, - ], - ); -} - #[test] fn test_transparent_struct() { #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -2674,129 +1991,6 @@ fn test_internally_tagged_unit_enum_with_unknown_fields() { ); } -#[test] -fn test_flatten_any_after_flatten_struct() { - #[derive(PartialEq, Debug)] - struct Any; - - impl<'de> Deserialize<'de> for Any { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct AnyVisitor; - - impl<'de> Visitor<'de> for AnyVisitor { - type Value = Any; - - fn expecting(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() - } - - fn visit_map(self, mut map: M) -> Result - where - M: MapAccess<'de>, - { - while let Some((Any, Any)) = map.next_entry()? {} - Ok(Any) - } - } - - deserializer.deserialize_any(AnyVisitor) - } - } - - #[derive(Deserialize, PartialEq, Debug)] - struct Outer { - #[serde(flatten)] - inner: Inner, - #[serde(flatten)] - extra: Any, - } - - #[derive(Deserialize, PartialEq, Debug)] - struct Inner { - inner: i32, - } - - let s = Outer { - inner: Inner { inner: 0 }, - extra: Any, - }; - - assert_de_tokens( - &s, - &[ - Token::Map { len: None }, - Token::Str("inner"), - Token::I32(0), - Token::MapEnd, - ], - ); -} - -#[test] -fn test_alias_in_flatten_context() { - #[derive(Debug, PartialEq, Deserialize)] - struct Outer { - #[serde(flatten)] - a: AliasStruct, - b: i32, - } - - assert_de_tokens( - &Outer { - a: AliasStruct { - a1: 1, - a2: 2, - a4: 4, - }, - b: 7, - }, - &[ - Token::Struct { - name: "Outer", - len: 4, - }, - Token::Str("a1"), - Token::I32(1), - Token::Str("a2"), - Token::I32(2), - Token::Str("a5"), - Token::I32(4), - Token::Str("b"), - Token::I32(7), - Token::StructEnd, - ], - ); - - assert_de_tokens( - &Outer { - a: AliasStruct { - a1: 1, - a2: 2, - a4: 4, - }, - b: 7, - }, - &[ - Token::Struct { - name: "Outer", - len: 4, - }, - Token::Str("a1"), - Token::I32(1), - Token::Str("a2"), - Token::I32(2), - Token::Str("a6"), - Token::I32(4), - Token::Str("b"), - Token::I32(7), - Token::StructEnd, - ], - ); -} - #[test] fn test_expecting_message() { #[derive(Deserialize, PartialEq, Debug)] @@ -2970,6 +2164,660 @@ fn test_expecting_message_identifier_enum() { mod flatten { use super::*; + #[test] + fn complex() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Outer { + y: u32, + #[serde(flatten)] + first: First, + #[serde(flatten)] + second: Second, + z: u32, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct First { + a: u32, + b: bool, + c: Vec, + d: String, + e: Option, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Second { + f: u32, + } + + assert_de_tokens( + &Outer { + y: 0, + first: First { + a: 1, + b: true, + c: vec!["a".into(), "b".into()], + d: "c".into(), + e: Some(2), + }, + second: Second { f: 3 }, + z: 4, + }, + &[ + Token::Map { len: None }, + Token::Str("y"), + Token::U32(0), + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::Bool(true), + Token::Str("c"), + Token::Seq { len: Some(2) }, + Token::Str("a"), + Token::Str("b"), + Token::SeqEnd, + Token::Str("d"), + Token::Str("c"), + Token::Str("e"), + Token::U64(2), + Token::Str("f"), + Token::U32(3), + Token::Str("z"), + Token::U32(4), + Token::MapEnd, + ], + ); + + assert_ser_tokens( + &Outer { + y: 0, + first: First { + a: 1, + b: true, + c: vec!["a".into(), "b".into()], + d: "c".into(), + e: Some(2), + }, + second: Second { f: 3 }, + z: 4, + }, + &[ + Token::Map { len: None }, + Token::Str("y"), + Token::U32(0), + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::Bool(true), + Token::Str("c"), + Token::Seq { len: Some(2) }, + Token::Str("a"), + Token::Str("b"), + Token::SeqEnd, + Token::Str("d"), + Token::Str("c"), + Token::Str("e"), + Token::Some, + Token::U64(2), + Token::Str("f"), + Token::U32(3), + Token::Str("z"), + Token::U32(4), + Token::MapEnd, + ], + ); + } + + #[test] + fn map_twice() { + #[derive(Debug, PartialEq, Deserialize)] + struct Outer { + #[serde(flatten)] + first: BTreeMap, + #[serde(flatten)] + between: Inner, + #[serde(flatten)] + second: BTreeMap, + } + + #[derive(Debug, PartialEq, Deserialize)] + struct Inner { + y: String, + } + + assert_de_tokens( + &Outer { + first: { + let mut first = BTreeMap::new(); + first.insert("x".to_owned(), "X".to_owned()); + first.insert("y".to_owned(), "Y".to_owned()); + first + }, + between: Inner { y: "Y".to_owned() }, + second: { + let mut second = BTreeMap::new(); + second.insert("x".to_owned(), "X".to_owned()); + second + }, + }, + &[ + Token::Map { len: None }, + Token::Str("x"), + Token::Str("X"), + Token::Str("y"), + Token::Str("Y"), + Token::MapEnd, + ], + ); + } + + #[test] + fn unsupported_type() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Outer { + outer: String, + #[serde(flatten)] + inner: String, + } + + assert_ser_tokens_error( + &Outer { + outer: "foo".into(), + inner: "bar".into(), + }, + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::Str("foo"), + ], + "can only flatten structs and maps (got a string)", + ); + assert_de_tokens_error::( + &[ + Token::Map { len: None }, + Token::Str("outer"), + Token::Str("foo"), + Token::Str("a"), + Token::Str("b"), + Token::MapEnd, + ], + "can only flatten structs and maps", + ); + } + + #[test] + fn unknown_field() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[serde(deny_unknown_fields)] + struct Outer { + dummy: String, + #[serde(flatten)] + inner: Inner, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Inner { + foo: HashMap, + } + + assert_de_tokens_error::( + &[ + Token::Struct { + name: "Outer", + len: 1, + }, + Token::Str("dummy"), + Token::Str("23"), + Token::Str("foo"), + Token::Map { len: None }, + Token::Str("a"), + Token::U32(1), + Token::Str("b"), + Token::U32(2), + Token::MapEnd, + Token::Str("bar"), + Token::U32(23), + Token::StructEnd, + ], + "unknown field `bar`", + ); + } + + #[test] + fn non_string_keys() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct TestStruct { + name: String, + age: u32, + #[serde(flatten)] + mapping: HashMap, + } + + let mut mapping = HashMap::new(); + mapping.insert(0, 42); + assert_tokens( + &TestStruct { + name: "peter".into(), + age: 3, + mapping, + }, + &[ + Token::Map { len: None }, + Token::Str("name"), + Token::Str("peter"), + Token::Str("age"), + Token::U32(3), + Token::U32(0), + Token::U32(42), + Token::MapEnd, + ], + ); + } + + #[test] + fn lifetime_propagation() { + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct A { + #[serde(flatten)] + t: T, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct B<'a> { + #[serde(flatten, borrow)] + t: HashMap<&'a str, u32>, + } + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct C<'a> { + #[serde(flatten, borrow)] + t: HashMap<&'a [u8], u32>, + } + + let mut owned_map = HashMap::new(); + owned_map.insert("x".to_string(), 42u32); + assert_tokens( + &A { t: owned_map }, + &[ + Token::Map { len: None }, + Token::Str("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + let mut borrowed_map = HashMap::new(); + borrowed_map.insert("x", 42u32); + assert_ser_tokens( + &B { + t: borrowed_map.clone(), + }, + &[ + Token::Map { len: None }, + Token::BorrowedStr("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &B { t: borrowed_map }, + &[ + Token::Map { len: None }, + Token::BorrowedStr("x"), + Token::U32(42), + Token::MapEnd, + ], + ); + + let mut borrowed_map = HashMap::new(); + borrowed_map.insert(&b"x"[..], 42u32); + assert_ser_tokens( + &C { + t: borrowed_map.clone(), + }, + &[ + Token::Map { len: None }, + Token::Seq { len: Some(1) }, + Token::U8(120), + Token::SeqEnd, + Token::U32(42), + Token::MapEnd, + ], + ); + + assert_de_tokens( + &C { t: borrowed_map }, + &[ + Token::Map { len: None }, + Token::BorrowedBytes(b"x"), + Token::U32(42), + Token::MapEnd, + ], + ); + } + + // Regression test for https://github.com/serde-rs/serde/issues/1904 + #[test] + fn enum_tuple_and_struct() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + enum Outer { + Tuple(f64, i32), + Flatten { + #[serde(flatten)] + nested: Nested, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Nested { + a: i32, + b: i32, + } + + assert_tokens( + &Outer::Tuple(1.2, 3), + &[ + Token::TupleVariant { + name: "Outer", + variant: "Tuple", + len: 2, + }, + Token::F64(1.2), + Token::I32(3), + Token::TupleVariantEnd, + ], + ); + assert_tokens( + &Outer::Flatten { + nested: Nested { a: 1, b: 2 }, + }, + &[ + Token::NewtypeVariant { + name: "Outer", + variant: "Flatten", + }, + Token::Map { len: None }, + Token::Str("a"), + Token::I32(1), + Token::Str("b"), + Token::I32(2), + Token::MapEnd, + ], + ); + } + + #[test] + fn option() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Outer { + #[serde(flatten)] + inner1: Option, + #[serde(flatten)] + inner2: Option, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Inner1 { + inner1: i32, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Inner2 { + inner2: i32, + } + + assert_tokens( + &Outer { + inner1: Some(Inner1 { inner1: 1 }), + inner2: Some(Inner2 { inner2: 2 }), + }, + &[ + Token::Map { len: None }, + Token::Str("inner1"), + Token::I32(1), + Token::Str("inner2"), + Token::I32(2), + Token::MapEnd, + ], + ); + + assert_tokens( + &Outer { + inner1: Some(Inner1 { inner1: 1 }), + inner2: None, + }, + &[ + Token::Map { len: None }, + Token::Str("inner1"), + Token::I32(1), + Token::MapEnd, + ], + ); + + assert_tokens( + &Outer { + inner1: None, + inner2: Some(Inner2 { inner2: 2 }), + }, + &[ + Token::Map { len: None }, + Token::Str("inner2"), + Token::I32(2), + Token::MapEnd, + ], + ); + + assert_tokens( + &Outer { + inner1: None, + inner2: None, + }, + &[Token::Map { len: None }, Token::MapEnd], + ); + } + + #[test] + fn ignored_any() { + #[derive(Deserialize, PartialEq, Debug)] + struct Outer { + #[serde(flatten)] + inner: IgnoredAny, + } + + assert_de_tokens( + &Outer { inner: IgnoredAny }, + &[Token::Map { len: None }, Token::MapEnd], + ); + + assert_de_tokens( + &Outer { inner: IgnoredAny }, + &[ + Token::Struct { + name: "DoNotMatter", + len: 0, + }, + Token::StructEnd, + ], + ); + } + + #[test] + fn flatten_any_after_flatten_struct() { + #[derive(PartialEq, Debug)] + struct Any; + + impl<'de> Deserialize<'de> for Any { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct AnyVisitor; + + impl<'de> Visitor<'de> for AnyVisitor { + type Value = Any; + + fn expecting(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + while let Some((Any, Any)) = map.next_entry()? {} + Ok(Any) + } + } + + deserializer.deserialize_any(AnyVisitor) + } + } + + #[derive(Deserialize, PartialEq, Debug)] + struct Outer { + #[serde(flatten)] + inner: Inner, + #[serde(flatten)] + extra: Any, + } + + #[derive(Deserialize, PartialEq, Debug)] + struct Inner { + inner: i32, + } + + let s = Outer { + inner: Inner { inner: 0 }, + extra: Any, + }; + + assert_de_tokens( + &s, + &[ + Token::Map { len: None }, + Token::Str("inner"), + Token::I32(0), + Token::MapEnd, + ], + ); + } + + #[test] + fn alias() { + #[derive(Debug, PartialEq, Deserialize)] + struct Outer { + #[serde(flatten)] + a: AliasStruct, + b: i32, + } + + assert_de_tokens( + &Outer { + a: AliasStruct { + a1: 1, + a2: 2, + a4: 4, + }, + b: 7, + }, + &[ + Token::Struct { + name: "Outer", + len: 4, + }, + Token::Str("a1"), + Token::I32(1), + Token::Str("a2"), + Token::I32(2), + Token::Str("a5"), + Token::I32(4), + Token::Str("b"), + Token::I32(7), + Token::StructEnd, + ], + ); + + assert_de_tokens( + &Outer { + a: AliasStruct { + a1: 1, + a2: 2, + a4: 4, + }, + b: 7, + }, + &[ + Token::Struct { + name: "Outer", + len: 4, + }, + Token::Str("a1"), + Token::I32(1), + Token::Str("a2"), + Token::I32(2), + Token::Str("a6"), + Token::I32(4), + Token::Str("b"), + Token::I32(7), + Token::StructEnd, + ], + ); + } + + mod unit { + use super::*; + + #[test] + fn unit() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Response { + #[serde(flatten)] + data: T, + status: usize, + } + + assert_tokens( + &Response { + data: (), + status: 0, + }, + &[ + Token::Map { len: None }, + Token::Str("status"), + Token::U64(0), + Token::MapEnd, + ], + ); + } + + #[test] + fn unit_struct() { + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Response { + #[serde(flatten)] + data: T, + status: usize, + } + + #[derive(Debug, PartialEq, Serialize, Deserialize)] + struct Unit; + + assert_tokens( + &Response { + data: Unit, + status: 0, + }, + &[ + Token::Map { len: None }, + Token::Str("status"), + Token::U64(0), + Token::MapEnd, + ], + ); + } + } + mod enum_ { use super::*; @@ -2977,6 +2825,44 @@ mod flatten { use super::*; use std::iter::FromIterator; + #[test] + fn straightforward() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::NewtypeVariant { + name: "Data", + variant: "A", + }, + Token::Map { len: None }, + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + ], + ); + } + #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Flatten { #[serde(flatten)] @@ -3101,6 +2987,52 @@ mod flatten { mod adjacently_tagged { use super::*; + #[test] + fn straightforward() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(tag = "t", content = "c")] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::Struct { + name: "Data", + len: 2, + }, + Token::Str("t"), + Token::UnitVariant { + name: "Data", + variant: "A", + }, + Token::Str("c"), + Token::Map { len: None }, + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + Token::StructEnd, + ], + ); + } + #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Flatten { outer: u32, @@ -3191,6 +3123,43 @@ mod flatten { mod internally_tagged { use super::*; + #[test] + fn straightforward() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(tag = "t")] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::Map { len: None }, + Token::Str("t"), + Token::Str("A"), + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + ], + ); + } + #[test] fn structs() { #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -3279,6 +3248,41 @@ mod flatten { mod untagged { use super::*; + #[test] + fn straightforward() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(untagged)] + enum Data { + A { + a: i32, + #[serde(flatten)] + flat: Flat, + }, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Flat { + b: i32, + } + + let data = Data::A { + a: 0, + flat: Flat { b: 0 }, + }; + + assert_tokens( + &data, + &[ + Token::Map { len: None }, + Token::Str("a"), + Token::I32(0), + Token::Str("b"), + Token::I32(0), + Token::MapEnd, + ], + ); + } + #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Flatten { #[serde(flatten)]