config: warn on unknown keys instead of dropping them silently

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Id06a7e2c96cfdcb69a21fa0416d988696a6a6964
This commit is contained in:
raf 2026-06-26 10:27:40 +03:00
commit 27a509362b
No known key found for this signature in database
GPG key ID: 29D95B64378DB4BF
3 changed files with 57 additions and 11 deletions

11
Cargo.lock generated
View file

@ -42,6 +42,7 @@ dependencies = [
"pound",
"rustix",
"serde",
"serde_ignored",
"smithay-client-toolkit",
"thiserror",
"toml",
@ -488,6 +489,16 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_ignored"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"

View file

@ -23,6 +23,7 @@ rustix = { version = "1.1.4", features = [
"fs",
] }
serde = { version = "1.0.228", features = ["derive"] }
serde_ignored = "0.1.12"
smithay-client-toolkit = "0.20.0"
thiserror = "2.0.18"
toml = "0.9"

View file

@ -195,8 +195,26 @@ impl Config {
if !path.exists() {
return Self::default();
}
match std::fs::read_to_string(&path) {
Ok(text) => match toml::from_str(&text) {
let text = match std::fs::read_to_string(&path) {
Ok(text) => text,
Err(err) => {
tracing::warn!("read config {}: {err}; using defaults", path.display());
return Self::default();
}
};
// Deserialize through serde_ignored so a typo'd key (`font-sze`) is
// reported instead of silently dropped, while still loading: unknown
// keys stay tolerated, keeping forward-compatibility with newer configs.
let de = match toml::Deserializer::parse(&text) {
Ok(de) => de,
Err(err) => {
tracing::warn!("config {}: {err}; using defaults", path.display());
return Self::default();
}
};
match serde_ignored::deserialize(de, |key| {
tracing::warn!("config {}: unknown key `{key}` ignored", path.display());
}) {
Ok(config) => {
tracing::info!("loaded config from {}", path.display());
config
@ -205,11 +223,6 @@ impl Config {
tracing::warn!("config {}: {err}; using defaults", path.display());
Self::default()
}
},
Err(err) => {
tracing::warn!("read config {}: {err}; using defaults", path.display());
Self::default()
}
}
}
}
@ -253,4 +266,25 @@ mod tests {
assert_eq!(c.main.term, "beer");
assert_eq!(c.scrollback.lines, 10_000);
}
#[test]
fn unknown_keys_are_reported_but_still_load() {
let toml = r##"
[main]
font-sze = 14
font = "JetBrains Mono"
[made-up]
x = 1
"##;
let mut unknown = Vec::new();
let de = toml::Deserializer::parse(toml).unwrap();
let c: Config =
serde_ignored::deserialize(de, |key| unknown.push(key.to_string())).unwrap();
// The typo and the bogus table are both surfaced...
assert!(unknown.iter().any(|k| k == "main.font-sze"), "{unknown:?}");
assert!(unknown.iter().any(|k| k == "made-up"), "{unknown:?}");
// ...yet the valid key still loaded.
assert_eq!(c.main.font, "JetBrains Mono");
}
}