diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-11 14:47:10 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-11 15:01:57 +0200 |
| commit | 42b02c47be9b285099203e44a2570636d4ca6f03 (patch) | |
| tree | 82bcb9e19bd886f36e1ca7b2d94a204c988f1fd1 /internal/config/load.go | |
| download | krino-42b02c47be9b285099203e44a2570636d4ca6f03.tar.gz krino-42b02c47be9b285099203e44a2570636d4ca6f03.zip | |
krino: foundation — sexp reader, config language, init/new/check
Diffstat (limited to 'internal/config/load.go')
| -rw-r--r-- | internal/config/load.go | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/internal/config/load.go b/internal/config/load.go new file mode 100644 index 0000000..f6eaeae --- /dev/null +++ b/internal/config/load.go @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package config + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + + "krino/internal/sexp" + "krino/internal/xdg" +) + +// Config is the whole configuration: the main file and the directories it +// includes. +type Config struct { + Main *Main + Dirs []*Dir +} + +// DefaultFile is krino.conf in the XDG config directory. +func DefaultFile() string { + return filepath.Join(xdg.ConfigHome(), "krino", "krino.conf") +} + +// DirFile is where directory name's config lives, beside the main file. +func DirFile(mainFile, name string) string { + return filepath.Join(filepath.Dir(mainFile), "dirs", name+".conf") +} + +// Load reads the main file and the files of the directories it includes. +// With names, only those directories are read, and each must be included. +// The Config is nil only when the main file itself cannot be read. +func Load(mainFile string, names ...string) (*Config, []*Diag) { + src, err := os.ReadFile(mainFile) + if errors.Is(err, fs.ErrNotExist) { + return nil, []*Diag{{File: mainFile, Msg: "not found; create it with: krino init"}} + } + if err != nil { + return nil, []*Diag{{File: mainFile, Msg: err.Error()}} + } + m, errs := ParseMain(mainFile, src) + cfg := &Config{Main: m} + if _, perr := sexp.Parse(mainFile, src); perr != nil { + // krino.conf itself is unreadable: report just that, and load none + // of the directories, so a syntax error never also produces + // "NAME is not in include" for names the caller asked for. + return cfg, errs + } + want := m.Include + if len(names) > 0 { + want = nil + for _, n := range names { + if _, ok := m.IncludePos[n]; !ok { + errs = append(errs, &Diag{File: mainFile, Msg: fmt.Sprintf("%q is not in include", n)}) + continue + } + want = append(want, n) + } + } + for _, name := range want { + file := DirFile(mainFile, name) + src, err := os.ReadFile(file) + if err != nil { + msg := err.Error() + if errors.Is(err, fs.ErrNotExist) { + msg = fmt.Sprintf("included %q, but %s does not exist; create it with: krino new %s PATH", name, file, name) + } + errs = append(errs, &Diag{File: mainFile, Pos: m.IncludePos[name], Msg: msg}) + continue + } + dir, derrs := ParseDir(name, file, src) + errs = append(errs, derrs...) + cfg.Dirs = append(cfg.Dirs, dir) + } + return cfg, errs +} + +// Resolved is the settings that apply in dir: built-in, then the main +// file's defaults, then the directory's own. +func (c *Config) Resolved(dir *Dir) Resolved { + return dir.Settings.Over(c.Main.Defaults.Over(Builtin())) +} + +// LogFile is the main file's (log ...), or the default under XDG_STATE_HOME. +func (c *Config) LogFile() string { + if c.Main.Log != "" { + return c.Main.Log + } + return filepath.Join(xdg.StateHome(), "krino", "krino.log") +} |
