fix: encode mixed arrays with inline table elements
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math"
|
||||
"reflect"
|
||||
"slices"
|
||||
@@ -313,7 +314,17 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, ctx string) error
|
||||
return doc.appendScalar(name, []any{}, ctx)
|
||||
}
|
||||
|
||||
if isTableElementValue(v.Index(0)) {
|
||||
// An array keeps the [[header]] form only when every element is a table.
|
||||
// TOML lets one array mix tables with scalars, and that mix renders as a
|
||||
// value array with the table elements written inline.
|
||||
allTables := true
|
||||
for i := range n {
|
||||
if !isTableElementValue(v.Index(i)) {
|
||||
allTables = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allTables {
|
||||
subs := make([]*tomlDoc, n)
|
||||
for i := range n {
|
||||
if i%ctxCheckInterval == 0 {
|
||||
@@ -347,7 +358,8 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, ctx string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// Regular array of scalars.
|
||||
// Value array. Table elements normalise to map[string]any and the emitter
|
||||
// writes them as inline tables.
|
||||
items := make([]any, n)
|
||||
for i := range n {
|
||||
if i%ctxCheckInterval == 0 {
|
||||
@@ -382,11 +394,22 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, ctx string) error
|
||||
// nested-array representations the emitter understands. Slices and arrays are
|
||||
// recursively normalised so that nested arrays (e.g. [][]int) work.
|
||||
func normaliseValue(v reflect.Value) (any, error) {
|
||||
// Map and slice elements arrive wrapped in interface{}; look through them.
|
||||
for v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() == reflect.Interface {
|
||||
return nil, fmt.Errorf("cannot encode nil value")
|
||||
}
|
||||
if v.CanInterface() {
|
||||
if m, ok := v.Interface().(Marshaler); ok {
|
||||
return m.MarshalTOML()
|
||||
}
|
||||
}
|
||||
// The datetime structs are TOML scalars; the emitter renders each of them.
|
||||
if t := v.Type(); t == timeGoType || isLocalDateType(t) {
|
||||
return v.Interface(), nil
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
return v.String(), nil
|
||||
@@ -402,6 +425,21 @@ func normaliseValue(v reflect.Value) (any, error) {
|
||||
return int64(u), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float(), nil
|
||||
case reflect.Map:
|
||||
// A table nested in a value array has no header form, so it renders
|
||||
// inline; the keys normalise to strings for the emitter.
|
||||
if v.Type().Key().Kind() != reflect.String {
|
||||
return nil, fmt.Errorf("map key must be string, got %s", v.Type().Key())
|
||||
}
|
||||
out := make(map[string]any, v.Len())
|
||||
for _, k := range v.MapKeys() {
|
||||
val, err := normaliseValue(v.MapIndex(k))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[%s]: %w", k.String(), err)
|
||||
}
|
||||
out[k.String()] = val
|
||||
}
|
||||
return out, nil
|
||||
case reflect.Slice, reflect.Array:
|
||||
items := make([]any, v.Len())
|
||||
for i := range v.Len() {
|
||||
@@ -683,6 +721,8 @@ func (e *encoder) writeValue(val any) error {
|
||||
}
|
||||
e.buf.WriteByte(']')
|
||||
return nil
|
||||
case map[string]any:
|
||||
return e.writeInlineTable(v)
|
||||
case nil:
|
||||
return fmt.Errorf("interpres: cannot encode nil value")
|
||||
default:
|
||||
@@ -690,6 +730,26 @@ func (e *encoder) writeValue(val any) error {
|
||||
}
|
||||
}
|
||||
|
||||
// writeInlineTable renders m as a TOML inline table with sorted keys, the
|
||||
// order buildMapDoc uses for header tables. It backs the table elements of a
|
||||
// value array, where the [[header]] form is not available.
|
||||
func (e *encoder) writeInlineTable(m map[string]any) error {
|
||||
keys := slices.Sorted(maps.Keys(m))
|
||||
e.buf.WriteByte('{')
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
e.buf.WriteString(", ")
|
||||
}
|
||||
e.writeKey(k)
|
||||
e.buf.WriteString(" = ")
|
||||
if err := e.writeValue(m[k]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
e.buf.WriteByte('}')
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *encoder) writeStringVal(s string) error {
|
||||
if e.opts.literalMultilineAt > 0 && strings.ContainsRune(s, '\n') && len(s) >= e.opts.literalMultilineAt {
|
||||
return writeLiteralMultilineString(&e.buf, s)
|
||||
|
||||
Reference in New Issue
Block a user