From 24feece1215559a844cc6fccede5c8e284adcdc5 Mon Sep 17 00:00:00 2001 From: magiccwarss Date: Sun, 8 Nov 2026 11:17:23 +0400 Subject: [PATCH] struct: preserve embedded pointer defaults when mapping --- struct.go | 2 +- struct_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/struct.go b/struct.go index 819a6e5..602a514 100644 --- a/struct.go +++ b/struct.go @@ -301,7 +301,7 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, isStruct := tpField.Type.Kind() == reflect.Struct isStructPtr := tpField.Type.Kind() == reflect.Pointer && tpField.Type.Elem().Kind() == reflect.Struct isAnonymousPtr := tpField.Type.Kind() == reflect.Pointer && tpField.Anonymous - if isAnonymousPtr { + if isAnonymousPtr && field.IsNil() { field.Set(reflect.New(tpField.Type.Elem())) } diff --git a/struct_test.go b/struct_test.go index ad6ad04..7904c12 100644 --- a/struct_test.go +++ b/struct_test.go @@ -910,3 +910,28 @@ Title = Consultant Engineer ) }) } + +func TestMapToWithMapperPreservesEmbeddedPointerDefaults(t *testing.T) { + type Options struct { + Name string + Port int + } + type Config struct { + *Options + } + for _, source := range []string{"", "[options]\nname = configured\n"} { + t.Run(source, func(t *testing.T) { + defaults := &Options{Name: "default", Port: 1234} + got := Config{Options: defaults} + err := MapToWithMapper(&got, TitleUnderscore, []byte(source)) + require.NoError(t, err) + require.Same(t, defaults, got.Options) + assert.Equal(t, 1234, got.Port) + if source == "" { + assert.Equal(t, "default", got.Name) + } else { + assert.Equal(t, "configured", got.Name) + } + }) + } +}