use std::collections::{BTreeMap, HashMap}; use ron::{error::Position, error::SpannedError, extensions::Extensions, ser::PrettyConfig, Error}; use serde::{Deserialize, Serialize}; #[test] fn struct_names_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::MissingStructField { field: "hi", outer: None }, position: Position { line: 7, col: 2 } })), ); } #[test] fn struct_names_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default().struct_names(true) ), Ok(()), ); assert_eq!( ron::from_str::( "AdjacentlyTagged(tag: B, content: B(ho: 24, a: A(hi: 42)))" ), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: 42 } }), ); assert_eq!( ron::from_str::( "AdjacentlyTagged(content: B(ho: 24, a: A(hi: 42)), tag: B)" ), Err(SpannedError { code: Error::MissingStructField { field: "ho", outer: Some(String::from("AdjacentlyTagged")) }, position: Position { line: 1, col: 58 } }), ); } #[test] fn struct_names_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: 42 } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 6, col: 2 } })), ); } #[test] fn struct_names_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::MissingStructField { field: "hi", outer: None }, position: Position { line: 6, col: 1 } })), ); } #[test] fn struct_names_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: 42 } } }, PrettyConfig::default().struct_names(true) ), Err(Err(SpannedError { code: Error::MissingStructField { field: "hi", outer: Some(String::from("C")) }, position: Position { line: 6, col: 1 } })), ); } #[test] fn implicit_some_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: B { ho: 24, a: A { hi: Some(Some(())) } } != B { ho: 24, a: A { hi: None } }")))) ); } #[test] fn implicit_some_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Unit; #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: Some(Some(Unit)) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: Some(Some(Unit)) } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Ok(()), ); assert_eq!( ron::from_str::( "#![enable(implicit_some)] (tag: B, content: (ho: 24, a: A(hi: ())))" ), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: Some(Some(Unit)) } }), ); assert_eq!( ron::from_str::( "#![enable(implicit_some)] (content: (ho: 24, a: A(hi: ())), tag: B)" ), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: None } // THIS IS WRONG }), ); } #[test] fn implicit_some_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: Some(Some(())) } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from( "ROUNDTRIP error: B { ho: 24, a: A { hi: Some(Some(())) } } != B { ho: 24, a: A { hi: None } }" )))), ); } #[test] fn implicit_some_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: Some(Some(Untagged::Unit)) } } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: Some(Some(Untagged::Unit)) } } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, a: B { a: A { hi: Some(Some(Unit)) } } } != FlattenedStruct { ho: 24, a: B { a: A { hi: None } } }")))) ); } #[test] fn implicit_some_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Option>, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: Some(Some([])) } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an empty array"), found: String::from("a unit value") }, position: Position { line: 6, col: 1 } })) ); assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: Some(Some([])) } } }, PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: C { ho: 24, a: B { a: A { hi: Some(Some([])) } } } != C { ho: 24, a: B { a: A { hi: None } } }")))) ); } #[test] fn newtype_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } // NOTE: // 1. ron is correctly collected into Content, newtype is a one-seq here // 2. newtype asks ContentDeserializer for newtype // 3. ContentDeserializer forwards any value to visit_newtype_struct // https://github.com/serde-rs/serde/blob/8c4aad3a59515f7b779f764d5e16d6bae297ab7f/serde/src/private/de.rs#L1347-L1359 assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A(42) }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, position: Position { line: 5, col: 2 } })) ); } #[test] fn newtype_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A(42) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (42)))"), Ok(AdjacentlyTagged::B { ho: 24, a: A(42) }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (42)), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, position: Position { line: 1, col: 36 } }) ); } #[test] fn newtype_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip(&Untagged::B { ho: 24, a: A(42) }, PrettyConfig::default()), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 4, col: 2 } })) ); } #[test] fn newtype_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A(42) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, position: Position { line: 4, col: 1 } })) ); } #[test] fn newtype_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A(i32); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A(42) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, position: Position { line: 4, col: 1 } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype((i32,)), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A::Newtype((42,)) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A::Newtype((42,)) }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("a tuple of size 1"), found: String::from("the unsigned integer `42`") }, position: Position { line: 6, col: 2 } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype([i32; 1]), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A::Newtype([42]) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A::Newtype([42]) }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Ok(()) ); assert_eq!( ron::from_str::( "#![enable(unwrap_variant_newtypes)] (tag: B, content: (ho: 24, a: Newtype(42)))" ), Ok(AdjacentlyTagged::B { ho: 24, a: A::Newtype([42]) }), ); assert_eq!( ron::from_str::( "#![enable(unwrap_variant_newtypes)] (content: (ho: 24, a: Newtype(42)), tag: B)" ), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an array of length 1"), found: String::from("the unsigned integer `42`") }, position: Position { line: 1, col: 79 } }) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct OneTuple(i32, #[serde(skip)] ()); #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: Option }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: Some(OneTuple(42, ())) }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: Some(OneTuple(42, ())) }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 5, col: 2 } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype([i32; 1]), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A::Newtype([42]) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A::Newtype([42]) } }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an array of length 1"), found: String::from("the unsigned integer `42`") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn one_tuple_inside_unwrapped_newtype_variant_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum A { Newtype((i32,)), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A::Newtype((42,)) } }, PrettyConfig::default() ), Ok(()), ); assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A::Newtype((42,)) } }, PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("a tuple of size 1"), found: String::from("the unsigned integer `42`") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn one_tuple_variant_inside_internally_tagged() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, position: Position { line: 7, col: 2 } })) ); } #[test] fn one_tuple_variant_inside_adjacently_tagged() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }, PrettyConfig::default() ), Ok(()) ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (hi: OneTuple(42))))"), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (hi: OneTuple(42))), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, position: Position { line: 1, col: 50 } }) ); } #[test] fn one_tuple_variant_inside_untagged() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: OneEnum::OneTuple(42, ()) } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 6, col: 2 } })) ); } #[test] fn one_tuple_variant_inside_flatten_struct() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: OneEnum::OneTuple(42, ()) } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn one_tuple_variant_inside_flatten_struct_variant() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] enum OneEnum { OneTuple(i32, #[serde(skip)] ()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: OneEnum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: OneEnum::OneTuple(42, ()) } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("the unsigned integer `42`") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn raw_value_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, position: Position { line: 7, col: 2 } })) ); } #[test] fn raw_value_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }, PrettyConfig::default() ), // adds an extra space Err(Ok(Error::Message(String::from("ROUNDTRIP error: B { ho: 24, a: A { hi: RawValue(42) } } != B { ho: 24, a: A { hi: RawValue( 42) } }")))) ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (hi:42)))"), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (hi:42)), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, position: Position { line: 1, col: 39 } }) ); } #[test] fn raw_value_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) .unwrap(), } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 6, col: 2 } })) ); } #[test] fn raw_value_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: ron::value::RawValue::from_boxed_ron( String::from("42").into_boxed_str() ) .unwrap(), } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn raw_value_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Box, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: ron::value::RawValue::from_boxed_ron( String::from("42").into_boxed_str() ) .unwrap(), } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any valid RON-value-string"), found: String::from("the unsigned integer `42`") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn unit_like_zero_length_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: [i32; 0], } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: [] } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("an empty array"), found: String::from("a unit value") }, position: Position { line: 7, col: 2 } })) ); } #[test] fn unit_like_zero_length_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct TupleStruct(); #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: TupleStruct, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: TupleStruct() } }, PrettyConfig::default() ), Ok(()), ); assert_eq!( ron::from_str::("(tag: B, content: (ho: 24, a: (hi: ())))"), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: TupleStruct() } }), ); assert_eq!( ron::from_str::("(content: (ho: 24, a: (hi: ())), tag: B)"), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple struct TupleStruct"), found: String::from("a unit value") }, position: Position { line: 1, col: 40 } }) ); } #[test] fn unit_like_zero_length_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Struct {} #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Struct, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: Struct {} } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 6, col: 2 } })) ); } #[test] fn unit_like_zero_length_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Enum { Tuple(), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Enum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: Enum::Tuple() } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("tuple variant"), found: String::from("a unit value") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn unit_like_zero_length_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Enum { Struct {}, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: Enum, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: Enum::Struct {} } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("struct variant"), found: String::from("a unit value") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn i128_inside_internally_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { B { ho: i32, a: A }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: i128::MAX } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("i128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &InternallyTagged::B { ho: 24, a: A { hi: i128::MAX } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as u128", i128::MAX) }, position: Position { line: 5, col: 52 } })) ); } #[test] fn u128_inside_adjacently_tagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: u128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum AdjacentlyTagged { B { ho: i32, a: A }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: u128::MAX } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("u128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &AdjacentlyTagged::B { ho: 24, a: A { hi: u128::MAX } }, PrettyConfig::default() ), Ok(()), ); #[cfg(feature = "integer128")] assert_eq!( ron::from_str::(&format!( "(tag: B, content: (ho: 24, a: (hi: {})))", u128::MAX ),), Ok(AdjacentlyTagged::B { ho: 24, a: A { hi: u128::MAX } }), ); #[cfg(feature = "integer128")] assert_eq!( ron::from_str::(&format!( "(content: (ho: 24, a: (hi: {})), tag: B)", u128::MAX ),), Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as u128", u128::MAX) }, position: Position { line: 1, col: 67 } }), ); } #[test] fn i128_inside_untagged() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { B { ho: i32, a: A }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: i128::MIN } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("i128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &Untagged::B { ho: 24, a: A { hi: i128::MIN } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as i128", i128::MIN) }, position: Position { line: 4, col: 53 } })) ); } #[test] fn u128_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: u128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: B, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: u128::MAX } } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("u128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: B { a: A { hi: u128::MAX } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as u128", u128::MAX) }, position: Position { line: 4, col: 52 } })) ); } #[test] fn i128_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct A { hi: i128, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { a: A, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: B, }, } #[cfg(not(feature = "integer128"))] assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: i128::MIN } } }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("i128 is not supported")))) ); #[cfg(feature = "integer128")] assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: B { a: A { hi: i128::MIN } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("any value"), found: format!("integer `{}` as i128", i128::MIN) }, position: Position { line: 4, col: 53 } })) ); } #[test] fn duplicate_key_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: [(String::from("ho"), 42)].into_iter().collect(), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::DuplicateStructField { field: "ho", outer: None }, position: Position { line: 3, col: 9 } })) ); } #[test] fn duplicate_key_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct C { ho: i32, hi: bool, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct B { #[serde(flatten)] inner: C, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] inner: B, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, inner: B { inner: C { ho: 42, hi: false } } }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::DuplicateStructField { field: "ho", outer: Some(String::from("A")) }, position: Position { line: 3, col: 9 } })) ); } #[test] fn non_string_key_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: [(1, true), (0, false)].into_iter().collect(), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::ExpectedString, position: Position { line: 3, col: 5 } })) ); } #[test] fn non_string_key_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: [('h', 0), ('i', 1)].into_iter().collect(), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::ExpectedString, position: Position { line: 3, col: 5 } })) ); } #[test] fn more_than_one_flatten_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Deep { #[serde(flatten)] other: HashMap, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { #[serde(flatten)] deep: Deep, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] hi: Inner, #[serde(flatten)] other: HashMap, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: HashMap::new(), }, }, other: [(String::from("42"), true)].into_iter().collect(), }, PrettyConfig::default() ), // both maps collect all the unknown keys Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: {} } }, other: {\"42\": true} } != FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: {\"42\": true} } }, other: {\"42\": true} }")))) ); } #[test] fn more_than_one_flatten_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Deep { #[serde(flatten)] other: HashMap, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { #[serde(flatten)] deep: Deep, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] hi: Inner, #[serde(flatten)] other: HashMap, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, hi: Inner { deep: Deep { other: [(String::from("24"), false)].into_iter().collect(), }, }, other: HashMap::new(), }, PrettyConfig::default() ), // both maps collect all the unknown keys Err(Ok(Error::Message(String::from("ROUNDTRIP error: A { ho: 24, hi: Inner { deep: Deep { other: {\"24\": false} } }, other: {} } != A { ho: 24, hi: Inner { deep: Deep { other: {\"24\": false} } }, other: {\"24\": false} }")))) ); } #[test] fn flatten_struct_beside_map_inside_flatten_struct() { // The non-flattened struct must contain some flattened fields #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { hi: i32, #[serde(flatten)] flat: (), } // The non-flattened struct must be behind a level of flatten #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { flat: Flattened, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] inner: Inner, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), inner: Inner { flat: Flattened { hi: 42, flat: () }, }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a map") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn flatten_struct_beside_map_inside_flatten_struct_variant() { // The non-flattened struct must contain some flattened fields #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { hi: i32, #[serde(flatten)] flat: (), } // The non-flattened struct must be behind a level of flatten #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Inner { flat: Flattened, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] inner: Inner, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), inner: Inner { flat: Flattened { hi: 42, flat: () }, }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a map") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn untagged_flatten_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Flattened { Struct { hi: i32, #[serde(flatten)] flat: (), }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42, flat: () }, }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, other: {}, flat: Struct { hi: 42, flat: () } } != FlattenedStruct { ho: 24, other: {\"hi\": 42}, flat: Struct { hi: 42, flat: () } }")))) ); } #[test] fn untagged_flatten_struct_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Flattened { Struct { hi: i32, #[serde(flatten)] flat: (), }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42, flat: () }, }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: A { ho: 24, other: {}, flat: Struct { hi: 42, flat: () } } != A { ho: 24, other: {\"hi\": 42}, flat: Struct { hi: 42, flat: () } }")))) ); } #[test] fn externally_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Flattened { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a unit value") }, position: Position { line: 4, col: 1 } })) ); } #[test] fn externally_tagged_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Flattened { Struct { hi: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a map") }, position: Position { line: 6, col: 1 } })) ); } #[test] fn externally_tagged_tuple_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Flattened { Tuple(i32, bool), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Tuple(42, true), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("a sequence") }, position: Position { line: 7, col: 1 } })) ); } #[test] fn internally_tagged_unit_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum Flattened { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Unit, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Unit\"") }, position: Position { line: 4, col: 1 } })) ); } #[test] fn internally_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum Flattened { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Newtype\"") }, position: Position { line: 4, col: 1 } })) ); } #[test] fn internally_tagged_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum Flattened { Struct { hi: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Struct\"") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn adjacently_tagged_unit_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Unit, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Unit\"") }, position: Position { line: 4, col: 1 } })) ); } #[test] fn adjacently_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Newtype\"") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn adjacently_tagged_struct_variant_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Struct { hi: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened::Struct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Struct\"") }, position: Position { line: 7, col: 1 } })) ); } #[test] fn adjacently_tagged_tuple_variant_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag", content = "content")] enum Flattened { Tuple(i32, bool), } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened::Tuple(42, true), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Tuple\"") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn tagged_struct_beside_map_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] struct Flattened { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, other: HashMap::new(), flat: Flattened { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Flattened\"") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn tagged_struct_beside_map_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] struct Flattened { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { A { ho: i32, #[serde(flatten)] other: HashMap, #[serde(flatten)] flat: Flattened, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::A { ho: 24, other: HashMap::new(), flat: Flattened { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("i32"), found: String::from("the string \"Flattened\"") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn zero_length_untagged_tuple_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { A(), } assert_eq!( check_roundtrip(&Untagged::A(), PrettyConfig::default()), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 1, col: 3 } })) ); } #[test] fn zero_length_untagged_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { A {}, } assert_eq!( check_roundtrip(&Untagged::A {}, PrettyConfig::default()), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 1, col: 3 } })) ); } #[test] fn unwrapped_one_element_untagged_tuple_variant() { // A tuple variant with just one element that is not a newtype variant #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { OneTuple(i32, #[serde(skip)] ()), } assert_eq!( check_roundtrip(&Untagged::OneTuple(42, ()), PrettyConfig::default()), Ok(()) ); assert_eq!( check_roundtrip( &Untagged::OneTuple(42, ()), PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) ), Ok(()) ); } #[test] fn unit_inside_untagged_newtype_variant_inside_internally_tagged_newtype_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Newtype(())), PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 3, col: 2 } })) ); } #[test] fn unit_inside_untagged_newtype_variant_inside_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: Untagged, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: Untagged::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 3, col: 1 } })) ); } #[test] fn unit_struct_inside_untagged_newtype_variant_inside_internally_tagged_newtype_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Unit; #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Newtype(Unit), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Newtype(Unit)), PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 3, col: 2 } })) ); } #[test] fn untagged_unit_variant_inside_internally_tagged_newtype_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] enum FlattenedStructVariant { C { ho: i32, #[serde(flatten)] a: Untagged, }, } assert_eq!( check_roundtrip( &FlattenedStructVariant::C { ho: 24, a: Untagged::Unit, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 3, col: 1 } })) ); } #[test] fn untagged_unit_variant_inside_flatten_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum Enum { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, Newtype(Enum), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Newtype(Enum::Unit)), PrettyConfig::default() ), Ok(()) ); assert_eq!( check_roundtrip( &InternallyTagged::Newtype(Untagged::Unit), PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 3, col: 2 } })) ); } #[test] fn unit_inside_internally_tagged_newtype_variant_inside_multi_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct AnotherFlattenedStruct { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: AnotherFlattenedStruct, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: InternallyTagged::Newtype(()), b: AnotherFlattenedStruct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("unit"), found: String::from("a map") }, position: Position { line: 5, col: 1 } })) ); } #[test] fn untagged_unit_variant_inside_internally_tagged_newtype_variant_inside_multi_flatten_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Unit, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct AnotherFlattenedStruct { hi: i32, } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(Untagged), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct FlattenedStruct { ho: i32, #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: AnotherFlattenedStruct, } assert_eq!( check_roundtrip( &FlattenedStruct { ho: 24, a: InternallyTagged::Newtype(Untagged::Unit), b: AnotherFlattenedStruct { hi: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::Message(String::from( "data did not match any variant of untagged enum Untagged" )), position: Position { line: 5, col: 1 } })) ); } #[test] fn flattened_externally_tagged_newtype_variant_beside_flattened_intenally_tagged_enum() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum ExternallyTagged { Newtype(()), Other(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(ExternallyTagged), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: ExternallyTagged, } assert_eq!( check_roundtrip( &Flattened { a: InternallyTagged::Newtype(ExternallyTagged::Other(())), b: ExternallyTagged::Newtype(()), }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("map with a single key"), found: String::from("a map"), }, position: Position { line: 5, col: 1 } })) ); } #[test] fn flattened_externally_tagged_struct_variant_beside_flattened_intenally_tagged_enum() { #[derive(PartialEq, Debug, Serialize, Deserialize)] enum ExternallyTagged { Struct { a: i32 }, Other(()), } #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "tag")] enum InternallyTagged { Newtype(ExternallyTagged), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: InternallyTagged, #[serde(flatten)] b: ExternallyTagged, } assert_eq!( check_roundtrip( &Flattened { a: InternallyTagged::Newtype(ExternallyTagged::Other(())), b: ExternallyTagged::Struct { a: 42 }, }, PrettyConfig::default() ), Err(Err(SpannedError { code: Error::InvalidValueForType { expected: String::from("map with a single key"), found: String::from("a map"), }, position: Position { line: 7, col: 1 } })) ); } #[test] fn flattened_map_inside_option_beside_flattened_struct_variant() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Struct { a: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: Untagged, #[serde(flatten)] b: Option>, } assert_eq!( check_roundtrip( &Flattened { a: Untagged::Struct { a: 42, }, b: Some([(String::from("b"), 24)].into_iter().collect()), }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Struct { a: 42 }, b: Some({\"b\": 24}) } != Flattened { a: Struct { a: 42 }, b: Some({\"a\": 42, \"b\": 24}) }")))) ); } #[test] fn flattened_untagged_struct_beside_flattened_untagged_struct() { #[derive(PartialEq, Debug, Serialize, Deserialize)] #[serde(untagged)] enum Untagged { Struct { a: i32 }, Other { b: i32 }, } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: Untagged, #[serde(flatten)] b: Untagged, } assert_eq!( check_roundtrip( &Flattened { a: Untagged::Struct { a: 42, }, b: Untagged::Other { b: 24, }, }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Struct { a: 42 }, b: Other { b: 24 } } != Flattened { a: Struct { a: 42 }, b: Struct { a: 42 } }")))) ); } #[test] fn flattened_field_inside_flattened_struct_alongside_map() { #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Units { a: i32, #[serde(flatten)] b: (), } #[derive(PartialEq, Debug, Serialize, Deserialize)] struct Flattened { #[serde(flatten)] a: Units, #[serde(flatten)] b: BTreeMap, } assert_eq!( check_roundtrip( &Flattened { a: Units { a: 42, b: (), }, b: [(String::from("c"), 24)].into_iter().collect(), }, PrettyConfig::default() ), Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Units { a: 42, b: () }, b: {\"c\": 24} } != Flattened { a: Units { a: 42, b: () }, b: {\"a\": 42, \"c\": 24} }")))) ); } fn check_roundtrip( val: &T, config: PrettyConfig, ) -> Result<(), Result> { let ron = ron::ser::to_string_pretty(val, config).map_err(|err| Ok(err))?; println!("{ron}"); let de = ron::from_str(&ron).map_err(|err| Err(err))?; if val == &de { Ok(()) } else { Err(Ok(Error::Message(format!( "ROUNDTRIP error: {:?} != {:?}", val, de )))) } }