--- internal ---
()
| 630 | // --- internal --- |
| 631 | |
| 632 | func (ps *UserProfileStore) load() { |
| 633 | data, err := os.ReadFile(ps.path) |
| 634 | if err != nil { |
| 635 | return |
| 636 | } |
| 637 | var p UserProfile |
| 638 | if err := json.Unmarshal(data, &p); err != nil { |
| 639 | // Quarantine for recovery instead of leaving the corrupt file in place |
| 640 | // for the next persist to overwrite (see persist.go). |
| 641 | if qpath, qerr := quarantineCorrupt(ps.path); qerr == nil { |
| 642 | ps.logger.Warn("user profile corrupt; quarantined for recovery", |
| 643 | zap.String("quarantine", qpath), zap.Error(err)) |
| 644 | } else { |
| 645 | ps.logger.Warn("user profile corrupt and quarantine failed", zap.Error(qerr)) |
| 646 | } |
| 647 | return |
| 648 | } |
| 649 | if p.TopCommands == nil { |
| 650 | p.TopCommands = make(map[string]int) |
| 651 | } |
| 652 | if p.Preferences == nil { |
| 653 | p.Preferences = make(map[string]string) |
| 654 | } |
| 655 | // Self-heal list fields polluted by earlier append-only versions and |
| 656 | // persist the healed profile, so legacy damage is fixed on the next run |
| 657 | // and never resurfaces. |
| 658 | healed := normalizeLoadedProfile(&p) |
| 659 | ps.profile = p |
| 660 | if healed { |
| 661 | ps.profile.LastUpdated = time.Now() |
| 662 | ps.persist() |
| 663 | } |
| 664 | } |
| 665 | |
| 666 | func (ps *UserProfileStore) persist() { |
| 667 | data, err := json.MarshalIndent(ps.profile, "", " ") |
no test coverage detected