From 2cfada117b209d1e26bdf9b5e702aea439736c71 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Thu, 2 Nov 2023 11:35:10 -0700 Subject: [PATCH] work on writing and reading configs --- env.go | 165 +++++++++++++++++++++++++++++++++++++++++++++++---------- go.mod | 5 ++ go.sum | 6 +++ 3 files changed, 148 insertions(+), 28 deletions(-) create mode 100644 go.sum diff --git a/env.go b/env.go index 6534dc8..7d9dcea 100644 --- a/env.go +++ b/env.go @@ -1,50 +1,57 @@ package config import ( + "encoding/json" "errors" "fmt" "os" "strings" + "sync" "time" + + "github.com/BurntSushi/toml" + "gopkg.in/yaml.v3" ) -type ConfigType string +type configType string const ( - ConfigTypeTOML ConfigType = "toml" - ConfigTypeYAML ConfigType = "yaml" - ConfigTypeJSON ConfigType = "json" + ConfigTypeTOML configType = "toml" + ConfigTypeYAML configType = "yaml" + ConfigTypeJSON configType = "json" ) -type ConfigManager struct { - configFileUsed string - configType ConfigType - envPrefix string - mapConfig map[string]any - defaultConfig map[string]any - envConfig map[string]string +type ConfigMap struct { + Key string + Value any } -var ( - ErrConfigFileNotFound = errors.New("config File Not Found") -) +type ConfigManager struct { + configFileUsed string + configType configType + envPrefix string + mapConfig map[string]ConfigMap + defaultConfig map[string]ConfigMap + envConfig map[string]ConfigMap + combinedConfig map[string]ConfigMap + mutex sync.RWMutex + explicitDefaults bool +} + +var ErrConfigFileNotFound = errors.New("config File Not Found") func NewConfigManager(automaticEnv bool) *ConfigManager { cm := ConfigManager{} - cm.envConfig = make(map[string]string) - cm.mapConfig = make(map[string]any) - cm.defaultConfig = make(map[string]any) + cm.envConfig = make(map[string]ConfigMap) + cm.mapConfig = make(map[string]ConfigMap) + cm.defaultConfig = make(map[string]ConfigMap) + cm.combinedConfig = make(map[string]ConfigMap) cm.envPrefix = "" envSet := os.Environ() for _, env := range envSet { kv := strings.Split(env, "=") - cm.envConfig[kv[0]] = kv[1] - lowerKey := strings.ToLower(kv[0]) - if cm.envConfig[lowerKey] == "" { - // if the key is not set, set it as the lower case of the key - // but don't clobber any existing, more specific (already lowercase) value - cm.envConfig[lowerKey] = kv[1] - } + lower := strings.ToLower(kv[0]) + cm.envConfig[lower] = ConfigMap{Key: kv[0], Value: kv[1]} } return &cm } @@ -53,7 +60,59 @@ func (c *ConfigManager) ConfigFileUsed() string { return c.configFileUsed } -func (c *ConfigManager) WriteConfig() { +func (c *ConfigManager) UseExplicitDefaults(enable bool) { + c.explicitDefaults = enable +} + +func (c *ConfigManager) collapse() { + c.mutex.RLock() + defer c.mutex.RUnlock() + ccm := make(map[string]ConfigMap) + for k, v := range c.defaultConfig { + ccm[k] = v + if _, ok := c.envConfig[k]; ok { + ccm[k] = c.envConfig[k] + } + } + for k, v := range c.mapConfig { + ccm[k] = v + } + c.combinedConfig = ccm +} + +func (c *ConfigManager) WriteConfig() error { + c.mutex.RLock() + defer c.mutex.RUnlock() + switch c.configType { + case ConfigTypeTOML: + f, err := os.Create(c.configFileUsed) + if err != nil { + return err + } + defer f.Close() + enc := toml.NewEncoder(f) + err = enc.Encode(c.combinedConfig) + return err + case ConfigTypeYAML: + f, err := os.Create(c.configFileUsed) + if err != nil { + return err + } + defer f.Close() + enc := yaml.NewEncoder(f) + err = enc.Encode(c.combinedConfig) + return err + case ConfigTypeJSON: + f, err := os.Create(c.configFileUsed) + if err != nil { + return err + } + defer f.Close() + enc := json.NewEncoder(f) + return enc.Encode(c.combinedConfig) + default: + return fmt.Errorf("config type %s not supported", c.configType) + } } func (c *ConfigManager) GetBool(key string) bool { @@ -88,22 +147,72 @@ func (c *ConfigManager) SetConfigType(configType string) error { return nil } -func (c *ConfigManager) SetDefault(key string, value any) { -} - func (c *ConfigManager) SetEnvPrefix(prefix string) { + c.envPrefix = prefix } func (c *ConfigManager) Set(key string, value any) { + c.mutex.Lock() + defer c.mutex.Unlock() + lower := strings.ToLower(key) + c.mapConfig[lower] = ConfigMap{Key: key, Value: value} +} + +func (c *ConfigManager) SetDefault(key string, value any) { + c.mutex.Lock() + defer c.mutex.Unlock() + lower := strings.ToLower(key) + c.defaultConfig[lower] = ConfigMap{Key: key, Value: value} } func (c *ConfigManager) GetIntSlice(key string) []int { } func (c *ConfigManager) ReadInConfig() error { + c.mutex.Lock() + defer c.mutex.Unlock() + // assume config = map[string]any + confFileData, err := readFile(c.configFileUsed, c.configType) + if err != nil { + return err + } + conf := make(map[string]ConfigMap) + for k, v := range confFileData { + lower := strings.ToLower(k) + conf[lower] = ConfigMap{Key: k, Value: v} + } + c.mapConfig = conf return nil } +func readFile(filename string, fileType configType) (map[string]any, error) { + fileData := make(map[string]any) + switch fileType { + case ConfigTypeTOML: + _, err := toml.DecodeFile(filename, &fileData) + return fileData, err + case ConfigTypeYAML: + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + d := yaml.NewDecoder(f) + err = d.Decode(&fileData) + return fileData, err + case ConfigTypeJSON: + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + err = json.NewDecoder(f).Decode(&fileData) + return fileData, err + default: + return nil, fmt.Errorf("config type %s not supported", fileType) + } +} + func (c *ConfigManager) SetConfigName(name string) { } diff --git a/go.mod b/go.mod index bbdaf4b..b956201 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module github.com/taigrr/jety go 1.21.3 + +require ( + github.com/BurntSushi/toml v1.3.2 + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..09e59e8 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=