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:
parent
c2d3845dbb
commit
1b03ef21f3
@ -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"));
|
||||||
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user