Skip to content

Commit

Permalink
Wrap UnmarshalTOML()/UnmarshalText return values in ParseError
Browse files Browse the repository at this point in the history
This adds position information.

Fixes #398
  • Loading branch information
arp242 committed Oct 1, 2023
1 parent 663cca6 commit b6fe702
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 2 deletions.
8 changes: 6 additions & 2 deletions decode.go
Expand Up @@ -218,7 +218,11 @@ func (md *MetaData) unify(data any, rv reflect.Value) error {

rvi := rv.Interface()
if v, ok := rvi.(Unmarshaler); ok {
return v.UnmarshalTOML(data)
err := v.UnmarshalTOML(data)
if err != nil {
return md.parseErr(err)
}
return nil
}
if v, ok := rvi.(encoding.TextUnmarshaler); ok {
return md.unifyText(data, v)
Expand Down Expand Up @@ -533,7 +537,7 @@ func (md *MetaData) unifyText(data any, v encoding.TextUnmarshaler) error {
return md.badtype("primitive (string-like)", data)
}
if err := v.UnmarshalText([]byte(s)); err != nil {
return err
return md.parseErr(err)
}
return nil
}
Expand Down
85 changes: 85 additions & 0 deletions error_test.go
Expand Up @@ -266,3 +266,88 @@ func TestParseError(t *testing.T) {
})
}
}

type Enum1 uint8

func (n *Enum1) UnmarshalText(text []byte) error {
switch t := strings.TrimSpace(string(text)); t {
case "ok":
*n = 1
default:
return fmt.Errorf("invalid value: %q", t)
}
return nil
}

// Make sure custom types are wrapped in ParseError with correct location.
func TestUnmarshalTypeError(t *testing.T) {
var c struct {
K1 string `toml:"k1"`
K2 Enum1 `toml:"k2"`
K3 Enum1 `toml:"k3"`
}
_, err := toml.Decode("k1 = 'asd'\nk2 = 'ok'\nk3 = 'invalid'\nk4 = 'ok'", &c)
if err == nil {
t.Fatal("error is nil")
}
var pErr toml.ParseError
if !errors.As(err, &pErr) {
t.Fatalf("not a ParseError: %#v", err)
}

want := `toml: error: invalid value: "invalid"
At line 3, column 6-13:
1 | k1 = 'asd'
2 | k2 = 'ok'
3 | k3 = 'invalid'
^^^^^^^
`

if have := pErr.ErrorWithUsage(); have != want {
t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
}
}

type Enum2 uint8

func (n *Enum2) UnmarshalTOML(text any) error {
switch t := strings.TrimSpace(text.(string)); t {
case "ok":
*n = 1
default:
return fmt.Errorf("invalid value: %q", t)
}
return nil
}

func TestMarhsalError(t *testing.T) {
var c struct {
K1 string `toml:"k1"`
K2 Enum2 `toml:"k2"`
K3 Enum2 `toml:"k3"`
}
_, err := toml.Decode("k1 = 'asd'\nk2 = 'ok'\nk3 = 'invalid'\nk4 = 'ok'", &c)
if err == nil {
t.Fatal("error is nil")
}
var pErr toml.ParseError
if !errors.As(err, &pErr) {
t.Fatalf("not a ParseError: %#v", err)
}

want := `toml: error: invalid value: "invalid"
At line 3, column 6-13:
1 | k1 = 'asd'
2 | k2 = 'ok'
3 | k3 = 'invalid'
^^^^^^^
`

if have := pErr.ErrorWithUsage(); have != want {
t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
}
}

0 comments on commit b6fe702

Please sign in to comment.