From 1551efb988fb3d2d3d2cdb0a08fe682c1f965045 Mon Sep 17 00:00:00 2001 From: Team Humaki Date: Thu, 17 Sep 2026 01:14:47 -0700 Subject: [PATCH] struct: map slices of struct pointers --- struct.go | 22 +++++++++++++++++----- struct_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/struct.go b/struct.go index 819a6e5..b9505e8 100644 --- a/struct.go +++ b/struct.go @@ -358,21 +358,33 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, } // mapToSlice maps all sections with the same name and returns the new value. -// The type of the Value must be a slice. +// The type of the Value must be a slice of structs or pointers to structs. func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (reflect.Value, error) { secs, err := s.f.SectionsByName(secName) if err != nil { return reflect.Value{}, err } - typ := val.Type().Elem() + elemType := val.Type().Elem() + isPtr := elemType.Kind() == reflect.Pointer + structType := elemType + if isPtr { + if elemType.Elem().Kind() != reflect.Struct { + return reflect.Value{}, fmt.Errorf("slice of pointers to non-struct type %s", elemType) + } + structType = elemType.Elem() + } + for i, sec := range secs { - elem := reflect.New(typ) + elem := reflect.New(structType) if err = sec.mapToField(elem, isStrict, i, sec.name); err != nil { return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err) } - - val = reflect.Append(val, elem.Elem()) + if isPtr { + val = reflect.Append(val, elem) + } else { + val = reflect.Append(val, elem.Elem()) + } } return val, nil } diff --git a/struct_test.go b/struct_test.go index ad6ad04..bdb1a97 100644 --- a/struct_test.go +++ b/struct_test.go @@ -436,6 +436,38 @@ func Test_MapToStructNonUniqueSections(t *testing.T) { assert.Equal(t, "fd00:2::3/128", newPeerSlice[1].AllowedIPs[1]) }) + t.Run("map non unique section to slice of struct pointers", func(t *testing.T) { + type Config struct { + Peers []*testPeer `ini:"Peer,nonunique"` + } + + f, err := LoadSources(LoadOptions{AllowNonUniqueSections: true}, []byte(confNonUniqueSectionDataStruct)) + require.NoError(t, err) + + cfg := new(Config) + require.NoError(t, f.MapTo(cfg)) + require.Len(t, cfg.Peers, 2) + require.NotNil(t, cfg.Peers[0]) + require.NotNil(t, cfg.Peers[1]) + + assert.Equal(t, "pubClientKey", cfg.Peers[0].PublicKey) + assert.Equal(t, "psKey", cfg.Peers[0].PresharedKey) + assert.Equal(t, "10.2.0.2/32", cfg.Peers[0].AllowedIPs[0]) + assert.Equal(t, "fd00:2::2/128", cfg.Peers[0].AllowedIPs[1]) + + assert.Equal(t, "pubClientKey2", cfg.Peers[1].PublicKey) + assert.Equal(t, "psKey2", cfg.Peers[1].PresharedKey) + assert.Equal(t, "10.2.0.3/32", cfg.Peers[1].AllowedIPs[0]) + assert.Equal(t, "fd00:2::3/128", cfg.Peers[1].AllowedIPs[1]) + + ptrSlice := make([]*testPeer, 0) + require.NoError(t, f.Section("Peer").MapTo(&ptrSlice)) + require.Len(t, ptrSlice, 2) + require.NotNil(t, ptrSlice[0]) + assert.Equal(t, "pubClientKey", ptrSlice[0].PublicKey) + assert.Equal(t, "pubClientKey2", ptrSlice[1].PublicKey) + }) + t.Run("map non unique sections with subsections to struct", func(t *testing.T) { iniFile, err := LoadSources(LoadOptions{AllowNonUniqueSections: true}, strings.NewReader(` [Section]