diff --git a/core/conf/config.go b/core/conf/config.go index bdaf7061..08e7f90f 100644 --- a/core/conf/config.go +++ b/core/conf/config.go @@ -13,8 +13,6 @@ import ( "github.com/zeromicro/go-zero/internal/encoding" ) -const distanceBetweenUpperAndLower = 32 - var loaders = map[string]func([]byte, interface{}) error{ ".json": LoadFromJsonBytes, ".toml": LoadFromTomlBytes, @@ -66,9 +64,9 @@ func LoadFromJsonBytes(content []byte, v interface{}) error { } finfo := buildFieldsInfo(reflect.TypeOf(v)) - camelCaseKeyMap := toCamelCaseKeyMap(m, finfo) + lowerCaseKeyMap := toLowerCaseKeyMap(m, finfo) - return mapping.UnmarshalJsonMap(camelCaseKeyMap, v, mapping.WithCanonicalKeyFunc(toCamelCase)) + return mapping.UnmarshalJsonMap(lowerCaseKeyMap, v, mapping.WithCanonicalKeyFunc(toLowerCase)) } // LoadConfigFromJsonBytes loads config into v from content json bytes. @@ -129,7 +127,7 @@ func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo { for i := 0; i < tp.NumField(); i++ { field := tp.Field(i) name := field.Name - ccName := toCamelCase(name) + lowerCaseName := toLowerCase(name) ft := mapping.Deref(field.Type) // flatten anonymous fields @@ -140,7 +138,7 @@ func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo { info[k] = v } } else { - info[ccName] = fieldInfo{ + info[lowerCaseName] = fieldInfo{ name: name, kind: ft.Kind(), } @@ -158,7 +156,7 @@ func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo { fields = buildFieldsInfo(ft.Elem()) } - info[ccName] = fieldInfo{ + info[lowerCaseName] = fieldInfo{ name: name, kind: ft.Kind(), children: fields, @@ -168,58 +166,18 @@ func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo { return info } -func toCamelCase(s string) string { - var buf strings.Builder - buf.Grow(len(s)) - var capNext bool - boundary := true - for _, v := range s { - isCap := v >= 'A' && v <= 'Z' - isLow := v >= 'a' && v <= 'z' - if boundary && (isCap || isLow) { - if capNext { - if isLow { - v -= distanceBetweenUpperAndLower - } - } else { - if isCap { - v += distanceBetweenUpperAndLower - } - } - boundary = false - } - if isCap || isLow { - buf.WriteRune(v) - capNext = false - continue - } - - switch v { - // '.' is used for chained keys, e.g. "grand.parent.child" - case ' ', '.', '\t': - buf.WriteRune(v) - capNext = false - boundary = true - case '_': - capNext = true - boundary = true - default: - buf.WriteRune(v) - capNext = true - } - } - - return buf.String() +func toLowerCase(s string) string { + return strings.ToLower(s) } -func toCamelCaseInterface(v interface{}, info map[string]fieldInfo) interface{} { +func toLowerCaseInterface(v interface{}, info map[string]fieldInfo) interface{} { switch vv := v.(type) { case map[string]interface{}: - return toCamelCaseKeyMap(vv, info) + return toLowerCaseKeyMap(vv, info) case []interface{}: var arr []interface{} for _, vvv := range vv { - arr = append(arr, toCamelCaseInterface(vvv, info)) + arr = append(arr, toLowerCaseInterface(vvv, info)) } return arr default: @@ -227,19 +185,19 @@ func toCamelCaseInterface(v interface{}, info map[string]fieldInfo) interface{} } } -func toCamelCaseKeyMap(m map[string]interface{}, info map[string]fieldInfo) map[string]interface{} { +func toLowerCaseKeyMap(m map[string]interface{}, info map[string]fieldInfo) map[string]interface{} { res := make(map[string]interface{}) for k, v := range m { ti, ok := info[k] if ok { - res[k] = toCamelCaseInterface(v, ti.children) + res[k] = toLowerCaseInterface(v, ti.children) continue } - cck := toCamelCase(k) - if ti, ok = info[cck]; ok { - res[toCamelCase(k)] = toCamelCaseInterface(v, ti.children) + lk := toLowerCase(k) + if ti, ok = info[lk]; ok { + res[lk] = toLowerCaseInterface(v, ti.children) } else { res[k] = v } diff --git a/core/conf/config_test.go b/core/conf/config_test.go index 547b4745..82dc5545 100644 --- a/core/conf/config_test.go +++ b/core/conf/config_test.go @@ -237,23 +237,23 @@ func TestToCamelCase(t *testing.T) { }, { input: "hello_world", - expect: "helloWorld", + expect: "hello_world", }, { input: "Hello_world", - expect: "helloWorld", + expect: "hello_world", }, { input: "hello_World", - expect: "helloWorld", + expect: "hello_world", }, { input: "helloWorld", - expect: "helloWorld", + expect: "helloworld", }, { input: "HelloWorld", - expect: "helloWorld", + expect: "helloworld", }, { input: "hello World", @@ -269,34 +269,34 @@ func TestToCamelCase(t *testing.T) { }, { input: "Hello World foo_bar", - expect: "hello world fooBar", + expect: "hello world foo_bar", }, { input: "Hello World foo_Bar", - expect: "hello world fooBar", + expect: "hello world foo_bar", }, { input: "Hello World Foo_bar", - expect: "hello world fooBar", + expect: "hello world foo_bar", }, { input: "Hello World Foo_Bar", - expect: "hello world fooBar", + expect: "hello world foo_bar", }, { input: "Hello.World Foo_Bar", - expect: "hello.world fooBar", + expect: "hello.world foo_bar", }, { input: "你好 World Foo_Bar", - expect: "你好 world fooBar", + expect: "你好 world foo_bar", }, } for _, test := range tests { test := test t.Run(test.input, func(t *testing.T) { - assert.Equal(t, test.expect, toCamelCase(test.input)) + assert.Equal(t, test.expect, toLowerCase(test.input)) }) } } @@ -332,6 +332,22 @@ func TestLoadFromYamlBytes(t *testing.T) { assert.Equal(t, "foo", val.Layer1.Layer2.Layer3) } +func TestLoadFromYamlBytesTerm(t *testing.T) { + input := []byte(`layer1: + layer2: + tls_conf: foo`) + var val struct { + Layer1 struct { + Layer2 struct { + Layer3 string `json:"tls_conf"` + } + } + } + + assert.NoError(t, LoadFromYamlBytes(input, &val)) + assert.Equal(t, "foo", val.Layer1.Layer2.Layer3) +} + func TestLoadFromYamlBytesLayers(t *testing.T) { input := []byte(`layer1: layer2: