aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/jmoiron/sqlx/reflectx/reflect.go')
-rw-r--r--vendor/github.com/jmoiron/sqlx/reflectx/reflect.go422
1 files changed, 422 insertions, 0 deletions
diff --git a/vendor/github.com/jmoiron/sqlx/reflectx/reflect.go b/vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
new file mode 100644
index 0000000..427ed2a
--- /dev/null
+++ b/vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
@@ -0,0 +1,422 @@
+// Package reflectx implements extensions to the standard reflect lib suitable
+// for implementing marshalling and unmarshalling packages. The main Mapper type
+// allows for Go-compatible named attribute access, including accessing embedded
+// struct attributes and the ability to use functions and struct tags to
+// customize field names.
+//
+package reflectx
+
+import (
+ "reflect"
+ "runtime"
+ "strings"
+ "sync"
+)
+
+// A FieldInfo is metadata for a struct field.
+type FieldInfo struct {
+ Index []int
+ Path string
+ Field reflect.StructField
+ Zero reflect.Value
+ Name string
+ Options map[string]string
+ Embedded bool
+ Children []*FieldInfo
+ Parent *FieldInfo
+}
+
+// A StructMap is an index of field metadata for a struct.
+type StructMap struct {
+ Tree *FieldInfo
+ Index []*FieldInfo
+ Paths map[string]*FieldInfo
+ Names map[string]*FieldInfo
+}
+
+// GetByPath returns a *FieldInfo for a given string path.
+func (f StructMap) GetByPath(path string) *FieldInfo {
+ return f.Paths[path]
+}
+
+// GetByTraversal returns a *FieldInfo for a given integer path. It is
+// analogous to reflect.FieldByIndex, but using the cached traversal
+// rather than re-executing the reflect machinery each time.
+func (f StructMap) GetByTraversal(index []int) *FieldInfo {
+ if len(index) == 0 {
+ return nil
+ }
+
+ tree := f.Tree
+ for _, i := range index {
+ if i >= len(tree.Children) || tree.Children[i] == nil {
+ return nil
+ }
+ tree = tree.Children[i]
+ }
+ return tree
+}
+
+// Mapper is a general purpose mapper of names to struct fields. A Mapper
+// behaves like most marshallers in the standard library, obeying a field tag
+// for name mapping but also providing a basic transform function.
+type Mapper struct {
+ cache map[reflect.Type]*StructMap
+ tagName string
+ tagMapFunc func(string) string
+ mapFunc func(string) string
+ mutex sync.Mutex
+}
+
+// NewMapper returns a new mapper using the tagName as its struct field tag.
+// If tagName is the empty string, it is ignored.
+func NewMapper(tagName string) *Mapper {
+ return &Mapper{
+ cache: make(map[reflect.Type]*StructMap),
+ tagName: tagName,
+ }
+}
+
+// NewMapperTagFunc returns a new mapper which contains a mapper for field names
+// AND a mapper for tag values. This is useful for tags like json which can
+// have values like "name,omitempty".
+func NewMapperTagFunc(tagName string, mapFunc, tagMapFunc func(string) string) *Mapper {
+ return &Mapper{
+ cache: make(map[reflect.Type]*StructMap),
+ tagName: tagName,
+ mapFunc: mapFunc,
+ tagMapFunc: tagMapFunc,
+ }
+}
+
+// NewMapperFunc returns a new mapper which optionally obeys a field tag and
+// a struct field name mapper func given by f. Tags will take precedence, but
+// for any other field, the mapped name will be f(field.Name)
+func NewMapperFunc(tagName string, f func(string) string) *Mapper {
+ return &Mapper{
+ cache: make(map[reflect.Type]*StructMap),
+ tagName: tagName,
+ mapFunc: f,
+ }
+}
+
+// TypeMap returns a mapping of field strings to int slices representing
+// the traversal down the struct to reach the field.
+func (m *Mapper) TypeMap(t reflect.Type) *StructMap {
+ m.mutex.Lock()
+ mapping, ok := m.cache[t]
+ if !ok {
+ mapping = getMapping(t, m.tagName, m.mapFunc, m.tagMapFunc)
+ m.cache[t] = mapping
+ }
+ m.mutex.Unlock()
+ return mapping
+}
+
+// FieldMap returns the mapper's mapping of field names to reflect values. Panics
+// if v's Kind is not Struct, or v is not Indirectable to a struct kind.
+func (m *Mapper) FieldMap(v reflect.Value) map[string]reflect.Value {
+ v = reflect.Indirect(v)
+ mustBe(v, reflect.Struct)
+
+ r := map[string]reflect.Value{}
+ tm := m.TypeMap(v.Type())
+ for tagName, fi := range tm.Names {
+ r[tagName] = FieldByIndexes(v, fi.Index)
+ }
+ return r
+}
+
+// FieldByName returns a field by the its mapped name as a reflect.Value.
+// Panics if v's Kind is not Struct or v is not Indirectable to a struct Kind.
+// Returns zero Value if the name is not found.
+func (m *Mapper) FieldByName(v reflect.Value, name string) reflect.Value {
+ v = reflect.Indirect(v)
+ mustBe(v, reflect.Struct)
+
+ tm := m.TypeMap(v.Type())
+ fi, ok := tm.Names[name]
+ if !ok {
+ return v
+ }
+ return FieldByIndexes(v, fi.Index)
+}
+
+// FieldsByName returns a slice of values corresponding to the slice of names
+// for the value. Panics if v's Kind is not Struct or v is not Indirectable
+// to a struct Kind. Returns zero Value for each name not found.
+func (m *Mapper) FieldsByName(v reflect.Value, names []string) []reflect.Value {
+ v = reflect.Indirect(v)
+ mustBe(v, reflect.Struct)
+
+ tm := m.TypeMap(v.Type())
+ vals := make([]reflect.Value, 0, len(names))
+ for _, name := range names {
+ fi, ok := tm.Names[name]
+ if !ok {
+ vals = append(vals, *new(reflect.Value))
+ } else {
+ vals = append(vals, FieldByIndexes(v, fi.Index))
+ }
+ }
+ return vals
+}
+
+// TraversalsByName returns a slice of int slices which represent the struct
+// traversals for each mapped name. Panics if t is not a struct or Indirectable
+// to a struct. Returns empty int slice for each name not found.
+func (m *Mapper) TraversalsByName(t reflect.Type, names []string) [][]int {
+ t = Deref(t)
+ mustBe(t, reflect.Struct)
+ tm := m.TypeMap(t)
+
+ r := make([][]int, 0, len(names))
+ for _, name := range names {
+ fi, ok := tm.Names[name]
+ if !ok {
+ r = append(r, []int{})
+ } else {
+ r = append(r, fi.Index)
+ }
+ }
+ return r
+}
+
+// FieldByIndexes returns a value for the field given by the struct traversal
+// for the given value.
+func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value {
+ for _, i := range indexes {
+ v = reflect.Indirect(v).Field(i)
+ // if this is a pointer and it's nil, allocate a new value and set it
+ if v.Kind() == reflect.Ptr && v.IsNil() {
+ alloc := reflect.New(Deref(v.Type()))
+ v.Set(alloc)
+ }
+ if v.Kind() == reflect.Map && v.IsNil() {
+ v.Set(reflect.MakeMap(v.Type()))
+ }
+ }
+ return v
+}
+
+// FieldByIndexesReadOnly returns a value for a particular struct traversal,
+// but is not concerned with allocating nil pointers because the value is
+// going to be used for reading and not setting.
+func FieldByIndexesReadOnly(v reflect.Value, indexes []int) reflect.Value {
+ for _, i := range indexes {
+ v = reflect.Indirect(v).Field(i)
+ }
+ return v
+}
+
+// Deref is Indirect for reflect.Types
+func Deref(t reflect.Type) reflect.Type {
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ return t
+}
+
+// -- helpers & utilities --
+
+type kinder interface {
+ Kind() reflect.Kind
+}
+
+// mustBe checks a value against a kind, panicing with a reflect.ValueError
+// if the kind isn't that which is required.
+func mustBe(v kinder, expected reflect.Kind) {
+ if k := v.Kind(); k != expected {
+ panic(&reflect.ValueError{Method: methodName(), Kind: k})
+ }
+}
+
+// methodName returns the caller of the function calling methodName
+func methodName() string {
+ pc, _, _, _ := runtime.Caller(2)
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ return "unknown method"
+ }
+ return f.Name()
+}
+
+type typeQueue struct {
+ t reflect.Type
+ fi *FieldInfo
+ pp string // Parent path
+}
+
+// A copying append that creates a new slice each time.
+func apnd(is []int, i int) []int {
+ x := make([]int, len(is)+1)
+ for p, n := range is {
+ x[p] = n
+ }
+ x[len(x)-1] = i
+ return x
+}
+
+type mapf func(string) string
+
+// parseName parses the tag and the target name for the given field using
+// the tagName (eg 'json' for `json:"foo"` tags), mapFunc for mapping the
+// field's name to a target name, and tagMapFunc for mapping the tag to
+// a target name.
+func parseName(field reflect.StructField, tagName string, mapFunc, tagMapFunc mapf) (tag, fieldName string) {
+ // first, set the fieldName to the field's name
+ fieldName = field.Name
+ // if a mapFunc is set, use that to override the fieldName
+ if mapFunc != nil {
+ fieldName = mapFunc(fieldName)
+ }
+
+ // if there's no tag to look for, return the field name
+ if tagName == "" {
+ return "", fieldName
+ }
+
+ // if this tag is not set using the normal convention in the tag,
+ // then return the fieldname.. this check is done because according
+ // to the reflect documentation:
+ // If the tag does not have the conventional format,
+ // the value returned by Get is unspecified.
+ // which doesn't sound great.
+ if !strings.Contains(string(field.Tag), tagName+":") {
+ return "", fieldName
+ }
+
+ // at this point we're fairly sure that we have a tag, so lets pull it out
+ tag = field.Tag.Get(tagName)
+
+ // if we have a mapper function, call it on the whole tag
+ // XXX: this is a change from the old version, which pulled out the name
+ // before the tagMapFunc could be run, but I think this is the right way
+ if tagMapFunc != nil {
+ tag = tagMapFunc(tag)
+ }
+
+ // finally, split the options from the name
+ parts := strings.Split(tag, ",")
+ fieldName = parts[0]
+
+ return tag, fieldName
+}
+
+// parseOptions parses options out of a tag string, skipping the name
+func parseOptions(tag string) map[string]string {
+ parts := strings.Split(tag, ",")
+ options := make(map[string]string, len(parts))
+ if len(parts) > 1 {
+ for _, opt := range parts[1:] {
+ // short circuit potentially expensive split op
+ if strings.Contains(opt, "=") {
+ kv := strings.Split(opt, "=")
+ options[kv[0]] = kv[1]
+ continue
+ }
+ options[opt] = ""
+ }
+ }
+ return options
+}
+
+// getMapping returns a mapping for the t type, using the tagName, mapFunc and
+// tagMapFunc to determine the canonical names of fields.
+func getMapping(t reflect.Type, tagName string, mapFunc, tagMapFunc mapf) *StructMap {
+ m := []*FieldInfo{}
+
+ root := &FieldInfo{}
+ queue := []typeQueue{}
+ queue = append(queue, typeQueue{Deref(t), root, ""})
+
+QueueLoop:
+ for len(queue) != 0 {
+ // pop the first item off of the queue
+ tq := queue[0]
+ queue = queue[1:]
+
+ // ignore recursive field
+ for p := tq.fi.Parent; p != nil; p = p.Parent {
+ if tq.fi.Field.Type == p.Field.Type {
+ continue QueueLoop
+ }
+ }
+
+ nChildren := 0
+ if tq.t.Kind() == reflect.Struct {
+ nChildren = tq.t.NumField()
+ }
+ tq.fi.Children = make([]*FieldInfo, nChildren)
+
+ // iterate through all of its fields
+ for fieldPos := 0; fieldPos < nChildren; fieldPos++ {
+
+ f := tq.t.Field(fieldPos)
+
+ // parse the tag and the target name using the mapping options for this field
+ tag, name := parseName(f, tagName, mapFunc, tagMapFunc)
+
+ // if the name is "-", disabled via a tag, skip it
+ if name == "-" {
+ continue
+ }
+
+ fi := FieldInfo{
+ Field: f,
+ Name: name,
+ Zero: reflect.New(f.Type).Elem(),
+ Options: parseOptions(tag),
+ }
+
+ // if the path is empty this path is just the name
+ if tq.pp == "" {
+ fi.Path = fi.Name
+ } else {
+ fi.Path = tq.pp + "." + fi.Name
+ }
+
+ // skip unexported fields
+ if len(f.PkgPath) != 0 && !f.Anonymous {
+ continue
+ }
+
+ // bfs search of anonymous embedded structs
+ if f.Anonymous {
+ pp := tq.pp
+ if tag != "" {
+ pp = fi.Path
+ }
+
+ fi.Embedded = true
+ fi.Index = apnd(tq.fi.Index, fieldPos)
+ nChildren := 0
+ ft := Deref(f.Type)
+ if ft.Kind() == reflect.Struct {
+ nChildren = ft.NumField()
+ }
+ fi.Children = make([]*FieldInfo, nChildren)
+ queue = append(queue, typeQueue{Deref(f.Type), &fi, pp})
+ } else if fi.Zero.Kind() == reflect.Struct || (fi.Zero.Kind() == reflect.Ptr && fi.Zero.Type().Elem().Kind() == reflect.Struct) {
+ fi.Index = apnd(tq.fi.Index, fieldPos)
+ fi.Children = make([]*FieldInfo, Deref(f.Type).NumField())
+ queue = append(queue, typeQueue{Deref(f.Type), &fi, fi.Path})
+ }
+
+ fi.Index = apnd(tq.fi.Index, fieldPos)
+ fi.Parent = tq.fi
+ tq.fi.Children[fieldPos] = &fi
+ m = append(m, &fi)
+ }
+ }
+
+ flds := &StructMap{Index: m, Tree: root, Paths: map[string]*FieldInfo{}, Names: map[string]*FieldInfo{}}
+ for _, fi := range flds.Index {
+ flds.Paths[fi.Path] = fi
+ if fi.Name != "" && !fi.Embedded {
+ flds.Names[fi.Path] = fi
+ }
+ }
+
+ return flds
+}