Skip to content

fix(prefs): 解析失败时逐字段抢救+备份,避免升级改 schema 静默清空设置 - #791

Merged
H-Chris233 merged 2 commits into
Open-Less:betafrom
bigsongeth:feat/prefs-salvage
Jul 13, 2026
Merged

H-Chris233 merged 2 commits into
Open-Less:betafrom
bigsongeth:feat/prefs-salvage

Conversation

@bigsongeth

@bigsongeth bigsongeth commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

User description

问题

preferences.json 走严格反序列化:容器级 #[serde(default)] 能容忍「缺字段」(老文件读新版本 OK),但扛不住「字段仍在、值非法」——当某次版本更新改了 prefs 的 schema(如重命名枚举变体、改字段类型),老用户文件里的旧值在新版本里不再合法,整份 UserPreferences 解析失败 → 旧代码 unwrap_or_else(default) 静默回落默认值,用户随手改一项就把历史设置(热键、模型选择、风格…)永久覆盖成默认。

改动

read_preferences 严格解析失败时,不再静默重置,而是:

  1. 原样备份坏文件为 preferences.corrupt-<ts>.json,永不销毁;
  2. 逐字段抢救(UserPreferences::salvage_from_json_bytes)——把 JSON 当对象逐 key 试解析,只把值非法的字段回默认,其余仍合法的设置全部保留;
  3. 写回抢救结果,得到干净可解析的文件。

新增测试 salvage_preserves_valid_fields_when_one_value_is_invalid:构造 defaultMode 非法但 dictationHotkey / activeAsrProvider 合法的文件,断言严格解析必失败、抢救后热键与 ASR 保住、仅非法字段回默认。

适用性

面向任何跨 schema 变更升级的用户,不局限于某种安装方式——只要某次发布改了 prefs 结构,此改动就能避免老用户设置被整份清空。维护者若认为发布流程已通过 serde default 覆盖此风险,可自行取舍。

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com


PR Type

Bug fix, Enhancement


Description

  • Backup corrupt preferences.json before salvage

  • Per-field rescue: keep valid values, drop only invalid ones

  • Write back salvaged file to avoid silent setting loss


Diagram Walkthrough

flowchart LR
  A["Strict decode fails"] --> B["Backup original file"]
  B --> C["Parse JSON as Value"]
  C --> D{Is object?}
  D -- yes --> E["Normalize aliases"]
  E --> F["For each field, test validity"]
  F --> G["Keep valid fields, drop invalid"]
  G --> H["Re-serialize and write back"]
  H --> I["Return salvaged UserPreferences"]
  D -- no --> J["Use defaults"]
Loading

File Walkthrough

Relevant files
Enhancement
preferences.rs
Backup & salvage corrupt preferences on parse failure       

openless-all/app/src-tauri/src/persistence/preferences.rs

  • On strict parse failure, backup original file instead of silently
    resetting
  • Add backup_unparseable_preferences for atomic backup with unique name
  • Salvage valid fields using UserPreferences::salvage_from_json_bytes
  • Write back salvaged preferences to avoid future decode failures
+133/-17
types.rs
Add per-field salvage logic for UserPreferences                   

openless-all/app/src-tauri/src/types.rs

  • Implement UserPreferences::salvage_from_json_bytes for per-field
    rescue
  • Add preference_field_is_valid to test individual field validity
  • Add normalize_preference_aliases to handle duplicate legacy keys
  • Add salvage_without_incomplete_legacy_hotkey for edge cases
  • Include unit tests verifying field preservation and alias handling
+175/-0 

@github-actions

github-actions Bot commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit eac5af5)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit eac5af5

@H-Chris233
H-Chris233 merged commit 01bff73 into Open-Less:beta Jul 13, 2026
5 checks passed
@bigsongeth
bigsongeth deleted the feat/prefs-salvage branch July 22, 2026 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants