mirror of
https://github.com/documize/community.git
synced 2025-07-21 06:09:42 +02:00
still moving codebase to new API (WIP)
This commit is contained in:
parent
72b14def6d
commit
d90b3249c3
44 changed files with 5276 additions and 336 deletions
2
vendor/github.com/jmoiron/sqlx/reflectx/README.md
generated
vendored
2
vendor/github.com/jmoiron/sqlx/reflectx/README.md
generated
vendored
|
@ -12,6 +12,6 @@ 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.
|
||||
tags in the ways that are vital to most marshallers, and they are slow.
|
||||
|
||||
This reflectx package extends reflect to achieve these goals.
|
||||
|
|
159
vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
generated
vendored
159
vendor/github.com/jmoiron/sqlx/reflectx/reflect.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
// Package reflectx implements extensions to the standard reflect lib suitable
|
||||
// for implementing marshaling and unmarshaling packages. The main Mapper type
|
||||
// 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.
|
||||
|
@ -7,14 +7,13 @@
|
|||
package reflectx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A FieldInfo is a collection of metadata about a struct field.
|
||||
// A FieldInfo is metadata for a struct field.
|
||||
type FieldInfo struct {
|
||||
Index []int
|
||||
Path string
|
||||
|
@ -41,7 +40,8 @@ func (f StructMap) GetByPath(path string) *FieldInfo {
|
|||
}
|
||||
|
||||
// GetByTraversal returns a *FieldInfo for a given integer path. It is
|
||||
// analogous to reflect.FieldByIndex.
|
||||
// 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
|
||||
|
@ -58,8 +58,8 @@ func (f StructMap) GetByTraversal(index []int) *FieldInfo {
|
|||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
|
@ -68,8 +68,8 @@ type Mapper struct {
|
|||
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.
|
||||
// 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),
|
||||
|
@ -127,7 +127,7 @@ func (m *Mapper) FieldMap(v reflect.Value) map[string]reflect.Value {
|
|||
return r
|
||||
}
|
||||
|
||||
// FieldByName returns a field by the its mapped name as a reflect.Value.
|
||||
// FieldByName returns a field by 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 {
|
||||
|
@ -182,11 +182,12 @@ func (m *Mapper) TraversalsByName(t reflect.Type, names []string) [][]int {
|
|||
return r
|
||||
}
|
||||
|
||||
// FieldByIndexes returns a value for a particular struct traversal.
|
||||
// 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, it's possible it is nil
|
||||
// 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)
|
||||
|
@ -225,13 +226,12 @@ type kinder interface {
|
|||
// 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 {
|
||||
if k := v.Kind(); k != expected {
|
||||
panic(&reflect.ValueError{Method: methodName(), Kind: k})
|
||||
}
|
||||
}
|
||||
|
||||
// methodName is returns the caller of the function calling methodName
|
||||
// methodName returns the caller of the function calling methodName
|
||||
func methodName() string {
|
||||
pc, _, _, _ := runtime.Caller(2)
|
||||
f := runtime.FuncForPC(pc)
|
||||
|
@ -257,19 +257,92 @@ func apnd(is []int, i int) []int {
|
|||
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 func(string) string) *StructMap {
|
||||
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()
|
||||
|
@ -278,53 +351,31 @@ func getMapping(t reflect.Type, tagName string, mapFunc, tagMapFunc func(string)
|
|||
|
||||
// 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)
|
||||
}
|
||||
// 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
|
||||
|
|
320
vendor/github.com/jmoiron/sqlx/reflectx/reflect_test.go
generated
vendored
320
vendor/github.com/jmoiron/sqlx/reflectx/reflect_test.go
generated
vendored
|
@ -247,11 +247,20 @@ func TestInlineStruct(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRecursiveStruct(t *testing.T) {
|
||||
type Person struct {
|
||||
Parent *Person
|
||||
}
|
||||
m := NewMapperFunc("db", strings.ToLower)
|
||||
var p *Person
|
||||
m.TypeMap(reflect.TypeOf(p))
|
||||
}
|
||||
|
||||
func TestFieldsEmbedded(t *testing.T) {
|
||||
m := NewMapper("db")
|
||||
|
||||
type Person struct {
|
||||
Name string `db:"name"`
|
||||
Name string `db:"name,size=64"`
|
||||
}
|
||||
type Place struct {
|
||||
Name string `db:"name"`
|
||||
|
@ -311,6 +320,9 @@ func TestFieldsEmbedded(t *testing.T) {
|
|||
if fi.Path != "person.name" {
|
||||
t.Errorf("Expecting %s, got %s", "person.name", fi.Path)
|
||||
}
|
||||
if fi.Options["size"] != "64" {
|
||||
t.Errorf("Expecting %s, got %s", "64", fi.Options["size"])
|
||||
}
|
||||
|
||||
fi = fields.GetByTraversal([]int{1, 0})
|
||||
if fi == nil {
|
||||
|
@ -508,6 +520,312 @@ func TestMapping(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetByTraversal(t *testing.T) {
|
||||
type C struct {
|
||||
C0 int
|
||||
C1 int
|
||||
}
|
||||
type B struct {
|
||||
B0 string
|
||||
B1 *C
|
||||
}
|
||||
type A struct {
|
||||
A0 int
|
||||
A1 B
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
Index []int
|
||||
ExpectedName string
|
||||
ExpectNil bool
|
||||
}{
|
||||
{
|
||||
Index: []int{0},
|
||||
ExpectedName: "A0",
|
||||
},
|
||||
{
|
||||
Index: []int{1, 0},
|
||||
ExpectedName: "B0",
|
||||
},
|
||||
{
|
||||
Index: []int{1, 1, 1},
|
||||
ExpectedName: "C1",
|
||||
},
|
||||
{
|
||||
Index: []int{3, 4, 5},
|
||||
ExpectNil: true,
|
||||
},
|
||||
{
|
||||
Index: []int{},
|
||||
ExpectNil: true,
|
||||
},
|
||||
{
|
||||
Index: nil,
|
||||
ExpectNil: true,
|
||||
},
|
||||
}
|
||||
|
||||
m := NewMapperFunc("db", func(n string) string { return n })
|
||||
tm := m.TypeMap(reflect.TypeOf(A{}))
|
||||
|
||||
for i, tc := range testCases {
|
||||
fi := tm.GetByTraversal(tc.Index)
|
||||
if tc.ExpectNil {
|
||||
if fi != nil {
|
||||
t.Errorf("%d: expected nil, got %v", i, fi)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if fi == nil {
|
||||
t.Errorf("%d: expected %s, got nil", i, tc.ExpectedName)
|
||||
continue
|
||||
}
|
||||
|
||||
if fi.Name != tc.ExpectedName {
|
||||
t.Errorf("%d: expected %s, got %s", i, tc.ExpectedName, fi.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMapperMethodsByName tests Mapper methods FieldByName and TraversalsByName
|
||||
func TestMapperMethodsByName(t *testing.T) {
|
||||
type C struct {
|
||||
C0 string
|
||||
C1 int
|
||||
}
|
||||
type B struct {
|
||||
B0 *C `db:"B0"`
|
||||
B1 C `db:"B1"`
|
||||
B2 string `db:"B2"`
|
||||
}
|
||||
type A struct {
|
||||
A0 *B `db:"A0"`
|
||||
B `db:"A1"`
|
||||
A2 int
|
||||
a3 int
|
||||
}
|
||||
|
||||
val := &A{
|
||||
A0: &B{
|
||||
B0: &C{C0: "0", C1: 1},
|
||||
B1: C{C0: "2", C1: 3},
|
||||
B2: "4",
|
||||
},
|
||||
B: B{
|
||||
B0: nil,
|
||||
B1: C{C0: "5", C1: 6},
|
||||
B2: "7",
|
||||
},
|
||||
A2: 8,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
Name string
|
||||
ExpectInvalid bool
|
||||
ExpectedValue interface{}
|
||||
ExpectedIndexes []int
|
||||
}{
|
||||
{
|
||||
Name: "A0.B0.C0",
|
||||
ExpectedValue: "0",
|
||||
ExpectedIndexes: []int{0, 0, 0},
|
||||
},
|
||||
{
|
||||
Name: "A0.B0.C1",
|
||||
ExpectedValue: 1,
|
||||
ExpectedIndexes: []int{0, 0, 1},
|
||||
},
|
||||
{
|
||||
Name: "A0.B1.C0",
|
||||
ExpectedValue: "2",
|
||||
ExpectedIndexes: []int{0, 1, 0},
|
||||
},
|
||||
{
|
||||
Name: "A0.B1.C1",
|
||||
ExpectedValue: 3,
|
||||
ExpectedIndexes: []int{0, 1, 1},
|
||||
},
|
||||
{
|
||||
Name: "A0.B2",
|
||||
ExpectedValue: "4",
|
||||
ExpectedIndexes: []int{0, 2},
|
||||
},
|
||||
{
|
||||
Name: "A1.B0.C0",
|
||||
ExpectedValue: "",
|
||||
ExpectedIndexes: []int{1, 0, 0},
|
||||
},
|
||||
{
|
||||
Name: "A1.B0.C1",
|
||||
ExpectedValue: 0,
|
||||
ExpectedIndexes: []int{1, 0, 1},
|
||||
},
|
||||
{
|
||||
Name: "A1.B1.C0",
|
||||
ExpectedValue: "5",
|
||||
ExpectedIndexes: []int{1, 1, 0},
|
||||
},
|
||||
{
|
||||
Name: "A1.B1.C1",
|
||||
ExpectedValue: 6,
|
||||
ExpectedIndexes: []int{1, 1, 1},
|
||||
},
|
||||
{
|
||||
Name: "A1.B2",
|
||||
ExpectedValue: "7",
|
||||
ExpectedIndexes: []int{1, 2},
|
||||
},
|
||||
{
|
||||
Name: "A2",
|
||||
ExpectedValue: 8,
|
||||
ExpectedIndexes: []int{2},
|
||||
},
|
||||
{
|
||||
Name: "XYZ",
|
||||
ExpectInvalid: true,
|
||||
ExpectedIndexes: []int{},
|
||||
},
|
||||
{
|
||||
Name: "a3",
|
||||
ExpectInvalid: true,
|
||||
ExpectedIndexes: []int{},
|
||||
},
|
||||
}
|
||||
|
||||
// build the names array from the test cases
|
||||
names := make([]string, len(testCases))
|
||||
for i, tc := range testCases {
|
||||
names[i] = tc.Name
|
||||
}
|
||||
m := NewMapperFunc("db", func(n string) string { return n })
|
||||
v := reflect.ValueOf(val)
|
||||
values := m.FieldsByName(v, names)
|
||||
if len(values) != len(testCases) {
|
||||
t.Errorf("expected %d values, got %d", len(testCases), len(values))
|
||||
t.FailNow()
|
||||
}
|
||||
indexes := m.TraversalsByName(v.Type(), names)
|
||||
if len(indexes) != len(testCases) {
|
||||
t.Errorf("expected %d traversals, got %d", len(testCases), len(indexes))
|
||||
t.FailNow()
|
||||
}
|
||||
for i, val := range values {
|
||||
tc := testCases[i]
|
||||
traversal := indexes[i]
|
||||
if !reflect.DeepEqual(tc.ExpectedIndexes, traversal) {
|
||||
t.Errorf("expected %v, got %v", tc.ExpectedIndexes, traversal)
|
||||
t.FailNow()
|
||||
}
|
||||
val = reflect.Indirect(val)
|
||||
if tc.ExpectInvalid {
|
||||
if val.IsValid() {
|
||||
t.Errorf("%d: expected zero value, got %v", i, val)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !val.IsValid() {
|
||||
t.Errorf("%d: expected valid value, got %v", i, val)
|
||||
continue
|
||||
}
|
||||
actualValue := reflect.Indirect(val).Interface()
|
||||
if !reflect.DeepEqual(tc.ExpectedValue, actualValue) {
|
||||
t.Errorf("%d: expected %v, got %v", i, tc.ExpectedValue, actualValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldByIndexes(t *testing.T) {
|
||||
type C struct {
|
||||
C0 bool
|
||||
C1 string
|
||||
C2 int
|
||||
C3 map[string]int
|
||||
}
|
||||
type B struct {
|
||||
B1 C
|
||||
B2 *C
|
||||
}
|
||||
type A struct {
|
||||
A1 B
|
||||
A2 *B
|
||||
}
|
||||
testCases := []struct {
|
||||
value interface{}
|
||||
indexes []int
|
||||
expectedValue interface{}
|
||||
readOnly bool
|
||||
}{
|
||||
{
|
||||
value: A{
|
||||
A1: B{B1: C{C0: true}},
|
||||
},
|
||||
indexes: []int{0, 0, 0},
|
||||
expectedValue: true,
|
||||
readOnly: true,
|
||||
},
|
||||
{
|
||||
value: A{
|
||||
A2: &B{B2: &C{C1: "answer"}},
|
||||
},
|
||||
indexes: []int{1, 1, 1},
|
||||
expectedValue: "answer",
|
||||
readOnly: true,
|
||||
},
|
||||
{
|
||||
value: &A{},
|
||||
indexes: []int{1, 1, 3},
|
||||
expectedValue: map[string]int{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
checkResults := func(v reflect.Value) {
|
||||
if tc.expectedValue == nil {
|
||||
if !v.IsNil() {
|
||||
t.Errorf("%d: expected nil, actual %v", i, v.Interface())
|
||||
}
|
||||
} else {
|
||||
if !reflect.DeepEqual(tc.expectedValue, v.Interface()) {
|
||||
t.Errorf("%d: expected %v, actual %v", i, tc.expectedValue, v.Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkResults(FieldByIndexes(reflect.ValueOf(tc.value), tc.indexes))
|
||||
if tc.readOnly {
|
||||
checkResults(FieldByIndexesReadOnly(reflect.ValueOf(tc.value), tc.indexes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustBe(t *testing.T) {
|
||||
typ := reflect.TypeOf(E1{})
|
||||
mustBe(typ, reflect.Struct)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
valueErr, ok := r.(*reflect.ValueError)
|
||||
if !ok {
|
||||
t.Errorf("unexpected Method: %s", valueErr.Method)
|
||||
t.Error("expected panic with *reflect.ValueError")
|
||||
return
|
||||
}
|
||||
if valueErr.Method != "github.com/jmoiron/sqlx/reflectx.TestMustBe" {
|
||||
}
|
||||
if valueErr.Kind != reflect.String {
|
||||
t.Errorf("unexpected Kind: %s", valueErr.Kind)
|
||||
}
|
||||
} else {
|
||||
t.Error("expected panic")
|
||||
}
|
||||
}()
|
||||
|
||||
typ = reflect.TypeOf("string")
|
||||
mustBe(typ, reflect.Struct)
|
||||
t.Error("got here, didn't expect to")
|
||||
}
|
||||
|
||||
type E1 struct {
|
||||
A int
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue