fix(decode): allocate embedded pointer maps and settle case collisions
Assisted-by: GLM 5.3
This commit is contained in:
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user