summaryrefslogtreecommitdiff
path: root/struct.go
diff options
context:
space:
mode:
Diffstat (limited to 'struct.go')
-rw-r--r--struct.go30
1 files changed, 21 insertions, 9 deletions
diff --git a/struct.go b/struct.go
index ee2bbf3..24b2ac0 100644
--- a/struct.go
+++ b/struct.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,16 +16,20 @@ type structField struct {
Quote bool
}
+// A structIndex is used by Decoder.Decode() and Encoder.Encode() when
+// decoding-to or encoding-from a struct.
type structIndex struct {
byPos []structField
byName map[string]int
}
+// indexStruct takes a struct Type, and indexes its fields for use by
+// Decoder.Decode() and Encoder.Encode().
func indexStruct(typ reflect.Type) structIndex {
var byPos []structField
byName := make(map[string][]int)
- indexStructInner(typ, nil, &byPos, byName, map[reflect.Type]struct{}{})
+ indexStructInner(typ, &byPos, byName, nil, map[reflect.Type]struct{}{})
ret := structIndex{
byName: make(map[string]int),
@@ -104,16 +108,22 @@ func indexStruct(typ reflect.Type) structIndex {
return ret
}
-func indexStructInner(typ reflect.Type, prefix []int, byPos *[]structField, byName map[string][]int, seen map[reflect.Type]struct{}) {
- if _, ok := seen[typ]; ok {
+// indexStructInner crawls the struct `typ`, storing information on
+// all struct fields foun in to `byPos` and `byName`. If `typ`
+// contains other structs as fields, indexStructInner will recurse and
+// call itself; keeping track of stack information with `stackPath`
+// (which identifies where we are in the parent struct) and
+// `stackSeen` (which is used for detecting loops).
+func indexStructInner(typ reflect.Type, byPos *[]structField, byName map[string][]int, stackPath []int, stackSeen map[reflect.Type]struct{}) {
+ if _, ok := stackSeen[typ]; ok {
return
}
- seen[typ] = struct{}{}
- defer delete(seen, typ)
+ stackSeen[typ] = struct{}{}
+ defer delete(stackSeen, typ)
n := typ.NumField()
for i := 0; i < n; i++ {
- path := append(append([]int(nil), prefix...), i)
+ stackPath := append(stackPath, i)
fTyp := typ.Field(i)
var embed bool
@@ -147,12 +157,12 @@ func indexStructInner(typ reflect.Type, prefix []int, byPos *[]structField, byNa
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
- indexStructInner(t, path, byPos, byName, seen)
+ indexStructInner(t, byPos, byName, stackPath, stackSeen)
} else {
byName[name] = append(byName[name], len(*byPos))
*byPos = append(*byPos, structField{
Name: name,
- Path: path,
+ Path: append([]int(nil), stackPath...),
Tagged: tagName != "",
OmitEmpty: opts.Contains("omitempty"),
Quote: opts.Contains("string") && isQuotable(fTyp.Type),
@@ -161,6 +171,8 @@ func indexStructInner(typ reflect.Type, prefix []int, byPos *[]structField, byNa
}
}
+// isQuotable returns whether a type is eligible for `json:,string`
+// quoting.
func isQuotable(typ reflect.Type) bool {
for typ.Kind() == reflect.Pointer {
typ = typ.Elem()