fix(decode): allocate embedded pointer maps and settle case collisions

Assisted-by: GLM 5.3
This commit is contained in:
2026-09-22 21:15:00 +02:00
parent e677e34508
commit f7e427ae3f
2 changed files with 103 additions and 4 deletions
+21 -4
View File
@@ -7,6 +7,7 @@ import (
"context"
"encoding"
"fmt"
"maps"
"reflect"
"slices"
"strings"
@@ -337,7 +338,8 @@ func (d *decoder) assignStruct(tbl map[string]any, dst reflect.Value) error {
if len(schema.required) > 0 {
seen = make(map[string]bool, len(tbl))
}
for key, val := range tbl {
for _, key := range d.tableKeys(tbl) {
val := tbl[key]
// A key that is already lowercase, which document keys usually are,
// hits the map directly; only a miss pays for the case fold.
resolved := key
@@ -349,12 +351,15 @@ func (d *decoder) assignStruct(tbl map[string]any, dst reflect.Value) error {
if !ok {
if schema.embedMaps != nil {
// Leftover keys land in an untagged embedded map, the inverse
// of the encoder inlining that map's entries.
// of the encoder inlining that map's entries. The assign call
// rather than assignMap itself lets it allocate the embedded
// pointer the field may be, the way any other destination is
// reached.
mv, err := fieldByIndex(dst, schema.embedMaps[0])
if err != nil {
return newDecodeError(key, err)
}
if err := d.assignMap(map[string]any{key: val}, mv); err != nil {
if err := d.assign(map[string]any{key: val}, mv); err != nil {
return newDecodeError(key, err)
}
}
@@ -379,6 +384,18 @@ func (d *decoder) assignStruct(tbl map[string]any, dst reflect.Value) error {
return nil
}
// tableKeys returns the keys of tbl in the order the document wrote them
// when the node index knows it, and in sorted order otherwise, the order a
// hand-built tree or a node-free parse offers. The order settles which of
// two keys that differ only in case wins one field: the same key wins every
// run, instead of whichever a map iteration happened to hand out.
func (d *decoder) tableKeys(tbl map[string]any) []string {
if node := d.nodeOf(tbl); node != nil {
return node.Keys()
}
return slices.Sorted(maps.Keys(tbl))
}
func (d *decoder) assignMap(tbl map[string]any, dst reflect.Value) error {
if dst.Type().Key().Kind() != reflect.String {
return fmt.Errorf("interpres: map key must be a string, got %s", dst.Type().Key())
@@ -499,7 +516,7 @@ func (d *decoder) setLocalTimeValue(t time.Time, dst reflect.Value) error {
t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), d.loc)))
return nil
}
return fmt.Errorf("interpres: cannot assign local date-time to time.Time; set Decoder.LocalTimeLocation to choose the zone")
return fmt.Errorf("interpres: cannot assign local date-time to time.Time; set LocalTimeLocation to choose the zone")
}
return fmt.Errorf("interpres: cannot assign local date-time to %s", dst.Type())
}