diff --git a/tracker/config.go b/tracker/config.go new file mode 100644 index 0000000..8f17f1a --- /dev/null +++ b/tracker/config.go @@ -0,0 +1,42 @@ +package tracker + +import ( + "embed" + "fmt" + "io/fs" + "os" + "path/filepath" + + "gopkg.in/yaml.v2" +) + +func ReadConfig(embedFS embed.FS, path string, out any) (warning error) { + bytes := must(fs.ReadFile(embedFS, path)) // the file _must_ exist in the embedded fs; panic on purpose if not + if err := yaml.UnmarshalStrict(bytes, out); err != nil { + panic(err) // also, the embedded default config file _must_ be strictly valid yaml; panic if not + } + // now, try to read the user config file, overlapping the default one. + // Notice that the return values are just warnings - there's always the + // default config there so no harm done if the user config is missing or + // invalid. + configDir, err := os.UserConfigDir() + if err != nil { + return fmt.Errorf("ReadConfig %v: %v", path, err) + } + configPath := filepath.Join(configDir, "sointu", path) + bytesUser, err := os.ReadFile(configPath) + if err != nil { + return fmt.Errorf("ReadConfig %v: %v", path, err) + } + if err := yaml.Unmarshal(bytesUser, out); err != nil { + return fmt.Errorf("ReadConfig %v: %v", path, err) + } + return nil +} + +func must[T any](ic T, err error) T { + if err != nil { + panic(err) + } + return ic +}