From 550c94c56a29e10e049a91ff31e3a9c798494972 Mon Sep 17 00:00:00 2001 From: Ya Hui Liang <46517115@qq.com> Date: Tue, 10 Oct 2023 17:42:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=AF=BB=E5=8F=96=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env/env.go | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++ env/env.md | 0 go.mod | 5 +- go.sum | 2 + 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 env/env.go create mode 100644 env/env.md diff --git a/env/env.go b/env/env.go new file mode 100644 index 0000000..a59c661 --- /dev/null +++ b/env/env.go @@ -0,0 +1,230 @@ +package env + +import ( + "fmt" + "io/ioutil" + "os" + "reflect" + "strconv" + "strings" + + "github.com/go-yaml/yaml" +) + +type EnvConfig struct { + env string + data *interface{} +} + +var envConfig = &EnvConfig{} + +// load configuration file +func LoadEnvConfig(file string, mode string) error { + var _file string + + if mode != "" { + _file = strings.Replace(file, "yaml", mode+".yaml", 1) + } + + data, failed := loadFile(file) + + dataSrc, failedSrc := loadFile(_file) + + // 如果两个文件都加载成功,则进行merge操作, + // 如果只有一个文件加载成功,则使用加载成功的文件 + if data != nil && dataSrc != nil && failed == nil && failedSrc == nil { + envConfig.SetEnv(mode) + envConfig.SetData(data) + envConfig.MergeData(dataSrc) + } else if data != nil && failed == nil { + envConfig.SetEnv(mode) + envConfig.SetData(data) + } else if dataSrc != nil && failedSrc == nil { + envConfig.SetEnv(mode) + envConfig.SetData(dataSrc) + } else { + return fmt.Errorf("file [ %s ] load failed", file) + } + + return nil +} + +func loadFile(file string) (*interface{}, error) { + if err := hasFile(file); err != nil { + return nil, err + } + data, failed := ioutil.ReadFile(file) + if failed != nil { + return nil, failed + } + + var _data = new(interface{}) + + failed = yaml.Unmarshal(data, _data) + if failed != nil { + return nil, failed + } + + return _data, nil +} + +func (p *EnvConfig) SetEnv(mode string) { + p.env = mode +} + +func (p *EnvConfig) SetData(data any) { + p.data = data.(*interface{}) +} + +func (p *EnvConfig) MergeData(data any) interface{} { + srcData := *p.data + distData := (data.(*interface{})) + + retData := mergeKeyForData(&srcData, distData) + envConfig.SetData(&retData) + + return &retData +} + +func (p *EnvConfig) GetRootValue() interface{} { + return *p.data +} + +// Get the value from root +func GetRootValue() interface{} { + return envConfig.GetRootValue() +} + +// Get the value from root for map +func GetRootMapValue() map[interface{}]interface{} { + data := envConfig.GetRootValue() + if data == nil { + return nil + } + return data.(map[interface{}]interface{}) +} + +// Get the value by path like below +// file.yaml +// test: test +// test2: +// test3: test333 +/// GetValue("test2.test3") ===> test333 +func GetValue(path string) (interface{}, error) { + val, err := getValueByPath(path) + return val, err +} + +func getValueByPath(path string) (interface{}, error) { + words := strings.Split(path, ".") + data := GetRootMapValue() + if data == nil { + return nil, fmt.Errorf("No key [ %s ] found!", path) + } + + len := len(words) + var val interface{} + var hasKey bool + for idx, work := range words { + val, hasKey = getValueByKey(work, data) + + if !hasKey { + return nil, fmt.Errorf("No key [ %s ] found!", path) + } + + if idx < len-1 { + data = val.(map[interface{}]interface{}) + } else { + return val, nil + } + } + return nil, nil +} + +func getValueByKey(key string, data map[interface{}]interface{}) (interface{}, bool) { + keys := strings.FieldsFunc(key, func(r rune) bool { + return string(r) == "[" + }) + var val interface{} + var hasKey bool + sliceLen := len(keys) + + if sliceLen > 1 { + for i, k := range keys { + if strings.HasSuffix(k, "]") { + keys[i] = strings.Replace(k, "]", "", 1) + } + } + + val, hasKey = data[keys[0]] + + if !hasKey { + return nil, false + } + + _val, isOk := val.([]interface{}) + + if !isOk { + return nil, false + } + + for i := 1; i < sliceLen; i++ { + idx, err := strconv.Atoi(keys[i]) + if err != nil || len(_val) <= idx { + return nil, false + } + + newVal := _val[idx] + + if i < sliceLen-1 && isArray(newVal) { + _val = newVal.([]interface{}) + } else if i < sliceLen-1 { + return nil, false + } else { + return newVal, true + } + } + } + + val, hasKey = data[keys[0]] + + return val, hasKey +} + +func isArray(param any) bool { + val := reflect.ValueOf(param) + if val.Kind() == reflect.Array || val.Kind() == reflect.Slice { + return true + } + return false +} + +func kind(param any) reflect.Kind { + kind := reflect.ValueOf(param) + return kind.Kind() +} + +func hasFile(file string) error { + if _, err := os.Stat(file); err != nil { + if os.IsNotExist(err) { + return err + } + } + return nil +} + +func mergeKeyForData(a *interface{}, b *interface{}) interface{} { + aa := (*a).(map[interface{}]interface{}) + bb := (*b).(map[interface{}]interface{}) + + for key, val := range bb { + newVal, hasKey := aa[key] + // 如果不存在的key或者类型不同的key直接复制过去 + if !hasKey || kind(val) != kind(newVal) || kind(val) != reflect.Map { + aa[key] = val + } else { + aa[key] = mergeKeyForData(&newVal, &val) + } + } + return aa +} diff --git a/env/env.md b/env/env.md new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod index 3e03066..2ae8037 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,7 @@ go 1.17 require github.com/go-redis/redis v6.15.9+incompatible -require github.com/issue9/assert v1.5.0 // indirect +require ( + github.com/go-yaml/yaml v2.1.0+incompatible + github.com/issue9/assert v1.5.0 // indirect +) diff --git a/go.sum b/go.sum index 872a914..e5aa968 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/issue9/assert v1.5.0 h1:9N71cQh6lt9/3y/WnB2lP0AuCxnTwunTToajoWnVD8M= github.com/issue9/assert v1.5.0/go.mod h1:Yktk83hAVl1SPSYtd9kjhBizuiBIqUQyj+D5SE2yjVY=