diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index ee8a23766..c6b2cfff2 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -1737,7 +1737,6 @@ fn deserialize_untagged_enum_after( quote!(__deserializer), )) }); - let attempts = first_attempt.into_iter().chain(attempts); // TODO this message could be better by saving the errors from the failed // attempts. The heuristic used by TOML was to count the number of fields // processed before an error, and use the error that happened after the @@ -1750,10 +1749,23 @@ fn deserialize_untagged_enum_after( ); let fallthrough_msg = cattrs.expecting().unwrap_or(&fallthrough_msg); + // Ignore any error associated with non-untagged deserialization so that we + // can fall through to the untagged variants. This may be infallible so we + // need to provide the error type. + let first_attempt = first_attempt.map(|expr| { + quote! { + if let _serde::__private::Result::<_, __D::Error>::Ok(__ok) = (|| { + #expr + })() { + return _serde::__private::Ok(__ok); + } + } + }); quote_block! { let __content = <_serde::__private::de::Content as _serde::Deserialize>::deserialize(__deserializer)?; let __deserializer = _serde::__private::de::ContentRefDeserializer::<__D::Error>::new(&__content); + #first_attempt #( if let _serde::__private::Ok(__ok) = #attempts { return _serde::__private::Ok(__ok); diff --git a/test_suite/tests/test_annotations.rs b/test_suite/tests/test_annotations.rs index fa314cbca..90e0a153a 100644 --- a/test_suite/tests/test_annotations.rs +++ b/test_suite/tests/test_annotations.rs @@ -2380,6 +2380,68 @@ fn test_partially_untagged_enum_desugared() { ); } +#[test] +fn test_partially_untagged_internally_tagged_enum() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(tag = "t")] + enum Data { + A, + B, + #[serde(untagged)] + Var(u32), + } + + let data = Data::A; + + assert_de_tokens( + &data, + &[ + Token::Map { len: None }, + Token::Str("t"), + Token::Str("A"), + Token::MapEnd, + ], + ); + + let data = Data::Var(42); + + assert_de_tokens(&data, &[Token::U32(42)]); + + // TODO test error output +} + +#[test] +fn test_partially_untagged_adjacently_tagged_enum() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[serde(tag = "t", content = "c")] + enum Data { + A(u32), + B, + #[serde(untagged)] + Var(u32), + } + + let data = Data::A(7); + + assert_de_tokens( + &data, + &[ + Token::Map { len: None }, + Token::Str("t"), + Token::Str("A"), + Token::Str("c"), + Token::U32(7), + Token::MapEnd, + ], + ); + + let data = Data::Var(42); + + assert_de_tokens(&data, &[Token::U32(42)]); + + // TODO test error output +} + #[test] fn test_flatten_option() { #[derive(Serialize, Deserialize, PartialEq, Debug)]