From 7111e04773005c1271da579d77dceccd4a5f3651 Mon Sep 17 00:00:00 2001 From: XIE Long Date: Wed, 9 Oct 2024 16:06:32 +0800 Subject: [PATCH] Fix panic while processing nested map and slice of structures #152 (#154) --- aconfigtoml/go.mod | 6 ++--- aconfigtoml/go.sum | 8 +++--- aconfigtoml/testdata/config.toml | 9 +++++++ aconfigtoml/toml_test.go | 12 +++++++-- reflection.go | 43 ++++++++++++++++++++++++-------- 5 files changed, 59 insertions(+), 19 deletions(-) diff --git a/aconfigtoml/go.mod b/aconfigtoml/go.mod index 877f82f..4572f7b 100644 --- a/aconfigtoml/go.mod +++ b/aconfigtoml/go.mod @@ -1,8 +1,8 @@ module github.com/cristalhq/aconfig/aconfigtoml -go 1.16 +go 1.18 require ( - github.com/BurntSushi/toml v1.1.0 - github.com/cristalhq/aconfig v0.17.0 + github.com/BurntSushi/toml v1.4.0 + github.com/cristalhq/aconfig v0.18.5 ) diff --git a/aconfigtoml/go.sum b/aconfigtoml/go.sum index 8e5bdae..6b6ebd9 100644 --- a/aconfigtoml/go.sum +++ b/aconfigtoml/go.sum @@ -1,4 +1,4 @@ -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/cristalhq/aconfig v0.17.0 h1:VYqg0YOM5yUEx0KH/VwUYF2e/PNI7dcUE66y+xEx73s= -github.com/cristalhq/aconfig v0.17.0/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/cristalhq/aconfig v0.18.5 h1:QqXH/Gy2c4QUQJTV2BN8UAuL/rqZ3IwhvxeC8OgzquA= +github.com/cristalhq/aconfig v0.18.5/go.mod h1:NXaRp+1e6bkO4dJn+wZ71xyaihMDYPtCSvEhMTm/H3E= diff --git a/aconfigtoml/testdata/config.toml b/aconfigtoml/testdata/config.toml index c67786b..a562641 100644 --- a/aconfigtoml/testdata/config.toml +++ b/aconfigtoml/testdata/config.toml @@ -1,2 +1,11 @@ foo = "value1" bar = "value2" + +[outter] + [outter.inner] + [[outter.inner.t1]] + a = "a" + b = "b" + [[outter.inner.t1]] + a = "c" + b = "d" \ No newline at end of file diff --git a/aconfigtoml/toml_test.go b/aconfigtoml/toml_test.go index 30164a1..94a35c6 100644 --- a/aconfigtoml/toml_test.go +++ b/aconfigtoml/toml_test.go @@ -15,8 +15,12 @@ var configEmbed embed.FS func TestTOMLEmbed(t *testing.T) { var cfg struct { - Foo string - Bar string + Foo string + Bar string + Outter map[string]map[string][]struct { + A string + B string + } } loader := aconfig.LoaderFor(&cfg, aconfig.Config{ SkipDefaults: true, @@ -40,6 +44,10 @@ func TestTOMLEmbed(t *testing.T) { if cfg.Bar != "value2" { t.Fatalf("have: %v", cfg.Bar) } + + if cfg.Outter["inner"]["t1"][0].A != "a" { + t.Fatalf("have: %v", cfg.Outter["inner"]["t1"][0].A) + } } func TestTOML(t *testing.T) { diff --git a/reflection.go b/reflection.go index 28494f3..cfcf9b3 100644 --- a/reflection.go +++ b/reflection.go @@ -159,9 +159,11 @@ func (l *Loader) setFieldData(field *fieldData, value interface{}) error { return nil } - pv := field.value.Addr().Interface() - if v, ok := pv.(encoding.TextUnmarshaler); ok { - return v.UnmarshalText([]byte(fmt.Sprint(value))) + if field.value.CanAddr() { + pv := field.value.Addr().Interface() + if v, ok := pv.(encoding.TextUnmarshaler); ok { + return v.UnmarshalText([]byte(fmt.Sprint(value))) + } } switch kind := field.value.Type().Kind(); kind { @@ -186,26 +188,44 @@ func (l *Loader) setFieldData(field *fieldData, value interface{}) error { case reflect.Interface: return l.setInterface(field, value) + case reflect.Struct: + fd := l.newFieldData(reflect.StructField{}, field.value, nil) + return l.m2s(mii(value), fd.value) + case reflect.Slice: if field.field.Type.Elem().Kind() == reflect.Struct { if value == nil { return nil } - v, ok := value.([]interface{}) + + if v, ok := value.([]interface{}); ok { + slice := reflect.MakeSlice(field.field.Type, len(v), len(v)) + for i, val := range v { + vv := mii(val) + + fd := l.newFieldData(reflect.StructField{}, slice.Index(i), nil) + if err := l.m2s(vv, fd.value); err != nil { + return err + } + } + field.value.Set(slice) + return nil + } + + v, ok := value.([]map[string]interface{}) if !ok { panic(fmt.Errorf("%T %v", value, value)) } slice := reflect.MakeSlice(field.field.Type, len(v), len(v)) for i, val := range v { - vv := mii(val) - fd := l.newFieldData(reflect.StructField{}, slice.Index(i), nil) - if err := l.m2s(vv, fd.value); err != nil { + if err := l.m2s(val, fd.value); err != nil { return err } } field.value.Set(slice) + return nil } return l.setSlice(field, sliceToString(value)) @@ -223,10 +243,12 @@ func (l *Loader) setFieldData(field *fieldData, value interface{}) error { return fmt.Errorf("incorrect map key %q: %w", key, err) } - fdv := l.newSimpleFieldData(reflect.New(field.field.Type.Elem()).Elem()) + fdv := l.newFieldData(reflect.StructField{}, reflect.New(field.value.Type().Elem()).Elem(), field) + fdv.field.Type = field.value.Type().Elem() if err := l.setFieldData(fdv, val); err != nil { return fmt.Errorf("incorrect map value %q: %w", val, err) } + mapp.SetMapIndex(fdk.value, fdv.value) } field.value.Set(mapp) @@ -309,6 +331,7 @@ func (l *Loader) setSlice(field *fieldData, value string) error { val = strings.TrimSpace(val) fd := l.newFieldData(reflect.StructField{}, slice.Index(i), nil) + fd.field.Type = field.field.Type.Elem() if err := l.setFieldData(fd, val); err != nil { return fmt.Errorf("incorrect slice item %q: %w", val, err) } @@ -334,8 +357,8 @@ func (l *Loader) setMap(field *fieldData, value string) error { return fmt.Errorf("incorrect map key %q: %w", key, err) } - fdv := l.newSimpleFieldData(reflect.New(field.field.Type.Elem()).Elem()) - fdv.field.Type = field.field.Type.Elem() + fdv := l.newFieldData(reflect.StructField{}, reflect.New(field.value.Type().Elem()).Elem(), field) + fdv.field.Type = field.value.Type().Elem() if err := l.setFieldData(fdv, val); err != nil { return fmt.Errorf("incorrect map value %q: %w", val, err) }