package config import ( "io/fs" "os" "path/filepath" "strings" gofig "github.com/gookit/config/v2" goojson "github.com/gookit/config/v2/json" gooyaml "github.com/gookit/config/v2/yaml" ) var ( defaultLocations = []string{ filepath.Join(os.Getenv("HOME"), "/.ocis/config/"), "/etc/ocis/", ".config/", } // supportedExtensions is determined by gookit/config. supportedExtensions = []string{ "yaml", "yml", "json", } ) // DefaultConfigSources returns a slice with matched expected config files. It sugars coat several aspects of config file // management by assuming there are 3 default locations a config file could be. // It uses globbing to match a config file by name, and retrieve any supported extension supported by our drivers. // It sanitizes the output depending on the list of drivers provided. func DefaultConfigSources(filename string, drivers []string) []string { var sources []string for i := range defaultLocations { dirFS := os.DirFS(defaultLocations[i]) pattern := filename + ".*" matched, _ := fs.Glob(dirFS, pattern) if len(matched) > 0 { // prepend path to results for j := 0; j < len(matched); j++ { matched[j] = filepath.Join(defaultLocations[i], matched[j]) } } sources = append(sources, matched...) } return sanitizeExtensions(sources, drivers, func(a, b string) bool { return strings.HasSuffix(filepath.Base(a), b) }) } // sanitizeExtensions removes elements from "set" which extensions are not in "ext". func sanitizeExtensions(set []string, ext []string, f func(a, b string) bool) []string { var r []string for i := 0; i < len(set); i++ { for j := 0; j < len(ext); j++ { if f(filepath.Base(set[i]), ext[j]) { r = append(r, set[i]) } } } return r } // BindSourcesToStructs assigns any config value from a config file / env variable to struct `dst`. Its only purpose // is to solely modify `dst`, not dealing with the config structs; and do so in a thread safe manner. func BindSourcesToStructs(extension string, dst interface{}) (*gofig.Config, error) { sources := DefaultConfigSources(extension, supportedExtensions) cnf := gofig.NewWithOptions(extension) cnf.WithOptions(func(options *gofig.Options) { options.DecoderConfig.TagName = "ocisConfig" }) cnf.AddDriver(gooyaml.Driver) cnf.AddDriver(goojson.Driver) _ = cnf.LoadFiles(sources...) err := cnf.BindStruct("", &dst) if err != nil { return nil, err } return cnf, nil }