From ad142c62f9b6361f6a74bce213d6ccd4cace700e Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Tue, 16 May 2023 09:05:32 -0400 Subject: [PATCH] Decode: fix decode into unsettable structs Fixes #866 --- unmarshaler.go | 13 +++++++++ unmarshaler_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/unmarshaler.go b/unmarshaler.go index bab11210..bec3df5f 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -1092,6 +1092,19 @@ func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node d.errorContext.Field = path f := fieldByIndex(v, path) + + if !f.CanSet() { + // If the field is not settable, need to take a slower path and make a copy of + // the struct itself to a new location. + nvp := reflect.New(v.Type()) + nvp.Elem().Set(v) + v = nvp.Elem() + _, err := d.handleKeyValuePart(key, value, v) + if err != nil { + return reflect.Value{}, err + } + return nvp.Elem(), nil + } x, err := d.handleKeyValueInner(key, value, f) if err != nil { return reflect.Value{}, err diff --git a/unmarshaler_test.go b/unmarshaler_test.go index 1cc17d0e..6ee1202e 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -2613,6 +2613,70 @@ func TestIssue851(t *testing.T) { require.Equal(t, map[string]string{"a": "1", "b": "2"}, target.Params) } +func TestIssue866(t *testing.T) { + type Pipeline struct { + Mapping map[string]struct { + Req [][]string `toml:"req"` + Res [][]string `toml:"res"` + } `toml:"mapping"` + } + + type Pipelines struct { + PipelineMapping map[string]*Pipeline `toml:"pipelines"` + } + + var badToml = ` +[pipelines.register] +mapping.inst.req = [ + ["param1", "value1"], +] +mapping.inst.res = [ + ["param2", "value2"], +] +` + + pipelines := new(Pipelines) + if err := toml.NewDecoder(bytes.NewBufferString(badToml)).DisallowUnknownFields().Decode(pipelines); err != nil { + t.Fatal(err) + } + if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" { + t.Fatal("unmarshal failed with mismatch value") + } + + var goodTooToml = ` +[pipelines.register] +mapping.inst.req = [ + ["param1", "value1"], +] +` + + pipelines = new(Pipelines) + if err := toml.NewDecoder(bytes.NewBufferString(goodTooToml)).DisallowUnknownFields().Decode(pipelines); err != nil { + t.Fatal(err) + } + if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" { + t.Fatal("unmarshal failed with mismatch value") + } + + var goodToml = ` +[pipelines.register.mapping.inst] +req = [ + ["param1", "value1"], +] +res = [ + ["param2", "value2"], +] +` + + pipelines = new(Pipelines) + if err := toml.NewDecoder(bytes.NewBufferString(goodToml)).DisallowUnknownFields().Decode(pipelines); err != nil { + t.Fatal(err) + } + if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" { + t.Fatal("unmarshal failed with mismatch value") + } +} + func TestUnmarshalDecodeErrors(t *testing.T) { examples := []struct { desc string