|
@@ -21,8 +21,8 @@ var loaders = map[string]func([]byte, any) error{
|
|
}
|
|
}
|
|
|
|
|
|
type fieldInfo struct {
|
|
type fieldInfo struct {
|
|
- name string
|
|
|
|
children map[string]fieldInfo
|
|
children map[string]fieldInfo
|
|
|
|
+ mapField *fieldInfo
|
|
}
|
|
}
|
|
|
|
|
|
// Load loads config into v from file, .json, .yaml and .yml are acceptable.
|
|
// Load loads config into v from file, .json, .yaml and .yml are acceptable.
|
|
@@ -107,21 +107,19 @@ func MustLoad(path string, v any, opts ...Option) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-func addOrMergeFields(info map[string]fieldInfo, key, name string, fields map[string]fieldInfo) {
|
|
|
|
- if prev, ok := info[key]; ok {
|
|
|
|
|
|
+func addOrMergeFields(info fieldInfo, key string, child fieldInfo) {
|
|
|
|
+ if prev, ok := info.children[key]; ok {
|
|
// merge fields
|
|
// merge fields
|
|
- for k, v := range fields {
|
|
|
|
|
|
+ for k, v := range child.children {
|
|
prev.children[k] = v
|
|
prev.children[k] = v
|
|
}
|
|
}
|
|
|
|
+ prev.mapField = child.mapField
|
|
} else {
|
|
} else {
|
|
- info[key] = fieldInfo{
|
|
|
|
- name: name,
|
|
|
|
- children: fields,
|
|
|
|
- }
|
|
|
|
|
|
+ info.children[key] = child
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-func buildFieldsInfo(tp reflect.Type) map[string]fieldInfo {
|
|
|
|
|
|
+func buildFieldsInfo(tp reflect.Type) fieldInfo {
|
|
tp = mapping.Deref(tp)
|
|
tp = mapping.Deref(tp)
|
|
|
|
|
|
switch tp.Kind() {
|
|
switch tp.Kind() {
|
|
@@ -130,46 +128,54 @@ func buildFieldsInfo(tp reflect.Type) map[string]fieldInfo {
|
|
case reflect.Array, reflect.Slice:
|
|
case reflect.Array, reflect.Slice:
|
|
return buildFieldsInfo(mapping.Deref(tp.Elem()))
|
|
return buildFieldsInfo(mapping.Deref(tp.Elem()))
|
|
default:
|
|
default:
|
|
- return nil
|
|
|
|
|
|
+ return fieldInfo{}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo {
|
|
|
|
- info := make(map[string]fieldInfo)
|
|
|
|
|
|
+func buildStructFieldsInfo(tp reflect.Type) fieldInfo {
|
|
|
|
+ info := fieldInfo{
|
|
|
|
+ children: make(map[string]fieldInfo),
|
|
|
|
+ }
|
|
|
|
|
|
for i := 0; i < tp.NumField(); i++ {
|
|
for i := 0; i < tp.NumField(); i++ {
|
|
field := tp.Field(i)
|
|
field := tp.Field(i)
|
|
name := field.Name
|
|
name := field.Name
|
|
lowerCaseName := toLowerCase(name)
|
|
lowerCaseName := toLowerCase(name)
|
|
ft := mapping.Deref(field.Type)
|
|
ft := mapping.Deref(field.Type)
|
|
-
|
|
|
|
// flatten anonymous fields
|
|
// flatten anonymous fields
|
|
if field.Anonymous {
|
|
if field.Anonymous {
|
|
- if ft.Kind() == reflect.Struct {
|
|
|
|
|
|
+ switch ft.Kind() {
|
|
|
|
+ case reflect.Struct:
|
|
fields := buildFieldsInfo(ft)
|
|
fields := buildFieldsInfo(ft)
|
|
- for k, v := range fields {
|
|
|
|
- addOrMergeFields(info, k, v.name, v.children)
|
|
|
|
|
|
+ for k, v := range fields.children {
|
|
|
|
+ addOrMergeFields(info, k, v)
|
|
|
|
+ }
|
|
|
|
+ info.mapField = fields.mapField
|
|
|
|
+ case reflect.Map:
|
|
|
|
+ elemField := buildFieldsInfo(mapping.Deref(ft.Elem()))
|
|
|
|
+ info.children[lowerCaseName] = fieldInfo{
|
|
|
|
+ mapField: &elemField,
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- info[lowerCaseName] = fieldInfo{
|
|
|
|
- name: name,
|
|
|
|
|
|
+ default:
|
|
|
|
+ info.children[lowerCaseName] = fieldInfo{
|
|
children: make(map[string]fieldInfo),
|
|
children: make(map[string]fieldInfo),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
|
|
|
|
- var fields map[string]fieldInfo
|
|
|
|
|
|
+ var finfo fieldInfo
|
|
switch ft.Kind() {
|
|
switch ft.Kind() {
|
|
case reflect.Struct:
|
|
case reflect.Struct:
|
|
- fields = buildFieldsInfo(ft)
|
|
|
|
|
|
+ finfo = buildFieldsInfo(ft)
|
|
case reflect.Array, reflect.Slice:
|
|
case reflect.Array, reflect.Slice:
|
|
- fields = buildFieldsInfo(ft.Elem())
|
|
|
|
|
|
+ finfo = buildFieldsInfo(ft.Elem())
|
|
case reflect.Map:
|
|
case reflect.Map:
|
|
- fields = buildFieldsInfo(ft.Elem())
|
|
|
|
|
|
+ elemInfo := buildFieldsInfo(mapping.Deref(ft.Elem()))
|
|
|
|
+ finfo.mapField = &elemInfo
|
|
}
|
|
}
|
|
|
|
|
|
- addOrMergeFields(info, lowerCaseName, name, fields)
|
|
|
|
|
|
+ addOrMergeFields(info, lowerCaseName, finfo)
|
|
}
|
|
}
|
|
|
|
|
|
return info
|
|
return info
|
|
@@ -179,7 +185,7 @@ func toLowerCase(s string) string {
|
|
return strings.ToLower(s)
|
|
return strings.ToLower(s)
|
|
}
|
|
}
|
|
|
|
|
|
-func toLowerCaseInterface(v any, info map[string]fieldInfo) any {
|
|
|
|
|
|
+func toLowerCaseInterface(v any, info fieldInfo) any {
|
|
switch vv := v.(type) {
|
|
switch vv := v.(type) {
|
|
case map[string]any:
|
|
case map[string]any:
|
|
return toLowerCaseKeyMap(vv, info)
|
|
return toLowerCaseKeyMap(vv, info)
|
|
@@ -194,19 +200,21 @@ func toLowerCaseInterface(v any, info map[string]fieldInfo) any {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-func toLowerCaseKeyMap(m map[string]any, info map[string]fieldInfo) map[string]any {
|
|
|
|
|
|
+func toLowerCaseKeyMap(m map[string]any, info fieldInfo) map[string]any {
|
|
res := make(map[string]any)
|
|
res := make(map[string]any)
|
|
|
|
|
|
for k, v := range m {
|
|
for k, v := range m {
|
|
- ti, ok := info[k]
|
|
|
|
|
|
+ ti, ok := info.children[k]
|
|
if ok {
|
|
if ok {
|
|
- res[k] = toLowerCaseInterface(v, ti.children)
|
|
|
|
|
|
+ res[k] = toLowerCaseInterface(v, ti)
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
|
|
|
|
lk := toLowerCase(k)
|
|
lk := toLowerCase(k)
|
|
- if ti, ok = info[lk]; ok {
|
|
|
|
- res[lk] = toLowerCaseInterface(v, ti.children)
|
|
|
|
|
|
+ if ti, ok = info.children[lk]; ok {
|
|
|
|
+ res[lk] = toLowerCaseInterface(v, ti)
|
|
|
|
+ } else if info.mapField != nil {
|
|
|
|
+ res[k] = toLowerCaseInterface(v, *info.mapField)
|
|
} else {
|
|
} else {
|
|
res[k] = v
|
|
res[k] = v
|
|
}
|
|
}
|