feat!: rework the public API to json/v2-style variadic options
Test / test (push) Successful in 1m49s

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-22 18:42:18 +02:00
parent 7ee155d1e9
commit 2e61ad0ba9
18 changed files with 398 additions and 393 deletions
+17 -21
View File
@@ -169,7 +169,7 @@ func TestDecoderDecodeContextHonoursCancellation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
var cfg map[string]any
err := NewDecoder().DecodeContext(ctx, []byte("a = 1\n"), &cfg)
err := UnmarshalContext(ctx, []byte("a = 1\n"), &cfg)
if !errors.Is(err, context.Canceled) {
t.Fatalf("DecodeContext returned %v, want context.Canceled", err)
}
@@ -194,7 +194,7 @@ count = 3
if out.Title != "x" || out.Count != 3 {
t.Errorf("out = %#v", out)
}
if err := NewDecoder().DecodeContext(context.Background(), in, &map[string]any{}); err != nil {
if err := Unmarshal(in, &map[string]any{}); err != nil {
t.Fatalf("Decoder.DecodeContext: %v", err)
}
}
@@ -695,8 +695,7 @@ func TestUnmarshalStrictEmbeddedMapStaysStrict(t *testing.T) {
RoundTripExtra
Name string `toml:"name"`
}
dec := NewDecoder().DisallowUnknownFields()
err := dec.Decode([]byte("name = \"n\"\nrogue = 1\n"), &Cfg{})
err := Unmarshal([]byte("name = \"n\"\nrogue = 1\n"), &Cfg{}, RejectUnknownFields(true))
if err == nil || !strings.Contains(err.Error(), "unknown field") {
t.Fatalf("expected unknown field error, got: %v", err)
}
@@ -980,10 +979,10 @@ func TestDecoderMaxDepth(t *testing.T) {
var cfg struct {
V any `toml:"v"`
}
if err := NewDecoder().MaxDepth(4).Decode(deep(4), &cfg); err != nil {
if err := Unmarshal(deep(4), &cfg, MaxNestingDepth(4)); err != nil {
t.Fatalf("at the limit: %v", err)
}
err := NewDecoder().MaxDepth(4).Decode(deep(5), &cfg)
err := Unmarshal(deep(5), &cfg, MaxNestingDepth(4))
if err == nil {
t.Fatal("expected a nesting error")
}
@@ -997,10 +996,10 @@ func TestDecoderMaxInputSize(t *testing.T) {
var cfg struct {
V string `toml:"v"`
}
if err := NewDecoder().MaxInputSize(len(doc)).Decode(doc, &cfg); err != nil {
if err := Unmarshal(doc, &cfg, MaxInputSize(len(doc))); err != nil {
t.Fatalf("at the limit: %v", err)
}
err := NewDecoder().MaxInputSize(len(doc)-1).Decode(doc, &cfg)
err := Unmarshal(doc, &cfg, MaxInputSize(len(doc)-1))
if err == nil {
t.Fatal("expected a size error")
}
@@ -1112,7 +1111,7 @@ frac = 2.5
`)
t.Run("the tree keeps the literal", func(t *testing.T) {
var tree map[string]any
if err := NewDecoder().UseNumber().Decode(data, &tree); err != nil {
if err := Unmarshal(data, &tree, NumbersAsLiterals(true)); err != nil {
t.Fatal(err)
}
for lit, key := range map[string]string{
@@ -1136,8 +1135,7 @@ frac = 2.5
Frac float64 `toml:"frac"`
Rate time.Duration
}
dec := NewDecoder().UseNumber()
if err := dec.Decode([]byte("hex = 0x1f\nplain = 42\nfrac = 2.5\nRate = 1_000\n"), &cfg); err != nil {
if err := Unmarshal([]byte("hex = 0x1f\nplain = 42\nfrac = 2.5\nRate = 1_000\n"), &cfg, NumbersAsLiterals(true)); err != nil {
t.Fatal(err)
}
if cfg.Hex != "0x1f" {
@@ -1156,14 +1154,14 @@ frac = 2.5
t.Run("invalid numbers are still parse errors", func(t *testing.T) {
for _, in := range []string{"a = 01\n", "a = 1__0\n", "a = 1x\n"} {
var tree map[string]any
if err := NewDecoder().UseNumber().Decode([]byte(in), &tree); err == nil {
if err := Unmarshal([]byte(in), &tree, NumbersAsLiterals(true)); err == nil {
t.Errorf("%q decoded without an error", in)
}
}
})
t.Run("without UseNumber the tree holds the evaluated kinds", func(t *testing.T) {
var tree map[string]any
if err := NewDecoder().Decode([]byte("hex = 0x1f\nfrac = 2.5\n"), &tree); err != nil {
if err := Unmarshal([]byte("hex = 0x1f\nfrac = 2.5\n"), &tree); err != nil {
t.Fatal(err)
}
if v, ok := tree["hex"].(int64); !ok || v != 31 {
@@ -1507,9 +1505,8 @@ func TestLocalTimeLocation(t *testing.T) {
Date LocalDate `toml:"date"`
Wall LocalDateTime `toml:"wall"`
}
dec := NewDecoder().LocalTimeLocation(zone)
in := []byte("when = 1979-05-27T07:32:00\ndate = 1979-05-27\nwall = 1979-05-27T07:32:00\n")
if err := dec.Decode(in, &cfg); err != nil {
if err := Unmarshal(in, &cfg, LocalTimeLocation(zone)); err != nil {
t.Fatal(err)
}
if got := cfg.When.Format("15:04:05 MST"); got != "07:32:00 CET" {
@@ -1523,8 +1520,7 @@ func TestLocalTimeLocation(t *testing.T) {
var cfg struct {
Wall LocalDateTime `toml:"wall"`
}
dec := NewDecoder().LocalTimeLocation(zone)
if err := dec.Decode([]byte("wall = 1979-05-27T07:32:00\n"), &cfg); err != nil {
if err := Unmarshal([]byte("wall = 1979-05-27T07:32:00\n"), &cfg, LocalTimeLocation(zone)); err != nil {
t.Fatal(err)
}
if cfg.Wall.Hour() != 7 {
@@ -1567,7 +1563,7 @@ func TestCancelInsideValue(t *testing.T) {
b.WriteString("]\n")
ctx := &errAfterN{Context: context.Background(), n: 2}
var tree map[string]any
err := NewDecoder().DecodeContext(ctx, []byte(b.String()), &tree)
err := UnmarshalContext(ctx, []byte(b.String()), &tree)
if err == nil {
t.Fatal("a cancelled context did not stop the parse inside the value")
}
@@ -1585,7 +1581,7 @@ func TestCancellationSynctest(t *testing.T) {
cancel()
start := time.Now()
var tree map[string]any
err := NewDecoder().DecodeContext(ctx, []byte("a = 1\n"), &tree)
err := UnmarshalContext(ctx, []byte("a = 1\n"), &tree)
if !errors.Is(err, context.Canceled) {
t.Errorf("err = %v, want context.Canceled", err)
}
@@ -1644,7 +1640,7 @@ func TestErrorMessagesGolden(t *testing.T) {
}
b.WriteString("\n")
var tree map[string]any
err := NewDecoder().MaxDepth(10).Decode([]byte(b.String()), &tree)
err := Unmarshal([]byte(b.String()), &tree, MaxNestingDepth(10))
return err
},
want: "interpres: line 1: nesting exceeds the limit of 10",
@@ -1663,7 +1659,7 @@ func TestErrorMessagesGolden(t *testing.T) {
var cfg struct {
Known int `toml:"known"`
}
return NewDecoder().DisallowUnknownFields().Decode([]byte("mystery = 1\n"), &cfg)
return Unmarshal([]byte("mystery = 1\n"), &cfg, RejectUnknownFields(true))
},
want: `interpres: unknown field "mystery" for struct { Known int "toml:\"known\"" }`,
},