aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/de.rs41
-rw-r--r--tests/enum_external_deserialize.rs40
2 files changed, 81 insertions, 0 deletions
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<String>,
+ /// 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::<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] {
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<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 {
use super::*;