Skip to content

Commit

Permalink
Support struct with slice of primitives (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
asafalima committed Oct 13, 2022
1 parent 6eccaaa commit 877d9d3
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 7 deletions.
34 changes: 34 additions & 0 deletions aconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,40 @@ func TestFileConfigFlagDelim(t *testing.T) {
mustEqual(t, cfg, want)
}

func TestSliceOfStructsWithSliceOfPrimitives(t *testing.T) {
type TestService struct {
Name string
Strings []string
Integers []int
Booleans []bool
}

type TestConfig struct {
Services []TestService
}
var cfg TestConfig
loader := LoaderFor(&cfg, Config{
SkipDefaults: true,
SkipEnv: true,
SkipFlags: true,
Files: []string{"testdata/slice-struct-primitive-slice.json"},
})

failIfErr(t, loader.Load())

want := TestConfig{
Services: []TestService{
{
Name: "service1",
Strings: []string{"string1", "string2"},
Integers: []int{1, 2},
Booleans: []bool{true, false},
},
},
}
mustEqual(t, cfg, want)
}

func failIfOk(t testing.TB, err error) {
t.Helper()
if err == nil {
Expand Down
27 changes: 20 additions & 7 deletions reflection.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (l *Loader) setFieldData(field *fieldData, value interface{}) error {
vv := mii(val)

fd := l.newFieldData(reflect.StructField{}, slice.Index(i), nil)
if err := m2s(vv, fd.value); err != nil {
if err := l.m2s(vv, fd.value); err != nil {
return err
}
}
Expand Down Expand Up @@ -345,7 +345,7 @@ func (l *Loader) setMap(field *fieldData, value string) error {
return nil
}

func m2s(m map[string]interface{}, structValue reflect.Value) error {
func (l *Loader) m2s(m map[string]interface{}, structValue reflect.Value) error {
for name, value := range m {
name = strings.Title(name)
structFieldValue := structValue.FieldByName(name)
Expand All @@ -362,11 +362,20 @@ func m2s(m map[string]interface{}, structValue reflect.Value) error {
if structFieldValue.Kind() == reflect.Slice && val.Kind() == reflect.Slice {
vals := value.([]interface{})
slice := reflect.MakeSlice(structFieldValue.Type(), len(vals), len(vals))
for i := 0; i < len(vals); i++ {
a := mii(vals[i])
b := slice.Index(i)
if err := m2s(a, b); err != nil {
return err
if isPrimitive(structFieldValue.Type().Elem()) {
for i := 0; i < len(vals); i++ {
fd := l.newFieldData(reflect.StructField{}, slice.Index(i), nil)
if err := l.setFieldData(fd, vals[i]); err != nil {
return fmt.Errorf("incorrect slice item %q: %w", vals[i], err)
}
}
} else {
for i := 0; i < len(vals); i++ {
a := mii(vals[i])
b := slice.Index(i)
if err := l.m2s(a, b); err != nil {
return err
}
}
}
structFieldValue.Set(slice)
Expand Down Expand Up @@ -395,3 +404,7 @@ func mii(m interface{}) map[string]interface{} {
panic(fmt.Sprintf("%T %v", m, m))
}
}

func isPrimitive(v reflect.Type) bool {
return v.Kind() < reflect.Array || v.Kind() == reflect.String
}
10 changes: 10 additions & 0 deletions testdata/slice-struct-primitive-slice.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"services": [
{
"name": "service1",
"strings": ["string1", "string2"],
"integers": [1, 2],
"booleans": [true, false]
}
]
}

0 comments on commit 877d9d3

Please sign in to comment.