From ecfaeabf2a92e26c6a521d5fac404a0fff6263b6 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 16 Sep 2026 01:34:45 +0200 Subject: milestone 1 review: claims span the run, explain's chain is opt-in and its own, overrides keyed by clean path, splice and enum guards --- internal/config/load.go | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'internal/config/load.go') diff --git a/internal/config/load.go b/internal/config/load.go index 3811802..14ce635 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -8,6 +8,7 @@ import ( "io/fs" "os" "path/filepath" + "sort" "krino/internal/sexp" "krino/internal/xdg" @@ -20,15 +21,45 @@ type Config struct { Dirs []*Dir } +// unusedOverrides reports every override LoadWith never read: text meant +// for a file this load does not touch, which would otherwise pass as +// checked while the file on disk was read instead (plan 13 review F4). +func unusedOverrides(over map[string][]byte, used map[string]bool, mainFile string) []*Diag { + var out []*Diag + for file := range over { + if !used[file] { + out = append(out, &Diag{File: mainFile, Msg: fmt.Sprintf("unsaved text for %s, which this configuration does not read", file)}) + } + } + sort.Slice(out, func(i, j int) bool { return out[i].Msg < out[j].Msg }) + return out +} + // readSource is the text of file: the caller's override when it has one, -// else the file's own bytes. -func readSource(overrides map[string][]byte, file string) ([]byte, error) { - if src, ok := overrides[file]; ok { +// else the file's own bytes. It records which overrides were used, so +// LoadWith can report one it never reached. +func readSource(overrides map[string][]byte, used map[string]bool, file string) ([]byte, error) { + key := filepath.Clean(file) + if src, ok := overrides[key]; ok { + used[key] = true return src, nil } return os.ReadFile(file) } +// cleanOverrides keys the caller's overrides by cleaned path, so a file +// named with a "." or ".." segment is still recognised as itself. +func cleanOverrides(overrides map[string][]byte) map[string][]byte { + if len(overrides) == 0 { + return nil + } + out := make(map[string][]byte, len(overrides)) + for file, src := range overrides { + out[filepath.Clean(file)] = src + } + return out +} + // DefaultFile is krino.conf in the XDG config directory. func DefaultFile() string { return filepath.Join(xdg.ConfigHome(), "krino", "krino.conf") @@ -52,7 +83,9 @@ func Load(mainFile string, names ...string) (*Config, []*Diag) { // checked exactly as a run would read it (GUI design ยง1.3). A nil map is // Load. func LoadWith(mainFile string, overrides map[string][]byte, names ...string) (*Config, []*Diag) { - src, err := readSource(overrides, mainFile) + over := cleanOverrides(overrides) + used := map[string]bool{} + src, err := readSource(over, used, mainFile) if errors.Is(err, fs.ErrNotExist) { return nil, []*Diag{{File: mainFile, Msg: "not found; create it with: krino init"}} } @@ -80,7 +113,7 @@ func LoadWith(mainFile string, overrides map[string][]byte, names ...string) (*C } for _, name := range want { file := DirFile(mainFile, name) - src, err := readSource(overrides, file) + src, err := readSource(over, used, file) if err != nil { msg := err.Error() if errors.Is(err, fs.ErrNotExist) { @@ -93,7 +126,7 @@ func LoadWith(mainFile string, overrides map[string][]byte, names ...string) (*C errs = append(errs, derrs...) cfg.Dirs = append(cfg.Dirs, dir) } - return cfg, errs + return cfg, append(errs, unusedOverrides(over, used, mainFile)...) } // Resolved is the settings that apply in dir: built-in, then the main -- cgit v1.3