返回 DeepSeek-Reasonix
remote_config_edit.go
根目录 / desktop / remote_config_edit.go
1 package main
2
3 import (
4 "fmt"
5 "strings"
6
7 "reasonix/internal/config"
8 )
9
10 // editUserConfig runs mutate against the user-global config under the edit lock
11 // and saves it there. Remote hosts are user-global (pinned in LoadForRoot).
12 func editUserConfig(mutate func(*config.Config) error) error {
13 return editUserConfigIfChanged(func(cfg *config.Config) (bool, error) {
14 if err := mutate(cfg); err != nil {
15 return false, err
16 }
17 return true, nil
18 })
19 }
20
21 // editUserConfigIfChanged keeps both the read and the no-op decision inside
22 // the edit lock. Callers can avoid rewriting an unchanged file without a
23 // stale read racing another process-local config mutation.
24 func editUserConfigIfChanged(mutate func(*config.Config) (bool, error)) error {
25 path := config.UserConfigPath()
26 if strings.TrimSpace(path) == "" {
27 return fmt.Errorf("cannot resolve user config path")
28 }
29 return editUserConfigIfChangedAtPath(path, config.LockConfigFileEdits, mutate)
30 }
31
32 // editUserConfigIfChangedAtPath is the strict read-modify-write transaction.
33 // The error-returning lock API is required even for a no-op mutation: unlike
34 // Config.SaveTo, that path has no later write at which to surface a failed
35 // cross-process lock acquisition.
36 func editUserConfigIfChangedAtPath(
37 path string,
38 lock func(string) (func(), error),
39 mutate func(*config.Config) (bool, error),
40 ) error {
41 unlock, err := lock(path)
42 if err != nil {
43 return fmt.Errorf("lock user config edits: %w", err)
44 }
45 defer unlock()
46 cfg := config.LoadForEdit(path)
47 if cfg == nil {
48 cfg = config.Default()
49 }
50 baseline := cfg.ModelSettingsBaseline()
51 changed, err := mutate(cfg)
52 if err != nil {
53 return err
54 }
55 if !changed {
56 return nil
57 }
58 return cfg.SaveModelSettingsTo(path, baseline)
59 }
60
60 lines GO