diff --git a/crates/engine/src/settings/prefs/document.rs b/crates/engine/src/settings/prefs/document.rs index 1f68c4a06..5db41e554 100644 --- a/crates/engine/src/settings/prefs/document.rs +++ b/crates/engine/src/settings/prefs/document.rs @@ -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 = 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::(Value::Object(trial)).is_ok() { diff --git a/crates/engine/src/tests/settings.rs b/crates/engine/src/tests/settings.rs index 5e38c90a3..7d65061ea 100644 --- a/crates/engine/src/tests/settings.rs +++ b/crates/engine/src/tests/settings.rs @@ -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#"{