From 1656e2e51d72063c83b8ee6ccb1fd660146054c5 Mon Sep 17 00:00:00 2001 From: Azriel Hoh Date: Mon, 12 Nov 2018 10:09:30 +1300 Subject: [PATCH] Error when deserializing struct if encountering unknown fields. Issue #225 --- src/de.rs | 41 ++++++++++++++++++++++++++++++ tests/enum_external_deserialize.rs | 40 +++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/src/de.rs b/src/de.rs index da30699..c7e8b34 100644 --- a/src/de.rs +++ b/src/de.rs @@ -178,6 +178,16 @@ enum ErrorKind { /// Dotted key attempted to extend something that is not a table. DottedKeyInvalidType, + /// An unexpected key was encountered. + /// + /// Used when deserializing a struct with a limited set of fields. + UnexpectedKeys { + /// The unexpected keys. + keys: Vec, + /// Keys that may be specified. + available: &'static [&'static str], + }, + #[doc(hidden)] __Nonexhaustive, } @@ -547,6 +557,28 @@ impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> { } } + match &self.value.e { + E::InlineTable(values) | E::DottedTable(values) => { + let extra_fields = values.iter() + .filter_map(|(key, _val)| { + if !fields.contains(&&(**key)) { + Some(key.clone()) + } else { + None + } + }) + .collect::>>(); + + if !extra_fields.is_empty() { + return Err(Error::from_kind(ErrorKind::UnexpectedKeys { + keys: extra_fields.iter().map(|k| k.to_string()).collect::>(), + available: fields, + })); + } + } + _ => {} + } + if name == spanned::NAME && fields == &[spanned::START, spanned::END, spanned::VALUE] { let start = self.value.start; let end = self.value.end; @@ -1653,6 +1685,14 @@ impl fmt::Display for Error { ErrorKind::DottedKeyInvalidType => { "dotted key attempted to extend non-table type".fmt(f)? } + ErrorKind::UnexpectedKeys { ref keys, available } => { + write!( + f, + "unexpected keys in table: `{:?}`, available keys: `{:?}`", + keys, + available + )? + } ErrorKind::__Nonexhaustive => panic!(), } @@ -1700,6 +1740,7 @@ impl error::Error for Error { ErrorKind::ExpectedTupleIndex { .. } => "expected table key", ErrorKind::ExpectedEmptyTable => "expected empty table", ErrorKind::DottedKeyInvalidType => "dotted key invalid type", + ErrorKind::UnexpectedKeys { .. } => "unexpected keys in table", ErrorKind::__Nonexhaustive => panic!(), } } diff --git a/tests/enum_external_deserialize.rs b/tests/enum_external_deserialize.rs index 3309ac6..e35f06a 100644 --- a/tests/enum_external_deserialize.rs +++ b/tests/enum_external_deserialize.rs @@ -20,6 +20,46 @@ struct Multi { enums: Vec, } +#[test] +fn invalid_variant_returns_error_with_good_message_string() { + let error = toml::from_str::("\"NonExistent\"") + .expect_err("Expected deserialization to fail."); + + assert_eq!( + error.to_string(), + "unknown variant `NonExistent`, expected one of `Plain`, `Tuple`, `NewType`, `Struct`" + ); +} + +#[test] +fn invalid_variant_returns_error_with_good_message_inline_table() { + let error = toml::from_str::("{ NonExistent = {} }") + .expect_err("Expected deserialization to fail."); + assert_eq!( + error.to_string(), + "unknown variant `NonExistent`, expected one of `Plain`, `Tuple`, `NewType`, `Struct`" + ); +} + +#[test] +fn extra_field_returns_expected_empty_table_error() { + let error = toml::from_str::("{ Plain = { extra_field = 404 } }") + .expect_err("Expected deserialization to fail."); + + assert_eq!(error.to_string(), "expected empty table"); +} + +#[test] +fn extra_field_returns_expected_empty_table_error_struct_variant() { + let error = toml::from_str::("{ Struct = { value = 123, extra_0 = 0, extra_1 = 1 } }") + .expect_err("Expected deserialization to fail."); + + assert_eq!( + error.to_string(), + r#"unexpected keys in table: `["extra_0", "extra_1"]`, available keys: `["value"]`"# + ); +} + mod enum_unit { use super::*;