1
0
mirror of https://github.com/Llewellynvdm/starship.git synced 2024-12-03 18:38:23 +00:00

fix(config): unrecognized config properties don't cause config error (#4547)

* Fix #4481, config does not error when unrecognized properties are present

* cleanup: use stuct update syntax to improve readability

from review feedback

Co-authored-by: David Knaack <davidkna@users.noreply.github.com>

* cleanup: renamed ValueDeserializer func w/ better name

* cleanup: added test to cover unknown key retry condition

Co-authored-by: David Knaack <davidkna@users.noreply.github.com>
This commit is contained in:
Brahm Lower 2022-11-20 09:27:48 -08:00 committed by GitHub
parent c2d3845dbb
commit 1b03ef21f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 4 deletions

View File

@ -48,7 +48,17 @@ impl<'a, T: Deserialize<'a> + Default> ModuleConfig<'a, ValueError> for T {
/// Create `ValueDeserializer` wrapper and use it to call `Deserialize::deserialize` on it. /// Create `ValueDeserializer` wrapper and use it to call `Deserialize::deserialize` on it.
fn from_config(config: &'a Value) -> Result<Self, ValueError> { fn from_config(config: &'a Value) -> Result<Self, ValueError> {
let deserializer = ValueDeserializer::new(config); let deserializer = ValueDeserializer::new(config);
T::deserialize(deserializer) T::deserialize(deserializer).or_else(|err| {
// If the error is an unrecognized key, print a warning and run
// deserialize ignoring that error. Otherwise, just return the error
if err.to_string().contains("Unknown key") {
log::warn!("{}", err);
let deserializer2 = ValueDeserializer::new(config).with_allow_unknown_keys();
T::deserialize(deserializer2)
} else {
Err(err)
}
})
} }
} }
@ -582,6 +592,24 @@ mod tests {
assert_eq!(rust_config.switch_c, Switch::Off); assert_eq!(rust_config.switch_c, Switch::Off);
} }
#[test]
fn test_load_unknown_key_config() {
#[derive(Clone, Default, Deserialize)]
#[serde(default)]
struct TestConfig<'a> {
pub foo: &'a str,
}
let config = toml::toml! {
foo = "test"
bar = "ignore me"
};
let rust_config = TestConfig::from_config(&config);
assert!(rust_config.is_ok());
assert_eq!(rust_config.unwrap().foo, "test");
}
#[test] #[test]
fn test_from_string() { fn test_from_string() {
let config = Value::String(String::from("S")); let config = Value::String(String::from("S"));

View File

@ -13,6 +13,7 @@ pub struct ValueDeserializer<'de> {
value: &'de Value, value: &'de Value,
info: Option<StructInfo>, info: Option<StructInfo>,
current_key: Option<&'de str>, current_key: Option<&'de str>,
error_on_ignored: bool,
} }
/// When deserializing a struct, this struct stores information about the struct. /// When deserializing a struct, this struct stores information about the struct.
@ -28,14 +29,28 @@ impl<'de> ValueDeserializer<'de> {
value, value,
info: None, info: None,
current_key: None, current_key: None,
error_on_ignored: true,
} }
} }
fn with_info(value: &'de Value, info: Option<StructInfo>, current_key: &'de str) -> Self { fn with_info(
value: &'de Value,
info: Option<StructInfo>,
current_key: &'de str,
ignored: bool,
) -> Self {
ValueDeserializer { ValueDeserializer {
value, value,
info, info,
current_key: Some(current_key), current_key: Some(current_key),
error_on_ignored: ignored,
}
}
pub fn with_allow_unknown_keys(self) -> Self {
ValueDeserializer {
error_on_ignored: false,
..self
} }
} }
} }
@ -83,7 +98,12 @@ impl<'de> Deserializer<'de> for ValueDeserializer<'de> {
let map = MapDeserializer::new(t.iter().map(|(k, v)| { let map = MapDeserializer::new(t.iter().map(|(k, v)| {
( (
k.as_str(), k.as_str(),
ValueDeserializer::with_info(v, self.info, k.as_str()), ValueDeserializer::with_info(
v,
self.info,
k.as_str(),
self.error_on_ignored,
),
) )
})); }));
map.deserialize_map(visitor) map.deserialize_map(visitor)
@ -131,6 +151,10 @@ impl<'de> Deserializer<'de> for ValueDeserializer<'de> {
return visitor.visit_none(); return visitor.visit_none();
} }
if !self.error_on_ignored {
return visitor.visit_none();
}
let did_you_mean = match (self.current_key, self.info) { let did_you_mean = match (self.current_key, self.info) {
(Some(key), Some(StructInfo { fields, .. })) => fields (Some(key), Some(StructInfo { fields, .. })) => fields
.iter() .iter()
@ -322,13 +346,17 @@ mod test {
let value = toml::toml! { let value = toml::toml! {
unknown_key = "foo" unknown_key = "foo"
}; };
let deserializer = ValueDeserializer::new(&value);
let deserializer = ValueDeserializer::new(&value);
let result = StarshipRootConfig::deserialize(deserializer).unwrap_err(); let result = StarshipRootConfig::deserialize(deserializer).unwrap_err();
assert_eq!( assert_eq!(
format!("{result}"), format!("{result}"),
"Error in 'StarshipRoot' at 'unknown_key': Unknown key" "Error in 'StarshipRoot' at 'unknown_key': Unknown key"
); );
let deserializer2 = ValueDeserializer::new(&value).with_allow_unknown_keys();
let result = StarshipRootConfig::deserialize(deserializer2);
assert!(result.is_ok());
} }
#[test] #[test]