Error when deserializing struct if encountering unknown fields.

Issue #225
This commit is contained in:
Azriel Hoh 2018-11-12 10:09:30 +13:00
parent 7862f04d08
commit 1656e2e51d
2 changed files with 81 additions and 0 deletions

View file

@ -178,6 +178,16 @@ enum ErrorKind {
/// Dotted key attempted to extend something that is not a table. /// Dotted key attempted to extend something that is not a table.
DottedKeyInvalidType, DottedKeyInvalidType,
/// An unexpected key was encountered.
///
/// Used when deserializing a struct with a limited set of fields.
UnexpectedKeys {
/// The unexpected keys.
keys: Vec<String>,
/// Keys that may be specified.
available: &'static [&'static str],
},
#[doc(hidden)] #[doc(hidden)]
__Nonexhaustive, __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::<Vec<Cow<'de, str>>>();
if !extra_fields.is_empty() {
return Err(Error::from_kind(ErrorKind::UnexpectedKeys {
keys: extra_fields.iter().map(|k| k.to_string()).collect::<Vec<_>>(),
available: fields,
}));
}
}
_ => {}
}
if name == spanned::NAME && fields == &[spanned::START, spanned::END, spanned::VALUE] { if name == spanned::NAME && fields == &[spanned::START, spanned::END, spanned::VALUE] {
let start = self.value.start; let start = self.value.start;
let end = self.value.end; let end = self.value.end;
@ -1653,6 +1685,14 @@ impl fmt::Display for Error {
ErrorKind::DottedKeyInvalidType => { ErrorKind::DottedKeyInvalidType => {
"dotted key attempted to extend non-table type".fmt(f)? "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!(), ErrorKind::__Nonexhaustive => panic!(),
} }
@ -1700,6 +1740,7 @@ impl error::Error for Error {
ErrorKind::ExpectedTupleIndex { .. } => "expected table key", ErrorKind::ExpectedTupleIndex { .. } => "expected table key",
ErrorKind::ExpectedEmptyTable => "expected empty table", ErrorKind::ExpectedEmptyTable => "expected empty table",
ErrorKind::DottedKeyInvalidType => "dotted key invalid type", ErrorKind::DottedKeyInvalidType => "dotted key invalid type",
ErrorKind::UnexpectedKeys { .. } => "unexpected keys in table",
ErrorKind::__Nonexhaustive => panic!(), ErrorKind::__Nonexhaustive => panic!(),
} }
} }

View file

@ -20,6 +20,46 @@ struct Multi {
enums: Vec<TheEnum>, enums: Vec<TheEnum>,
} }
#[test]
fn invalid_variant_returns_error_with_good_message_string() {
let error = toml::from_str::<TheEnum>("\"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::<TheEnum>("{ 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::<TheEnum>("{ 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::<TheEnum>("{ 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 { mod enum_unit {
use super::*; use super::*;