diff --git a/core/conf/config.go b/core/conf/config.go index b64b9258..062e1b8f 100644 --- a/core/conf/config.go +++ b/core/conf/config.go @@ -22,7 +22,6 @@ var loaders = map[string]func([]byte, any) error{ type fieldInfo struct { name string - kind reflect.Kind children map[string]fieldInfo } @@ -140,7 +139,6 @@ func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo { } else { info[lowerCaseName] = fieldInfo{ name: name, - kind: ft.Kind(), } } continue @@ -156,10 +154,16 @@ func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo { fields = buildFieldsInfo(ft.Elem()) } - info[lowerCaseName] = fieldInfo{ - name: name, - kind: ft.Kind(), - children: fields, + if prev, ok := info[lowerCaseName]; ok { + // merge fields + for k, v := range fields { + prev.children[k] = v + } + } else { + info[lowerCaseName] = fieldInfo{ + name: name, + children: fields, + } } } diff --git a/core/conf/config_test.go b/core/conf/config_test.go index 7be4f540..3465c729 100644 --- a/core/conf/config_test.go +++ b/core/conf/config_test.go @@ -384,6 +384,78 @@ func TestLoadFromYamlBytesLayers(t *testing.T) { assert.Equal(t, "foo", val.Value) } +func TestLoadFromYamlItemOverlay(t *testing.T) { + type ( + Redis struct { + Host string + Port int + } + + RedisKey struct { + Redis + Key string + } + + Server struct { + Redis RedisKey + } + + TestConfig struct { + Server + Redis Redis + } + ) + + input := []byte(`Redis: + Host: localhost + Port: 6379 + Key: test +`) + + var c TestConfig + if assert.NoError(t, LoadFromYamlBytes(input, &c)) { + assert.Equal(t, "localhost", c.Redis.Host) + assert.Equal(t, 6379, c.Redis.Port) + assert.Equal(t, "test", c.Server.Redis.Key) + } +} + +func TestLoadFromYamlItemOverlayWithMap(t *testing.T) { + type ( + Redis struct { + Host string + Port int + } + + RedisKey struct { + Redis + Key string + } + + Server struct { + Redis RedisKey + } + + TestConfig struct { + Server + Redis map[string]interface{} + } + ) + + input := []byte(`Redis: + Host: localhost + Port: 6379 + Key: test +`) + + var c TestConfig + if assert.NoError(t, LoadFromYamlBytes(input, &c)) { + assert.Equal(t, "localhost", c.Server.Redis.Host) + assert.Equal(t, 6379, c.Server.Redis.Port) + assert.Equal(t, "test", c.Server.Redis.Key) + } +} + func TestUnmarshalJsonBytesMap(t *testing.T) { input := []byte(`{"foo":{"/mtproto.RPCTos": "bff.bff","bar":"baz"}}`) diff --git a/core/conf/readme.md b/core/conf/readme.md index cad39198..7bd58540 100644 --- a/core/conf/readme.md +++ b/core/conf/readme.md @@ -4,6 +4,7 @@ ```go type RestfulConf struct { + ServiceName string `json:",env=SERVICE_NAME"` // read from env automatically Host string `json:",default=0.0.0.0"` Port int LogMode string `json:",options=[file,console]"` @@ -21,20 +22,20 @@ type RestfulConf struct { ```yaml # most fields are optional or have default values -Port: 8080 -LogMode: console +port: 8080 +logMode: console # you can use env settings -MaxBytes: ${MAX_BYTES} +maxBytes: ${MAX_BYTES} ``` - toml example ```toml # most fields are optional or have default values -Port = 8_080 -LogMode = "console" +port = 8_080 +logMode = "console" # you can use env settings -MaxBytes = "${MAX_BYTES}" +maxBytes = "${MAX_BYTES}" ``` 3. Load the config from a file: