refactor: unify the error paths in one Path type
Test / test (push) Canceled after 1m31s

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-09-22 00:34:48 +02:00
parent a8a2fcf8c3
commit b02471c09a
6 changed files with 119 additions and 69 deletions
+25 -31
View File
@@ -249,7 +249,7 @@ func buildOrderedDoc(om *OrderedMap, doc *tomlDoc, path encPath) error {
case OrderedMap:
sub := &tomlDoc{ctx: doc.ctx, opts: doc.opts, depth: doc.depth + 1}
if atDepthLimit(sub.depth) {
return &EncodeError{Path: path.key(key).String(), Err: errDepthLimit()}
return &EncodeError{Path: path.key(key).segments(), Err: errDepthLimit()}
}
if err := buildOrderedDoc(&x, sub, path.key(key)); err != nil {
return err
@@ -262,7 +262,7 @@ func buildOrderedDoc(om *OrderedMap, doc *tomlDoc, path encPath) error {
}
sub := &tomlDoc{ctx: doc.ctx, opts: doc.opts, depth: doc.depth + 1}
if atDepthLimit(sub.depth) {
return &EncodeError{Path: path.key(key).String(), Err: errDepthLimit()}
return &EncodeError{Path: path.key(key).segments(), Err: errDepthLimit()}
}
if err := buildOrderedDoc(x, sub, path.key(key)); err != nil {
return err
@@ -384,10 +384,14 @@ func (p *encPath) elem(i int) encPath {
return encPath{parent: p, index: i}
}
// String renders the path root first: keys join with dots and an array
// element carries its bracketed index, so the third port under server reads
// "server.ports[2]".
// String renders the path root first, in the notation Path.String uses.
func (p encPath) String() string {
return Path(p.segments()).String()
}
// segments returns the path's segments, root first: a key contributes its
// name, an array element its bracketed index.
func (p encPath) segments() []string {
var parts []string
for s := &p; s != nil; s = s.parent {
switch {
@@ -399,18 +403,8 @@ func (p encPath) String() string {
parts = append(parts, s.name)
}
}
var b strings.Builder
for _, part := range slices.Backward(parts) {
if strings.HasPrefix(part, "[") {
b.WriteString(part)
continue
}
if b.Len() > 0 {
b.WriteByte('.')
}
b.WriteString(part)
}
return b.String()
slices.Reverse(parts)
return parts
}
// --- reflection walk: struct ---------------------------------------------
@@ -637,10 +631,10 @@ func addField(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInl
if m, ok := marshalerOf(v); ok {
mv, err := m.MarshalTOML()
if err != nil {
return &EncodeError{Path: path.key(name).String(), Err: err}
return &EncodeError{Path: path.key(name).segments(), Err: err}
}
if mv == nil {
return &EncodeError{Path: path.key(name).String(), Err: errNilMarshalTOML}
return &EncodeError{Path: path.key(name).segments(), Err: errNilMarshalTOML}
}
v = reflect.ValueOf(mv)
}
@@ -648,7 +642,7 @@ func addField(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInl
// a scalar kind or a struct.
s, isText, err := textValue(v)
if err != nil {
return &EncodeError{Path: path.key(name).String(), Err: err}
return &EncodeError{Path: path.key(name).segments(), Err: err}
}
if isText {
doc.addScalar(name, s)
@@ -668,7 +662,7 @@ func addField(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInl
om := v.Interface().(OrderedMap)
sub := &tomlDoc{ctx: doc.ctx, opts: doc.opts, depth: doc.depth + 1}
if atDepthLimit(sub.depth) {
return &EncodeError{Path: path.key(name).String(), Err: errDepthLimit()}
return &EncodeError{Path: path.key(name).segments(), Err: errDepthLimit()}
}
if err := buildOrderedDoc(&om, sub, path.key(name)); err != nil {
return err
@@ -700,7 +694,7 @@ func addField(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInl
func addSubTable(doc *tomlDoc, name string, v reflect.Value, path encPath, forceInline bool) error {
sub := &tomlDoc{ctx: doc.ctx, opts: doc.opts, depth: doc.depth + 1}
if atDepthLimit(sub.depth) {
return &EncodeError{Path: path.key(name).String(), Err: errDepthLimit()}
return &EncodeError{Path: path.key(name).segments(), Err: errDepthLimit()}
}
switch v.Kind() {
case reflect.Struct:
@@ -775,7 +769,7 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath, for
// A `,inline` tag on an array of tables asks for a form that would change
// the value's Go type on re-parse, so the error is the honest answer.
if forceInline && allTables {
return &EncodeError{Path: path.key(name).String(), Err: errors.New("an array of tables has no inline form")}
return &EncodeError{Path: path.key(name).segments(), Err: errors.New("an array of tables has no inline form")}
}
if allTables {
subs := make([]*tomlDoc, n)
@@ -787,7 +781,7 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath, for
}
sub := &tomlDoc{ctx: doc.ctx, opts: doc.opts, depth: doc.depth + 1}
if atDepthLimit(sub.depth) {
return &EncodeError{Path: apath.elem(i).String(), Err: errDepthLimit()}
return &EncodeError{Path: apath.elem(i).segments(), Err: errDepthLimit()}
}
switch {
case ev.Type() == orderedMapType:
@@ -797,7 +791,7 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath, for
}
case ev.Kind() == reflect.Struct:
if isScalarStruct(ev.Type()) {
return &EncodeError{Path: apath.elem(i).String(), Err: errors.New("heterogeneous array contains scalar")}
return &EncodeError{Path: apath.elem(i).segments(), Err: errors.New("heterogeneous array contains scalar")}
}
if err := buildStructDoc(ev, sub, apath.elem(i)); err != nil {
return err
@@ -807,7 +801,7 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath, for
return err
}
default:
return &EncodeError{Path: apath.elem(i).String(), Err: errors.New("heterogeneous array, expected table")}
return &EncodeError{Path: apath.elem(i).segments(), Err: errors.New("heterogeneous array, expected table")}
}
subs[i] = sub
}
@@ -826,7 +820,7 @@ func addArrayValue(doc *tomlDoc, name string, v reflect.Value, path encPath, for
}
val, err := normaliseValue(ev)
if err != nil {
return &EncodeError{Path: apath.elem(i).String(), Err: err}
return &EncodeError{Path: apath.elem(i).segments(), Err: err}
}
items[i] = val
}
@@ -867,7 +861,7 @@ func marshalerOf(v reflect.Value) (Marshaler, bool) {
func resolveElement(v reflect.Value, path encPath) (reflect.Value, error) {
ev := followPtr(v)
if !ev.IsValid() {
return ev, &EncodeError{Path: path.String(), Err: errors.New("nil element")}
return ev, &EncodeError{Path: path.segments(), Err: errors.New("nil element")}
}
m, ok := marshalerOf(ev)
if !ok {
@@ -875,14 +869,14 @@ func resolveElement(v reflect.Value, path encPath) (reflect.Value, error) {
}
mv, err := m.MarshalTOML()
if err != nil {
return reflect.Value{}, &EncodeError{Path: path.String(), Err: err}
return reflect.Value{}, &EncodeError{Path: path.segments(), Err: err}
}
if mv == nil {
return reflect.Value{}, &EncodeError{Path: path.String(), Err: errNilMarshalTOML}
return reflect.Value{}, &EncodeError{Path: path.segments(), Err: errNilMarshalTOML}
}
ev = followPtr(reflect.ValueOf(mv))
if !ev.IsValid() {
return ev, &EncodeError{Path: path.String(), Err: errors.New("nil element")}
return ev, &EncodeError{Path: path.segments(), Err: errors.New("nil element")}
}
return ev, nil
}