From bc9958322f3a39281ab085dd34597bed618b4964 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Thu, 21 Aug 2025 10:39:23 +0200 Subject: [PATCH] Add missing UnmarshalTOML call (#996) Fixes #994. --- unmarshaler.go | 11 +++++++++++ unmarshaler_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/unmarshaler.go b/unmarshaler.go index 189be52..eda6dd3 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -1154,6 +1154,17 @@ func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node case reflect.Struct: path, found := structFieldPath(v, string(key.Node().Data)) if !found { + // If no matching struct field is found but the target implements the + // unstable.Unmarshaler interface (and it is enabled), delegate the + // decoding of this value to the custom unmarshaler. + if d.unmarshalerInterface { + if v.CanAddr() && v.Addr().CanInterface() { + if outi, ok := v.Addr().Interface().(unstable.Unmarshaler); ok { + return reflect.Value{}, outi.UnmarshalTOML(value) + } + } + } + // Otherwise, keep previous behavior and skip until the next table. d.skipUntilTable = true break } diff --git a/unmarshaler_test.go b/unmarshaler_test.go index 6ee6636..fa38685 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -3998,3 +3998,45 @@ foo = "bar"`, }) } } + +type doc994 struct{} + +func (d *doc994) UnmarshalTOML(value *unstable.Node) error { + return errors.New("expected-error") +} + +func TestIssue994(t *testing.T) { + var _ unstable.Unmarshaler = (*doc994)(nil) + tomlBytes := []byte(`foo = "bar"`) + var d doc994 + err := toml.NewDecoder(bytes.NewReader(tomlBytes)). + EnableUnmarshalerInterface(). + Decode(&d) + + assert.Error(t, err) + + if err.Error() != "expected-error" { + t.Fatalf("expected error 'expected-error', got '%s'", err.Error()) + } +} + +type doc994ok struct { + S string +} + +func (d *doc994ok) UnmarshalTOML(value *unstable.Node) error { + d.S = string(value.Data) + " from unmarshaler" + return nil +} + +func TestIssue994_OK(t *testing.T) { + var _ unstable.Unmarshaler = (*doc994ok)(nil) + tomlBytes := []byte(`foo = "bar"`) + var d doc994ok + err := toml.NewDecoder(bytes.NewReader(tomlBytes)). + EnableUnmarshalerInterface(). + Decode(&d) + + assert.NoError(t, err) + assert.Equal(t, "bar from unmarshaler", d.S) +}