mirror of
https://github.com/documize/community.git
synced 2025-07-21 06:09:42 +02:00
initial commit
This commit is contained in:
commit
18933c6767
1841 changed files with 810642 additions and 0 deletions
17
vendor/github.com/jmoiron/sqlx/reflectx/README.md
generated
vendored
Normal file
17
vendor/github.com/jmoiron/sqlx/reflectx/README.md
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# reflectx
|
||||
|
||||
The sqlx package has special reflect needs. In particular, it needs to:
|
||||
|
||||
* be able to map a name to a field
|
||||
* understand embedded structs
|
||||
* understand mapping names to fields by a particular tag
|
||||
* user specified name -> field mapping functions
|
||||
|
||||
These behaviors mimic the behaviors by the standard library marshallers and also the
|
||||
behavior of standard Go accessors.
|
||||
|
||||
The first two are amply taken care of by `Reflect.Value.FieldByName`, and the third is
|
||||
addressed by `Reflect.Value.FieldByNameFunc`, but these don't quite understand struct
|
||||
tags in the ways that are vital to most marshalers, and they are slow.
|
||||
|
||||
This reflectx package extends reflect to achieve these goals.
|
371
vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
generated
vendored
Normal file
371
vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
generated
vendored
Normal file
|
@ -0,0 +1,371 @@
|
|||
// Package reflectx implements extensions to the standard reflect lib suitable
|
||||
// for implementing marshaling and unmarshaling 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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A FieldInfo is a collection of metadata about 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.
|
||||
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, optionally obeying a field tag for name
|
||||
// mapping and a function to provide a basic mapping of fields to names.
|
||||
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 which optionally obeys the field tag given
|
||||
// by tagName. 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 a particular struct traversal.
|
||||
func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value {
|
||||
for _, i := range indexes {
|
||||
v = reflect.Indirect(v).Field(i)
|
||||
// if this is a pointer, it's possible it is nil
|
||||
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) {
|
||||
k := v.Kind()
|
||||
if k != expected {
|
||||
panic(&reflect.ValueError{Method: methodName(), Kind: k})
|
||||
}
|
||||
}
|
||||
|
||||
// methodName is 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
|
||||
}
|
||||
|
||||
// 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 func(string) string) *StructMap {
|
||||
m := []*FieldInfo{}
|
||||
|
||||
root := &FieldInfo{}
|
||||
queue := []typeQueue{}
|
||||
queue = append(queue, typeQueue{Deref(t), root, ""})
|
||||
|
||||
for len(queue) != 0 {
|
||||
// pop the first item off of the queue
|
||||
tq := queue[0]
|
||||
queue = queue[1:]
|
||||
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)
|
||||
|
||||
fi := FieldInfo{}
|
||||
fi.Field = f
|
||||
fi.Zero = reflect.New(f.Type).Elem()
|
||||
fi.Options = map[string]string{}
|
||||
|
||||
var tag, name string
|
||||
if tagName != "" && strings.Contains(string(f.Tag), tagName+":") {
|
||||
tag = f.Tag.Get(tagName)
|
||||
name = tag
|
||||
} else {
|
||||
if mapFunc != nil {
|
||||
name = mapFunc(f.Name)
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Split(name, ",")
|
||||
if len(parts) > 1 {
|
||||
name = parts[0]
|
||||
for _, opt := range parts[1:] {
|
||||
kv := strings.Split(opt, "=")
|
||||
if len(kv) > 1 {
|
||||
fi.Options[kv[0]] = kv[1]
|
||||
} else {
|
||||
fi.Options[kv[0]] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tagMapFunc != nil {
|
||||
tag = tagMapFunc(tag)
|
||||
}
|
||||
|
||||
fi.Name = name
|
||||
|
||||
if tq.pp == "" || (tq.pp == "" && tag == "") {
|
||||
fi.Path = fi.Name
|
||||
} else {
|
||||
fi.Path = fmt.Sprintf("%s.%s", tq.pp, fi.Name)
|
||||
}
|
||||
|
||||
// if the name is "-", disabled via a tag, skip it
|
||||
if name == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
587
vendor/github.com/jmoiron/sqlx/reflectx/reflect_test.go
generated
vendored
Normal file
587
vendor/github.com/jmoiron/sqlx/reflectx/reflect_test.go
generated
vendored
Normal file
|
@ -0,0 +1,587 @@
|
|||
package reflectx
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func ival(v reflect.Value) int {
|
||||
return v.Interface().(int)
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
type Foo struct {
|
||||
A int
|
||||
B int
|
||||
C int
|
||||
}
|
||||
|
||||
f := Foo{1, 2, 3}
|
||||
fv := reflect.ValueOf(f)
|
||||
m := NewMapperFunc("", func(s string) string { return s })
|
||||
|
||||
v := m.FieldByName(fv, "A")
|
||||
if ival(v) != f.A {
|
||||
t.Errorf("Expecting %d, got %d", ival(v), f.A)
|
||||
}
|
||||
v = m.FieldByName(fv, "B")
|
||||
if ival(v) != f.B {
|
||||
t.Errorf("Expecting %d, got %d", f.B, ival(v))
|
||||
}
|
||||
v = m.FieldByName(fv, "C")
|
||||
if ival(v) != f.C {
|
||||
t.Errorf("Expecting %d, got %d", f.C, ival(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicEmbedded(t *testing.T) {
|
||||
type Foo struct {
|
||||
A int
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
Foo // `db:""` is implied for an embedded struct
|
||||
B int
|
||||
C int `db:"-"`
|
||||
}
|
||||
|
||||
type Baz struct {
|
||||
A int
|
||||
Bar `db:"Bar"`
|
||||
}
|
||||
|
||||
m := NewMapperFunc("db", func(s string) string { return s })
|
||||
|
||||
z := Baz{}
|
||||
z.A = 1
|
||||
z.B = 2
|
||||
z.C = 4
|
||||
z.Bar.Foo.A = 3
|
||||
|
||||
zv := reflect.ValueOf(z)
|
||||
fields := m.TypeMap(reflect.TypeOf(z))
|
||||
|
||||
if len(fields.Index) != 5 {
|
||||
t.Errorf("Expecting 5 fields")
|
||||
}
|
||||
|
||||
// for _, fi := range fields.Index {
|
||||
// log.Println(fi)
|
||||
// }
|
||||
|
||||
v := m.FieldByName(zv, "A")
|
||||
if ival(v) != z.A {
|
||||
t.Errorf("Expecting %d, got %d", z.A, ival(v))
|
||||
}
|
||||
v = m.FieldByName(zv, "Bar.B")
|
||||
if ival(v) != z.Bar.B {
|
||||
t.Errorf("Expecting %d, got %d", z.Bar.B, ival(v))
|
||||
}
|
||||
v = m.FieldByName(zv, "Bar.A")
|
||||
if ival(v) != z.Bar.Foo.A {
|
||||
t.Errorf("Expecting %d, got %d", z.Bar.Foo.A, ival(v))
|
||||
}
|
||||
v = m.FieldByName(zv, "Bar.C")
|
||||
if _, ok := v.Interface().(int); ok {
|
||||
t.Errorf("Expecting Bar.C to not exist")
|
||||
}
|
||||
|
||||
fi := fields.GetByPath("Bar.C")
|
||||
if fi != nil {
|
||||
t.Errorf("Bar.C should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmbeddedSimple(t *testing.T) {
|
||||
type UUID [16]byte
|
||||
type MyID struct {
|
||||
UUID
|
||||
}
|
||||
type Item struct {
|
||||
ID MyID
|
||||
}
|
||||
z := Item{}
|
||||
|
||||
m := NewMapper("db")
|
||||
m.TypeMap(reflect.TypeOf(z))
|
||||
}
|
||||
|
||||
func TestBasicEmbeddedWithTags(t *testing.T) {
|
||||
type Foo struct {
|
||||
A int `db:"a"`
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
Foo // `db:""` is implied for an embedded struct
|
||||
B int `db:"b"`
|
||||
}
|
||||
|
||||
type Baz struct {
|
||||
A int `db:"a"`
|
||||
Bar // `db:""` is implied for an embedded struct
|
||||
}
|
||||
|
||||
m := NewMapper("db")
|
||||
|
||||
z := Baz{}
|
||||
z.A = 1
|
||||
z.B = 2
|
||||
z.Bar.Foo.A = 3
|
||||
|
||||
zv := reflect.ValueOf(z)
|
||||
fields := m.TypeMap(reflect.TypeOf(z))
|
||||
|
||||
if len(fields.Index) != 5 {
|
||||
t.Errorf("Expecting 5 fields")
|
||||
}
|
||||
|
||||
// for _, fi := range fields.index {
|
||||
// log.Println(fi)
|
||||
// }
|
||||
|
||||
v := m.FieldByName(zv, "a")
|
||||
if ival(v) != z.Bar.Foo.A { // the dominant field
|
||||
t.Errorf("Expecting %d, got %d", z.Bar.Foo.A, ival(v))
|
||||
}
|
||||
v = m.FieldByName(zv, "b")
|
||||
if ival(v) != z.B {
|
||||
t.Errorf("Expecting %d, got %d", z.B, ival(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlatTags(t *testing.T) {
|
||||
m := NewMapper("db")
|
||||
|
||||
type Asset struct {
|
||||
Title string `db:"title"`
|
||||
}
|
||||
type Post struct {
|
||||
Author string `db:"author,required"`
|
||||
Asset Asset `db:""`
|
||||
}
|
||||
// Post columns: (author title)
|
||||
|
||||
post := Post{Author: "Joe", Asset: Asset{Title: "Hello"}}
|
||||
pv := reflect.ValueOf(post)
|
||||
|
||||
v := m.FieldByName(pv, "author")
|
||||
if v.Interface().(string) != post.Author {
|
||||
t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "title")
|
||||
if v.Interface().(string) != post.Asset.Title {
|
||||
t.Errorf("Expecting %s, got %s", post.Asset.Title, v.Interface().(string))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNestedStruct(t *testing.T) {
|
||||
m := NewMapper("db")
|
||||
|
||||
type Details struct {
|
||||
Active bool `db:"active"`
|
||||
}
|
||||
type Asset struct {
|
||||
Title string `db:"title"`
|
||||
Details Details `db:"details"`
|
||||
}
|
||||
type Post struct {
|
||||
Author string `db:"author,required"`
|
||||
Asset `db:"asset"`
|
||||
}
|
||||
// Post columns: (author asset.title asset.details.active)
|
||||
|
||||
post := Post{
|
||||
Author: "Joe",
|
||||
Asset: Asset{Title: "Hello", Details: Details{Active: true}},
|
||||
}
|
||||
pv := reflect.ValueOf(post)
|
||||
|
||||
v := m.FieldByName(pv, "author")
|
||||
if v.Interface().(string) != post.Author {
|
||||
t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "title")
|
||||
if _, ok := v.Interface().(string); ok {
|
||||
t.Errorf("Expecting field to not exist")
|
||||
}
|
||||
v = m.FieldByName(pv, "asset.title")
|
||||
if v.Interface().(string) != post.Asset.Title {
|
||||
t.Errorf("Expecting %s, got %s", post.Asset.Title, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "asset.details.active")
|
||||
if v.Interface().(bool) != post.Asset.Details.Active {
|
||||
t.Errorf("Expecting %v, got %v", post.Asset.Details.Active, v.Interface().(bool))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInlineStruct(t *testing.T) {
|
||||
m := NewMapperTagFunc("db", strings.ToLower, nil)
|
||||
|
||||
type Employee struct {
|
||||
Name string
|
||||
ID int
|
||||
}
|
||||
type Boss Employee
|
||||
type person struct {
|
||||
Employee `db:"employee"`
|
||||
Boss `db:"boss"`
|
||||
}
|
||||
// employees columns: (employee.name employee.id boss.name boss.id)
|
||||
|
||||
em := person{Employee: Employee{Name: "Joe", ID: 2}, Boss: Boss{Name: "Dick", ID: 1}}
|
||||
ev := reflect.ValueOf(em)
|
||||
|
||||
fields := m.TypeMap(reflect.TypeOf(em))
|
||||
if len(fields.Index) != 6 {
|
||||
t.Errorf("Expecting 6 fields")
|
||||
}
|
||||
|
||||
v := m.FieldByName(ev, "employee.name")
|
||||
if v.Interface().(string) != em.Employee.Name {
|
||||
t.Errorf("Expecting %s, got %s", em.Employee.Name, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(ev, "boss.id")
|
||||
if ival(v) != em.Boss.ID {
|
||||
t.Errorf("Expecting %v, got %v", em.Boss.ID, ival(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsEmbedded(t *testing.T) {
|
||||
m := NewMapper("db")
|
||||
|
||||
type Person struct {
|
||||
Name string `db:"name"`
|
||||
}
|
||||
type Place struct {
|
||||
Name string `db:"name"`
|
||||
}
|
||||
type Article struct {
|
||||
Title string `db:"title"`
|
||||
}
|
||||
type PP struct {
|
||||
Person `db:"person,required"`
|
||||
Place `db:",someflag"`
|
||||
Article `db:",required"`
|
||||
}
|
||||
// PP columns: (person.name name title)
|
||||
|
||||
pp := PP{}
|
||||
pp.Person.Name = "Peter"
|
||||
pp.Place.Name = "Toronto"
|
||||
pp.Article.Title = "Best city ever"
|
||||
|
||||
fields := m.TypeMap(reflect.TypeOf(pp))
|
||||
// for i, f := range fields {
|
||||
// log.Println(i, f)
|
||||
// }
|
||||
|
||||
ppv := reflect.ValueOf(pp)
|
||||
|
||||
v := m.FieldByName(ppv, "person.name")
|
||||
if v.Interface().(string) != pp.Person.Name {
|
||||
t.Errorf("Expecting %s, got %s", pp.Person.Name, v.Interface().(string))
|
||||
}
|
||||
|
||||
v = m.FieldByName(ppv, "name")
|
||||
if v.Interface().(string) != pp.Place.Name {
|
||||
t.Errorf("Expecting %s, got %s", pp.Place.Name, v.Interface().(string))
|
||||
}
|
||||
|
||||
v = m.FieldByName(ppv, "title")
|
||||
if v.Interface().(string) != pp.Article.Title {
|
||||
t.Errorf("Expecting %s, got %s", pp.Article.Title, v.Interface().(string))
|
||||
}
|
||||
|
||||
fi := fields.GetByPath("person")
|
||||
if _, ok := fi.Options["required"]; !ok {
|
||||
t.Errorf("Expecting required option to be set")
|
||||
}
|
||||
if !fi.Embedded {
|
||||
t.Errorf("Expecting field to be embedded")
|
||||
}
|
||||
if len(fi.Index) != 1 || fi.Index[0] != 0 {
|
||||
t.Errorf("Expecting index to be [0]")
|
||||
}
|
||||
|
||||
fi = fields.GetByPath("person.name")
|
||||
if fi == nil {
|
||||
t.Errorf("Expecting person.name to exist")
|
||||
}
|
||||
if fi.Path != "person.name" {
|
||||
t.Errorf("Expecting %s, got %s", "person.name", fi.Path)
|
||||
}
|
||||
|
||||
fi = fields.GetByTraversal([]int{1, 0})
|
||||
if fi == nil {
|
||||
t.Errorf("Expecting traveral to exist")
|
||||
}
|
||||
if fi.Path != "name" {
|
||||
t.Errorf("Expecting %s, got %s", "name", fi.Path)
|
||||
}
|
||||
|
||||
fi = fields.GetByTraversal([]int{2})
|
||||
if fi == nil {
|
||||
t.Errorf("Expecting traversal to exist")
|
||||
}
|
||||
if _, ok := fi.Options["required"]; !ok {
|
||||
t.Errorf("Expecting required option to be set")
|
||||
}
|
||||
|
||||
trs := m.TraversalsByName(reflect.TypeOf(pp), []string{"person.name", "name", "title"})
|
||||
if !reflect.DeepEqual(trs, [][]int{{0, 0}, {1, 0}, {2, 0}}) {
|
||||
t.Errorf("Expecting traversal: %v", trs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPtrFields(t *testing.T) {
|
||||
m := NewMapperTagFunc("db", strings.ToLower, nil)
|
||||
type Asset struct {
|
||||
Title string
|
||||
}
|
||||
type Post struct {
|
||||
*Asset `db:"asset"`
|
||||
Author string
|
||||
}
|
||||
|
||||
post := &Post{Author: "Joe", Asset: &Asset{Title: "Hiyo"}}
|
||||
pv := reflect.ValueOf(post)
|
||||
|
||||
fields := m.TypeMap(reflect.TypeOf(post))
|
||||
if len(fields.Index) != 3 {
|
||||
t.Errorf("Expecting 3 fields")
|
||||
}
|
||||
|
||||
v := m.FieldByName(pv, "asset.title")
|
||||
if v.Interface().(string) != post.Asset.Title {
|
||||
t.Errorf("Expecting %s, got %s", post.Asset.Title, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "author")
|
||||
if v.Interface().(string) != post.Author {
|
||||
t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamedPtrFields(t *testing.T) {
|
||||
m := NewMapperTagFunc("db", strings.ToLower, nil)
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type Asset struct {
|
||||
Title string
|
||||
|
||||
Owner *User `db:"owner"`
|
||||
}
|
||||
type Post struct {
|
||||
Author string
|
||||
|
||||
Asset1 *Asset `db:"asset1"`
|
||||
Asset2 *Asset `db:"asset2"`
|
||||
}
|
||||
|
||||
post := &Post{Author: "Joe", Asset1: &Asset{Title: "Hiyo", Owner: &User{"Username"}}} // Let Asset2 be nil
|
||||
pv := reflect.ValueOf(post)
|
||||
|
||||
fields := m.TypeMap(reflect.TypeOf(post))
|
||||
if len(fields.Index) != 9 {
|
||||
t.Errorf("Expecting 9 fields")
|
||||
}
|
||||
|
||||
v := m.FieldByName(pv, "asset1.title")
|
||||
if v.Interface().(string) != post.Asset1.Title {
|
||||
t.Errorf("Expecting %s, got %s", post.Asset1.Title, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "asset1.owner.name")
|
||||
if v.Interface().(string) != post.Asset1.Owner.Name {
|
||||
t.Errorf("Expecting %s, got %s", post.Asset1.Owner.Name, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "asset2.title")
|
||||
if v.Interface().(string) != post.Asset2.Title {
|
||||
t.Errorf("Expecting %s, got %s", post.Asset2.Title, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "asset2.owner.name")
|
||||
if v.Interface().(string) != post.Asset2.Owner.Name {
|
||||
t.Errorf("Expecting %s, got %s", post.Asset2.Owner.Name, v.Interface().(string))
|
||||
}
|
||||
v = m.FieldByName(pv, "author")
|
||||
if v.Interface().(string) != post.Author {
|
||||
t.Errorf("Expecting %s, got %s", post.Author, v.Interface().(string))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldMap(t *testing.T) {
|
||||
type Foo struct {
|
||||
A int
|
||||
B int
|
||||
C int
|
||||
}
|
||||
|
||||
f := Foo{1, 2, 3}
|
||||
m := NewMapperFunc("db", strings.ToLower)
|
||||
|
||||
fm := m.FieldMap(reflect.ValueOf(f))
|
||||
|
||||
if len(fm) != 3 {
|
||||
t.Errorf("Expecting %d keys, got %d", 3, len(fm))
|
||||
}
|
||||
if fm["a"].Interface().(int) != 1 {
|
||||
t.Errorf("Expecting %d, got %d", 1, ival(fm["a"]))
|
||||
}
|
||||
if fm["b"].Interface().(int) != 2 {
|
||||
t.Errorf("Expecting %d, got %d", 2, ival(fm["b"]))
|
||||
}
|
||||
if fm["c"].Interface().(int) != 3 {
|
||||
t.Errorf("Expecting %d, got %d", 3, ival(fm["c"]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagNameMapping(t *testing.T) {
|
||||
type Strategy struct {
|
||||
StrategyID string `protobuf:"bytes,1,opt,name=strategy_id" json:"strategy_id,omitempty"`
|
||||
StrategyName string
|
||||
}
|
||||
|
||||
m := NewMapperTagFunc("json", strings.ToUpper, func(value string) string {
|
||||
if strings.Contains(value, ",") {
|
||||
return strings.Split(value, ",")[0]
|
||||
}
|
||||
return value
|
||||
})
|
||||
strategy := Strategy{"1", "Alpah"}
|
||||
mapping := m.TypeMap(reflect.TypeOf(strategy))
|
||||
|
||||
for _, key := range []string{"strategy_id", "STRATEGYNAME"} {
|
||||
if fi := mapping.GetByPath(key); fi == nil {
|
||||
t.Errorf("Expecting to find key %s in mapping but did not.", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapping(t *testing.T) {
|
||||
type Person struct {
|
||||
ID int
|
||||
Name string
|
||||
WearsGlasses bool `db:"wears_glasses"`
|
||||
}
|
||||
|
||||
m := NewMapperFunc("db", strings.ToLower)
|
||||
p := Person{1, "Jason", true}
|
||||
mapping := m.TypeMap(reflect.TypeOf(p))
|
||||
|
||||
for _, key := range []string{"id", "name", "wears_glasses"} {
|
||||
if fi := mapping.GetByPath(key); fi == nil {
|
||||
t.Errorf("Expecting to find key %s in mapping but did not.", key)
|
||||
}
|
||||
}
|
||||
|
||||
type SportsPerson struct {
|
||||
Weight int
|
||||
Age int
|
||||
Person
|
||||
}
|
||||
s := SportsPerson{Weight: 100, Age: 30, Person: p}
|
||||
mapping = m.TypeMap(reflect.TypeOf(s))
|
||||
for _, key := range []string{"id", "name", "wears_glasses", "weight", "age"} {
|
||||
if fi := mapping.GetByPath(key); fi == nil {
|
||||
t.Errorf("Expecting to find key %s in mapping but did not.", key)
|
||||
}
|
||||
}
|
||||
|
||||
type RugbyPlayer struct {
|
||||
Position int
|
||||
IsIntense bool `db:"is_intense"`
|
||||
IsAllBlack bool `db:"-"`
|
||||
SportsPerson
|
||||
}
|
||||
r := RugbyPlayer{12, true, false, s}
|
||||
mapping = m.TypeMap(reflect.TypeOf(r))
|
||||
for _, key := range []string{"id", "name", "wears_glasses", "weight", "age", "position", "is_intense"} {
|
||||
if fi := mapping.GetByPath(key); fi == nil {
|
||||
t.Errorf("Expecting to find key %s in mapping but did not.", key)
|
||||
}
|
||||
}
|
||||
|
||||
if fi := mapping.GetByPath("isallblack"); fi != nil {
|
||||
t.Errorf("Expecting to ignore `IsAllBlack` field")
|
||||
}
|
||||
}
|
||||
|
||||
type E1 struct {
|
||||
A int
|
||||
}
|
||||
type E2 struct {
|
||||
E1
|
||||
B int
|
||||
}
|
||||
type E3 struct {
|
||||
E2
|
||||
C int
|
||||
}
|
||||
type E4 struct {
|
||||
E3
|
||||
D int
|
||||
}
|
||||
|
||||
func BenchmarkFieldNameL1(b *testing.B) {
|
||||
e4 := E4{D: 1}
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := reflect.ValueOf(e4)
|
||||
f := v.FieldByName("D")
|
||||
if f.Interface().(int) != 1 {
|
||||
b.Fatal("Wrong value.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFieldNameL4(b *testing.B) {
|
||||
e4 := E4{}
|
||||
e4.A = 1
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := reflect.ValueOf(e4)
|
||||
f := v.FieldByName("A")
|
||||
if f.Interface().(int) != 1 {
|
||||
b.Fatal("Wrong value.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFieldPosL1(b *testing.B) {
|
||||
e4 := E4{D: 1}
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := reflect.ValueOf(e4)
|
||||
f := v.Field(1)
|
||||
if f.Interface().(int) != 1 {
|
||||
b.Fatal("Wrong value.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFieldPosL4(b *testing.B) {
|
||||
e4 := E4{}
|
||||
e4.A = 1
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := reflect.ValueOf(e4)
|
||||
f := v.Field(0)
|
||||
f = f.Field(0)
|
||||
f = f.Field(0)
|
||||
f = f.Field(0)
|
||||
if f.Interface().(int) != 1 {
|
||||
b.Fatal("Wrong value.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFieldByIndexL4(b *testing.B) {
|
||||
e4 := E4{}
|
||||
e4.A = 1
|
||||
idx := []int{0, 0, 0, 0}
|
||||
for i := 0; i < b.N; i++ {
|
||||
v := reflect.ValueOf(e4)
|
||||
f := FieldByIndexes(v, idx)
|
||||
if f.Interface().(int) != 1 {
|
||||
b.Fatal("Wrong value.")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue