diff --git a/config/config.go b/config/config.go index e3fef11c..1fde1587 100644 --- a/config/config.go +++ b/config/config.go @@ -43,12 +43,14 @@ func NewResolver(file string) (*Resolver, error) { return nil, err } - var m map[string]string - err = yaml.Unmarshal(b, &m) + var rawValues map[string]interface{} + err = yaml.Unmarshal(b, &rawValues) if err != nil { return nil, err } - for k, v := range m { + // Flatten nested YAML values + flattenedValues := flatten(rawValues) + for k, v := range flattenedValues { if _, ok := flags[k]; !ok { flags[k] = v } diff --git a/config/flatten.go b/config/flatten.go new file mode 100644 index 00000000..382f22d9 --- /dev/null +++ b/config/flatten.go @@ -0,0 +1,61 @@ +package config + +import "fmt" + +// flatten flattens the nested struct. +// +// All keys will be joined by dot +// e.g. {"a": {"b":"c"}} => {"a.b":"c"} +// or {"a": {"b":[1,2]}} => {"a.b.0":1, "a.b.1": 2} +func flatten(data map[string]interface{}) map[string]string { + ret := make(map[string]string) + for k, v := range data { + switch typed := v.(type) { + case map[interface{}]interface{}: + for fk, fv := range flatten(convertMap(typed)) { + ret[fmt.Sprintf("%s.%s", k, fk)] = fv + } + case map[string]interface{}: + for fk, fv := range flatten(typed) { + ret[fmt.Sprintf("%s.%s", k, fk)] = fv + } + case []interface{}: + for fk, fv := range flattenSlice(typed) { + ret[fmt.Sprintf("%s.%s", k, fk)] = fv + } + default: + ret[k] = fmt.Sprint(typed) + } + } + return ret +} +func flattenSlice(data []interface{}) map[string]string { + ret := make(map[string]string) + for idx, v := range data { + switch typed := v.(type) { + case map[interface{}]interface{}: + for fk, fv := range flatten(convertMap(typed)) { + ret[fmt.Sprintf("%d,%s", idx, fk)] = fv + } + case map[string]interface{}: + for fk, fv := range flatten(typed) { + ret[fmt.Sprintf("%d,%s", idx, fk)] = fv + } + case []interface{}: + for fk, fv := range flattenSlice(typed) { + ret[fmt.Sprintf("%d,%s", idx, fk)] = fv + } + default: + ret[fmt.Sprint(idx)] = fmt.Sprint(typed) + } + } + return ret +} + +func convertMap(originalMap map[interface{}]interface{}) map[string]interface{} { + convertedMap := map[string]interface{}{} + for key, value := range originalMap { + convertedMap[key.(string)] = value + } + return convertedMap +}