diff options
Diffstat (limited to 'struct.go')
-rw-r--r-- | struct.go | 30 |
1 files changed, 21 insertions, 9 deletions
@@ -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() |