Skip to content

Commit

Permalink
struct: correctly mapping non unique sections to slice field (#242)
Browse files Browse the repository at this point in the history
* bugfix: use the value from the correct section when using non unique sections in some special cases

* refactor wording and small fix
  • Loading branch information
aligator committed May 28, 2020
1 parent ad8a106 commit 1fc6efb
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 6 deletions.
17 changes: 11 additions & 6 deletions struct.go
Expand Up @@ -278,7 +278,9 @@ func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bo
return rawName, omitEmpty, allowShadow, allowNonUnique
}

func (s *Section) mapToField(val reflect.Value, isStrict bool) error {
// mapToField maps the given value to the matching field of the given section.
// The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int) error {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
Expand Down Expand Up @@ -307,13 +309,16 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool) error {
}

if isAnonymous || isStruct || isStructPtr {
if sec, err := s.f.GetSection(fieldName); err == nil {
if secs, err := s.f.SectionsByName(fieldName); err == nil {
if len(secs) <= sectionIndex {
return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName)
}
// Only set the field to non-nil struct value if we have a section for it.
// Otherwise, we end up with a non-nil struct ptr even though there is no data.
if isStructPtr && field.IsNil() {
field.Set(reflect.New(tpField.Type.Elem()))
}
if err = sec.mapToField(field, isStrict); err != nil {
if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex); err != nil {
return fmt.Errorf("map to field %q: %v", fieldName, err)
}
continue
Expand Down Expand Up @@ -350,9 +355,9 @@ func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (
}

typ := val.Type().Elem()
for _, sec := range secs {
for i, sec := range secs {
elem := reflect.New(typ)
if err = sec.mapToField(elem, isStrict); err != nil {
if err = sec.mapToField(elem, isStrict, i); err != nil {
return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err)
}

Expand Down Expand Up @@ -382,7 +387,7 @@ func (s *Section) mapTo(v interface{}, isStrict bool) error {
return nil
}

return s.mapToField(val, isStrict)
return s.mapToField(val, isStrict, 0)
}

// MapTo maps section to given struct.
Expand Down
44 changes: 44 additions & 0 deletions struct_test.go
Expand Up @@ -414,6 +414,50 @@ func Test_MapToStructNonUniqueSections(t *testing.T) {
So(newPeerSlice[1].AllowedIPs[0], ShouldEqual, "10.2.0.3/32")
So(newPeerSlice[1].AllowedIPs[1], ShouldEqual, "fd00:2::3/128")
})

Convey("Map non unique sections with subsections to struct", func() {
iniFile, err := ini.LoadSources(ini.LoadOptions{AllowNonUniqueSections: true}, strings.NewReader(`
[Section]
FieldInSubSection = 1
FieldInSubSection2 = 2
FieldInSection = 3
[Section]
FieldInSubSection = 4
FieldInSubSection2 = 5
FieldInSection = 6
`))
So(err, ShouldBeNil)

type SubSection struct {
FieldInSubSection string `ini:"FieldInSubSection"`
}
type SubSection2 struct {
FieldInSubSection2 string `ini:"FieldInSubSection2"`
}

type Section struct {
SubSection `ini:"Section"`
SubSection2 `ini:"Section"`
FieldInSection string `ini:"FieldInSection"`
}

type File struct {
Sections []Section `ini:"Section,,,nonunique"`
}

f := new(File)
err = iniFile.MapTo(f)
So(err, ShouldBeNil)

So(f.Sections[0].FieldInSubSection, ShouldEqual, "1")
So(f.Sections[0].FieldInSubSection2, ShouldEqual, "2")
So(f.Sections[0].FieldInSection, ShouldEqual, "3")

So(f.Sections[1].FieldInSubSection, ShouldEqual, "4")
So(f.Sections[1].FieldInSubSection2, ShouldEqual, "5")
So(f.Sections[1].FieldInSection, ShouldEqual, "6")
})
})
}

Expand Down

0 comments on commit 1fc6efb

Please sign in to comment.