fix(prefs): when recovering from a bad value, try only the settings' own pages one by one (#3010)

This commit is contained in:
Dylan M. Taylor authored and GitHub committed 2026-11-03 14:10:24 -05:00
1 parent 5464a2380e
commit 2f31c22a09
2 files changed
+24

No files matched your search

@@ -76,7 +76,15 @@ impl Prefs {
Err(_) => {
let mut base = serde_json::to_value(Prefs::default()).unwrap_or_default();
if let (Some(b), Some(o)) = (base.as_object_mut(), v.as_object()) {
// Only the settings' own pages are tried one by one (each trial parses the
// whole document): any other key is kept as it is, so a document of many
// unknown keys costs one pass, not one parse per key.
let pages: Vec<String> = b.keys().cloned().collect();
for (k, x) in o {
if !pages.contains(k) {
b.insert(k.clone(), x.clone());
continue;
}
let mut trial = b.clone();
trial.insert(k.clone(), x.clone());
if serde_json::from_value::<Prefs>(Value::Object(trial)).is_ok() {
+16
View File
@@ -40,6 +40,22 @@ fn prefs_round_trip_through_json() {
assert_eq!(Prefs::from_json(&Prefs::default().to_json()), Prefs::default());
}
/// A document with one bad page and many unknown keys recovers its good pages in one pass per
/// page, not one full parse per key.
#[test]
fn prefs_recovery_tries_only_the_settings_pages() {
let mut doc = serde_json::Map::new();
doc.insert("general".into(), json!({"undoLevels": "not a number"}));
doc.insert("labels".into(), serde_json::to_value(Prefs::default()).unwrap()["labels"].clone());
for i in 0..20_000 {
doc.insert(format!("unknown{i}"), json!({"x": i}));
}
let t = std::time::Instant::now();
let p = Prefs::from_json(&serde_json::Value::Object(doc).to_string());
assert!(t.elapsed() < std::time::Duration::from_secs(10), "{:?}", t.elapsed());
assert_eq!(p.general, Prefs::default().general);
}
#[test]
fn prefs_migrate_old_layout_and_keep_unknown_keys() {
let old = r#"{