|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- // Copyright 2016 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- // Package fields provides a view of the fields of a struct that follows the Go
- // rules, amended to consider tags and case insensitivity.
- //
- // Usage
- //
- // First define a function that interprets tags:
- //
- // func parseTag(st reflect.StructTag) (name string, keep bool, other interface{}, err error) { ... }
- //
- // The function's return values describe whether to ignore the field
- // completely or provide an alternate name, as well as other data from the
- // parse that is stored to avoid re-parsing.
- //
- // Then define a function to validate the type:
- //
- // func validate(t reflect.Type) error { ... }
- //
- // Then, if necessary, define a function to specify leaf types - types
- // which should be considered one field and not be recursed into:
- //
- // func isLeafType(t reflect.Type) bool { ... }
- //
- // eg:
- //
- // func isLeafType(t reflect.Type) bool {
- // return t == reflect.TypeOf(time.Time{})
- // }
- //
- // Next, construct a Cache, passing your functions. As its name suggests, a
- // Cache remembers validation and field information for a type, so subsequent
- // calls with the same type are very fast.
- //
- // cache := fields.NewCache(parseTag, validate, isLeafType)
- //
- // To get the fields of a struct type as determined by the above rules, call
- // the Fields method:
- //
- // fields, err := cache.Fields(reflect.TypeOf(MyStruct{}))
- //
- // The return value can be treated as a slice of Fields.
- //
- // Given a string, such as a key or column name obtained during unmarshalling,
- // call Match on the list of fields to find a field whose name is the best
- // match:
- //
- // field := fields.Match(name)
- //
- // Match looks for an exact match first, then falls back to a case-insensitive
- // comparison.
- package fields
-
- import (
- "bytes"
- "errors"
- "reflect"
- "sort"
- "strings"
- "sync"
- )
-
- // A Field records information about a struct field.
- type Field struct {
- Name string // effective field name
- NameFromTag bool // did Name come from a tag?
- Type reflect.Type // field type
- Index []int // index sequence, for reflect.Value.FieldByIndex
- ParsedTag interface{} // third return value of the parseTag function
-
- nameBytes []byte
- equalFold func(s, t []byte) bool
- }
-
- // ParseTagFunc is a function that accepts a struct tag and returns four values: an alternative name for the field
- // extracted from the tag, a boolean saying whether to keep the field or ignore it, additional data that is stored
- // with the field information to avoid having to parse the tag again, and an error.
- type ParseTagFunc func(reflect.StructTag) (name string, keep bool, other interface{}, err error)
-
- // ValidateFunc is a function that accepts a reflect.Type and returns an error if the struct type is invalid in any
- // way.
- type ValidateFunc func(reflect.Type) error
-
- // LeafTypesFunc is a function that accepts a reflect.Type and returns true if the struct type a leaf, or false if not.
- // TODO(deklerk) is this description accurate?
- type LeafTypesFunc func(reflect.Type) bool
-
- // A Cache records information about the fields of struct types.
- //
- // A Cache is safe for use by multiple goroutines.
- type Cache struct {
- parseTag ParseTagFunc
- validate ValidateFunc
- leafTypes LeafTypesFunc
- cache sync.Map // from reflect.Type to cacheValue
- }
-
- // NewCache constructs a Cache.
- //
- // Its first argument should be a function that accepts
- // a struct tag and returns four values: an alternative name for the field
- // extracted from the tag, a boolean saying whether to keep the field or ignore
- // it, additional data that is stored with the field information to avoid
- // having to parse the tag again, and an error.
- //
- // Its second argument should be a function that accepts a reflect.Type and
- // returns an error if the struct type is invalid in any way. For example, it
- // may check that all of the struct field tags are valid, or that all fields
- // are of an appropriate type.
- func NewCache(parseTag ParseTagFunc, validate ValidateFunc, leafTypes LeafTypesFunc) *Cache {
- if parseTag == nil {
- parseTag = func(reflect.StructTag) (string, bool, interface{}, error) {
- return "", true, nil, nil
- }
- }
- if validate == nil {
- validate = func(reflect.Type) error {
- return nil
- }
- }
- if leafTypes == nil {
- leafTypes = func(reflect.Type) bool {
- return false
- }
- }
-
- return &Cache{
- parseTag: parseTag,
- validate: validate,
- leafTypes: leafTypes,
- }
- }
-
- // A fieldScan represents an item on the fieldByNameFunc scan work list.
- type fieldScan struct {
- typ reflect.Type
- index []int
- }
-
- // Fields returns all the exported fields of t, which must be a struct type. It
- // follows the standard Go rules for embedded fields, modified by the presence
- // of tags. The result is sorted lexicographically by index.
- //
- // These rules apply in the absence of tags:
- // Anonymous struct fields are treated as if their inner exported fields were
- // fields in the outer struct (embedding). The result includes all fields that
- // aren't shadowed by fields at higher level of embedding. If more than one
- // field with the same name exists at the same level of embedding, it is
- // excluded. An anonymous field that is not of struct type is treated as having
- // its type as its name.
- //
- // Tags modify these rules as follows:
- // A field's tag is used as its name.
- // An anonymous struct field with a name given in its tag is treated as
- // a field having that name, rather than an embedded struct (the struct's
- // fields will not be returned).
- // If more than one field with the same name exists at the same level of embedding,
- // but exactly one of them is tagged, then the tagged field is reported and the others
- // are ignored.
- func (c *Cache) Fields(t reflect.Type) (List, error) {
- if t.Kind() != reflect.Struct {
- panic("fields: Fields of non-struct type")
- }
- return c.cachedTypeFields(t)
- }
-
- // A List is a list of Fields.
- type List []Field
-
- // Match returns the field in the list whose name best matches the supplied
- // name, nor nil if no field does. If there is a field with the exact name, it
- // is returned. Otherwise the first field (sorted by index) whose name matches
- // case-insensitively is returned.
- func (l List) Match(name string) *Field {
- return l.MatchBytes([]byte(name))
- }
-
- // MatchBytes is identical to Match, except that the argument is a byte slice.
- func (l List) MatchBytes(name []byte) *Field {
- var f *Field
- for i := range l {
- ff := &l[i]
- if bytes.Equal(ff.nameBytes, name) {
- return ff
- }
- if f == nil && ff.equalFold(ff.nameBytes, name) {
- f = ff
- }
- }
- return f
- }
-
- type cacheValue struct {
- fields List
- err error
- }
-
- // cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
- // This code has been copied and modified from
- // https://go.googlesource.com/go/+/go1.7.3/src/encoding/json/encode.go.
- func (c *Cache) cachedTypeFields(t reflect.Type) (List, error) {
- var cv cacheValue
- x, ok := c.cache.Load(t)
- if ok {
- cv = x.(cacheValue)
- } else {
- if err := c.validate(t); err != nil {
- cv = cacheValue{nil, err}
- } else {
- f, err := c.typeFields(t)
- cv = cacheValue{List(f), err}
- }
- c.cache.Store(t, cv)
- }
- return cv.fields, cv.err
- }
-
- func (c *Cache) typeFields(t reflect.Type) ([]Field, error) {
- fields, err := c.listFields(t)
- if err != nil {
- return nil, err
- }
- sort.Sort(byName(fields))
- // Delete all fields that are hidden by the Go rules for embedded fields.
-
- // The fields are sorted in primary order of name, secondary order of field
- // index length. So the first field with a given name is the dominant one.
- var out []Field
- for advance, i := 0, 0; i < len(fields); i += advance {
- // One iteration per name.
- // Find the sequence of fields with the name of this first field.
- fi := fields[i]
- name := fi.Name
- for advance = 1; i+advance < len(fields); advance++ {
- fj := fields[i+advance]
- if fj.Name != name {
- break
- }
- }
- // Find the dominant field, if any, out of all fields that have the same name.
- dominant, ok := dominantField(fields[i : i+advance])
- if ok {
- out = append(out, dominant)
- }
- }
- sort.Sort(byIndex(out))
- return out, nil
- }
-
- func (c *Cache) listFields(t reflect.Type) ([]Field, error) {
- // This uses the same condition that the Go language does: there must be a unique instance
- // of the match at a given depth level. If there are multiple instances of a match at the
- // same depth, they annihilate each other and inhibit any possible match at a lower level.
- // The algorithm is breadth first search, one depth level at a time.
-
- // The current and next slices are work queues:
- // current lists the fields to visit on this depth level,
- // and next lists the fields on the next lower level.
- current := []fieldScan{}
- next := []fieldScan{{typ: t}}
-
- // nextCount records the number of times an embedded type has been
- // encountered and considered for queueing in the 'next' slice.
- // We only queue the first one, but we increment the count on each.
- // If a struct type T can be reached more than once at a given depth level,
- // then it annihilates itself and need not be considered at all when we
- // process that next depth level.
- var nextCount map[reflect.Type]int
-
- // visited records the structs that have been considered already.
- // Embedded pointer fields can create cycles in the graph of
- // reachable embedded types; visited avoids following those cycles.
- // It also avoids duplicated effort: if we didn't find the field in an
- // embedded type T at level 2, we won't find it in one at level 4 either.
- visited := map[reflect.Type]bool{}
-
- var fields []Field // Fields found.
-
- for len(next) > 0 {
- current, next = next, current[:0]
- count := nextCount
- nextCount = nil
-
- // Process all the fields at this depth, now listed in 'current'.
- // The loop queues embedded fields found in 'next', for processing during the next
- // iteration. The multiplicity of the 'current' field counts is recorded
- // in 'count'; the multiplicity of the 'next' field counts is recorded in 'nextCount'.
- for _, scan := range current {
- t := scan.typ
- if visited[t] {
- // We've looked through this type before, at a higher level.
- // That higher level would shadow the lower level we're now at,
- // so this one can't be useful to us. Ignore it.
- continue
- }
- visited[t] = true
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
-
- exported := (f.PkgPath == "")
-
- // If a named field is unexported, ignore it. An anonymous
- // unexported field is processed, because it may contain
- // exported fields, which are visible.
- if !exported && !f.Anonymous {
- continue
- }
-
- // Examine the tag.
- tagName, keep, other, err := c.parseTag(f.Tag)
- if err != nil {
- return nil, err
- }
- if !keep {
- continue
- }
- if c.leafTypes(f.Type) {
- fields = append(fields, newField(f, tagName, other, scan.index, i))
- continue
- }
-
- var ntyp reflect.Type
- if f.Anonymous {
- // Anonymous field of type T or *T.
- ntyp = f.Type
- if ntyp.Kind() == reflect.Ptr {
- ntyp = ntyp.Elem()
- }
- }
-
- // Record fields with a tag name, non-anonymous fields, or
- // anonymous non-struct fields.
- if tagName != "" || ntyp == nil || ntyp.Kind() != reflect.Struct {
- if !exported {
- continue
- }
- fields = append(fields, newField(f, tagName, other, scan.index, i))
- if count[t] > 1 {
- // If there were multiple instances, add a second,
- // so that the annihilation code will see a duplicate.
- fields = append(fields, fields[len(fields)-1])
- }
- continue
- }
-
- // Queue embedded struct fields for processing with next level,
- // but only if the embedded types haven't already been queued.
- if nextCount[ntyp] > 0 {
- nextCount[ntyp] = 2 // exact multiple doesn't matter
- continue
- }
- if nextCount == nil {
- nextCount = map[reflect.Type]int{}
- }
- nextCount[ntyp] = 1
- if count[t] > 1 {
- nextCount[ntyp] = 2 // exact multiple doesn't matter
- }
- var index []int
- index = append(index, scan.index...)
- index = append(index, i)
- next = append(next, fieldScan{ntyp, index})
- }
- }
- }
- return fields, nil
- }
-
- func newField(f reflect.StructField, tagName string, other interface{}, index []int, i int) Field {
- name := tagName
- if name == "" {
- name = f.Name
- }
- sf := Field{
- Name: name,
- NameFromTag: tagName != "",
- Type: f.Type,
- ParsedTag: other,
- nameBytes: []byte(name),
- }
- sf.equalFold = foldFunc(sf.nameBytes)
- sf.Index = append(sf.Index, index...)
- sf.Index = append(sf.Index, i)
- return sf
- }
-
- // byName sorts fields using the following criteria, in order:
- // 1. name
- // 2. embedding depth
- // 3. tag presence (preferring a tagged field)
- // 4. index sequence.
- type byName []Field
-
- func (x byName) Len() int { return len(x) }
-
- func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-
- func (x byName) Less(i, j int) bool {
- if x[i].Name != x[j].Name {
- return x[i].Name < x[j].Name
- }
- if len(x[i].Index) != len(x[j].Index) {
- return len(x[i].Index) < len(x[j].Index)
- }
- if x[i].NameFromTag != x[j].NameFromTag {
- return x[i].NameFromTag
- }
- return byIndex(x).Less(i, j)
- }
-
- // byIndex sorts field by index sequence.
- type byIndex []Field
-
- func (x byIndex) Len() int { return len(x) }
-
- func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-
- func (x byIndex) Less(i, j int) bool {
- xi := x[i].Index
- xj := x[j].Index
- ln := len(xi)
- if l := len(xj); l < ln {
- ln = l
- }
- for k := 0; k < ln; k++ {
- if xi[k] != xj[k] {
- return xi[k] < xj[k]
- }
- }
- return len(xi) < len(xj)
- }
-
- // dominantField looks through the fields, all of which are known to have the
- // same name, to find the single field that dominates the others using Go's
- // embedding rules, modified by the presence of tags. If there are multiple
- // top-level fields, the boolean will be false: This condition is an error in
- // Go and we skip all the fields.
- func dominantField(fs []Field) (Field, bool) {
- // The fields are sorted in increasing index-length order, then by presence of tag.
- // That means that the first field is the dominant one. We need only check
- // for error cases: two fields at top level, either both tagged or neither tagged.
- if len(fs) > 1 && len(fs[0].Index) == len(fs[1].Index) && fs[0].NameFromTag == fs[1].NameFromTag {
- return Field{}, false
- }
- return fs[0], true
- }
-
- // ParseStandardTag extracts the sub-tag named by key, then parses it using the
- // de facto standard format introduced in encoding/json:
- // "-" means "ignore this tag". It must occur by itself. (parseStandardTag returns an error
- // in this case, whereas encoding/json accepts the "-" even if it is not alone.)
- // "<name>" provides an alternative name for the field
- // "<name>,opt1,opt2,..." specifies options after the name.
- // The options are returned as a []string.
- func ParseStandardTag(key string, t reflect.StructTag) (name string, keep bool, options []string, err error) {
- s := t.Get(key)
- parts := strings.Split(s, ",")
- if parts[0] == "-" {
- if len(parts) > 1 {
- return "", false, nil, errors.New(`"-" field tag with options`)
- }
- return "", false, nil, nil
- }
- if len(parts) > 1 {
- options = parts[1:]
- }
- return parts[0], true, options, nil
- }
|