feat: typed decode and encode errors with the key path

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-17 21:20:47 +02:00
parent 1e3198c8b6
commit 3f41266710
7 changed files with 219 additions and 18 deletions
+48
View File
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"math"
"slices"
"strings"
"testing"
)
@@ -606,3 +607,50 @@ func TestUnmarshalStrictEmbeddedMapStaysStrict(t *testing.T) {
t.Fatalf("expected unknown field error, got: %v", err)
}
}
func TestDecodeErrorCarriesPath(t *testing.T) {
type Item struct {
Name string `toml:"name"`
Weight uint8 `toml:"weight"`
}
type Cfg struct {
Tags []string `toml:"tags"`
Items []Item `toml:"items"`
}
var cfg Cfg
err := Unmarshal([]byte("[[items]]\nname = \"a\"\nweight = 300\n"), &cfg)
if err == nil {
t.Fatal("expected an overflow error")
}
de, ok := errors.AsType[*DecodeError](err)
if !ok {
t.Fatalf("expected a *DecodeError, got %T: %v", err, err)
}
want := []string{"items", "[0]", "weight"}
if !slices.Equal(de.Path, want) {
t.Fatalf("Path = %v, want %v", de.Path, want)
}
if de.Err == nil || !strings.Contains(de.Err.Error(), "overflows uint8") {
t.Fatalf("Err = %v", de.Err)
}
// The rendered message keeps its shape: segments joined with ": ".
wantMsg := "items: [0]: weight: interpres: integer 300 overflows uint8"
if err.Error() != wantMsg {
t.Fatalf("message = %q, want %q", err.Error(), wantMsg)
}
}
func TestDecodeErrorOnMapDestination(t *testing.T) {
var m map[string]uint8
err := Unmarshal([]byte("count = -1\n"), &m)
if err == nil {
t.Fatal("expected an error")
}
de, ok := errors.AsType[*DecodeError](err)
if !ok {
t.Fatalf("expected a *DecodeError, got %T: %v", err, err)
}
if !slices.Equal(de.Path, []string{"count"}) {
t.Fatalf("Path = %v", de.Path)
}
}