From f46a1b19370777ca5592e1453ed83c96b9f1b12a Mon Sep 17 00:00:00 2001 From: Dmitriy Aleksandrov Date: Wed, 26 Mar 2025 18:43:49 +0300 Subject: [PATCH] fix: write subconfig fields only if driver has value for this fields Signed-off-by: Dmitriy Aleksandrov --- parser.go | 33 ++++++++++++++++++++--------- parser_test.go | 56 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/parser.go b/parser.go index 7bb5b79..6419156 100644 --- a/parser.go +++ b/parser.go @@ -72,7 +72,7 @@ func getLoggerValue(field fmap.Field, val any) string { return valueLog } -func copyToSubConfig(conf, subConf any, subpath string) error { +func copyToSubConfig(conf, subConf any, subpath string, parsedPaths []string) error { confFields, err := fmap.GetFrom(conf) if err != nil { return err @@ -82,15 +82,24 @@ func copyToSubConfig(conf, subConf any, subpath string) error { return err } for _, path := range confFields.GetAllPaths() { - if path != subpath && strings.HasPrefix(path, subpath) { - field := confFields.MustFind(path) - subFieldPath := strings.Replace(path, subpath+".", "", -1) - subField, ok := subConfFields.Find(subFieldPath) - if !ok { - return fmt.Errorf("subconf field %s not found", path) + // exclude paths that is not parsed by tinyconf drivers + skip := true + for _, parsedPath := range parsedPaths { + if path == parsedPath { + skip = false + break } - subField.Set(subConf, field.Get(conf)) } + if skip || path == subpath || !strings.HasPrefix(path, subpath) { + continue + } + field := confFields.MustFind(path) + subFieldPath := strings.Replace(path, subpath+".", "", -1) + subField, ok := subConfFields.Find(subFieldPath) + if !ok { + return fmt.Errorf("subconf field %s not found", path) + } + subField.Set(subConf, field.Get(conf)) } return nil } @@ -99,7 +108,9 @@ func (c *Manager) Parse(conf any) (err error) { confParse := conf confTypeOf := reflect.TypeOf(conf) register, ok := c.registered[confTypeOf] + parsedPaths := make([]string, 0) if !ok { + RegisteredLoop: for registeredTypeOf, registeredConf := range c.registered { for _, path := range registeredConf.Storage.GetAllPaths() { field := registeredConf.Storage.MustFind(path) @@ -109,9 +120,9 @@ func (c *Manager) Parse(conf any) (err error) { register = registeredConf confParse = reflect.New(registeredTypeOf.Elem()).Interface() defer func() { - err = copyToSubConfig(confParse, conf, field.GetStructPath()) + err = copyToSubConfig(confParse, conf, field.GetStructPath(), parsedPaths) }() - break + break RegisteredLoop } } } @@ -144,6 +155,8 @@ func (c *Manager) Parse(conf any) (err error) { if currentValue != driverValue.Value { log.Debug("override", LogField("value", getLoggerValue(field, driverValue.Value))) field.Set(confParse, driverValue.Value) + // only for sub configs + parsedPaths = append(parsedPaths, path) } } } diff --git a/parser_test.go b/parser_test.go index 778b8de..accc606 100644 --- a/parser_test.go +++ b/parser_test.go @@ -329,21 +329,57 @@ func (tl *testLogger) With(_ ...Field) Logger { func TestManager_ParseSubConfig(t *testing.T) { type FieldConfig struct { - Test string + Test string + Test2 string } type Config struct { Field FieldConfig } - c := &Manager{ - drivers: []Driver{&parseMockDriver{name: "d3", value: "test"}}, - registered: make(map[reflect.Type]*Registered), - log: &testLogger{}, + testCases := []struct { + Name string + Drivers []Driver + expectedFieldValues any + config *FieldConfig + }{ + { + Name: "Simple value", + Drivers: []Driver{ + &parseMockDriver{ + value: "test", + }, + }, + expectedFieldValues: "test", + config: &FieldConfig{}, + }, + { + Name: "Driver no value, no override existing fields values", + Drivers: []Driver{ + &parseMockDriver{ + value: "", + err: ErrValueNotFound, + }, + }, + expectedFieldValues: "test123", + config: &FieldConfig{ + Test: "test123", + Test2: "test123", + }, + }, + } + for _, testCase := range testCases { + t.Run(testCase.Name, func(t *testing.T) { + c := &Manager{ + drivers: testCase.Drivers, + registered: make(map[reflect.Type]*Registered), + log: &testLogger{}, + } + conf := &Config{Field: FieldConfig{}} + assert.NoError(t, c.Register(conf)) + assert.NoError(t, c.Parse(testCase.config)) + assert.Equal(t, testCase.expectedFieldValues, testCase.config.Test) + assert.Equal(t, testCase.expectedFieldValues, testCase.config.Test2) + }) } - conf := &Config{} - subConf := &FieldConfig{} - assert.NoError(t, c.Register(conf)) - - assert.NoError(t, c.Parse(subConf)) } func TestManager_Parse(t *testing.T) { t.Parallel()