refactor field list and field value list
This commit is contained in:
parent
bcadfd8b71
commit
a7b031f3b4
207
field.go
207
field.go
@ -51,76 +51,12 @@ func fieldFromType(name string, t reflect.Type) Field {
|
||||
var f Field
|
||||
ft := unpackType(t, pointer|slice)
|
||||
f.typ = scalarType(ft)
|
||||
f.size = scalarSize(ft)
|
||||
f.list = acceptsList(t)
|
||||
f.name = strcase.ToKebab(name)
|
||||
f.path = []string{name}
|
||||
return f
|
||||
}
|
||||
|
||||
func nilField() Field {
|
||||
var fi Field
|
||||
fi.typ = Any
|
||||
return fi
|
||||
}
|
||||
|
||||
func fieldFromValue(v reflect.Value) Field {
|
||||
var fi Field
|
||||
fi.value = v.Interface()
|
||||
fi.typ = scalarType(v.Type())
|
||||
fi.size = valueSize(v)
|
||||
return fi
|
||||
}
|
||||
|
||||
func fields(t reflect.Type) []Field {
|
||||
t = unpackType(t, pointer)
|
||||
list := t.Kind() == reflect.Slice
|
||||
t = unpackType(t, pointer|slice)
|
||||
if !isStruct(t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var f []Field
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
tfi := t.Field(i)
|
||||
if !exported(tfi.Name) && !tfi.Anonymous {
|
||||
continue
|
||||
}
|
||||
|
||||
if acceptsScalar(tfi.Type) {
|
||||
fi := fieldFromType(tfi.Name, tfi.Type)
|
||||
f = append(f, fi)
|
||||
continue
|
||||
}
|
||||
|
||||
ffi := fields(tfi.Type)
|
||||
if !tfi.Anonymous {
|
||||
for i := range ffi {
|
||||
ffi[i].name = fmt.Sprintf(
|
||||
"%s-%s",
|
||||
strcase.ToKebab(tfi.Name),
|
||||
ffi[i].name,
|
||||
)
|
||||
|
||||
ffi[i].path = append(
|
||||
[]string{tfi.Name},
|
||||
ffi[i].path...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
f = append(f, ffi...)
|
||||
}
|
||||
|
||||
if list {
|
||||
for i := range f {
|
||||
f[i].list = true
|
||||
}
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func prependFieldName(name string, f []Field) {
|
||||
for i := range f {
|
||||
if f[i].name == "" {
|
||||
@ -134,19 +70,74 @@ func prependFieldName(name string, f []Field) {
|
||||
}
|
||||
}
|
||||
|
||||
func fields(t reflect.Type) []Field {
|
||||
t = unpackType(t, pointer|slice)
|
||||
if !isStruct(t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var f []Field
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
fi := t.Field(i)
|
||||
if !exported(fi.Name) && !fi.Anonymous {
|
||||
continue
|
||||
}
|
||||
|
||||
if acceptsScalar(fi.Type) {
|
||||
fi := fieldFromType(fi.Name, fi.Type)
|
||||
f = append(f, fi)
|
||||
continue
|
||||
}
|
||||
|
||||
ffi := fields(fi.Type)
|
||||
if !fi.Anonymous {
|
||||
prependFieldName(fi.Name, ffi)
|
||||
}
|
||||
|
||||
if acceptsList(fi.Type) {
|
||||
for i := range ffi {
|
||||
ffi[i].list = true
|
||||
}
|
||||
}
|
||||
|
||||
f = append(f, ffi...)
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func freeFields(f []Field) {
|
||||
for i := range f {
|
||||
f[i].free = true
|
||||
}
|
||||
}
|
||||
|
||||
func scalarFieldValues(name string, t reflect.Type, v reflect.Value) []Field {
|
||||
var f []Field
|
||||
vv := unpackAllValues(v)
|
||||
for _, vvi := range vv {
|
||||
if !vvi.IsValid() || !isScalar(vvi.Type()) {
|
||||
continue
|
||||
}
|
||||
|
||||
fi := fieldFromType(name, t)
|
||||
fi.value = vvi.Interface()
|
||||
f = append(f, fi)
|
||||
}
|
||||
|
||||
if len(f) == 0 {
|
||||
f = append(f, fieldFromType(name, t))
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func scalarMapFields(v reflect.Value) []Field {
|
||||
var f []Field
|
||||
for _, key := range v.MapKeys() {
|
||||
name := key.Interface().(string)
|
||||
value := v.MapIndex(key)
|
||||
fk := fieldValues(value)
|
||||
prependFieldName(name, fk)
|
||||
fk := scalarFieldValues(name, v.Type().Elem(), value)
|
||||
freeFields(fk)
|
||||
f = append(f, fk...)
|
||||
}
|
||||
@ -154,55 +145,60 @@ func scalarMapFields(v reflect.Value) []Field {
|
||||
return f
|
||||
}
|
||||
|
||||
func fieldValues(v reflect.Value) []Field {
|
||||
if !v.IsValid() {
|
||||
return []Field{nilField()}
|
||||
}
|
||||
|
||||
func structFields(v reflect.Value) []Field {
|
||||
var f []Field
|
||||
v = unpackValue(v, pointer|anytype|iface)
|
||||
t := v.Type()
|
||||
if isScalar(t) {
|
||||
return []Field{fieldFromValue(v)}
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Slice {
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
f = append(f, fieldValues(v.Index(i))...)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
fi := t.Field(i)
|
||||
if !exported(fi.Name) && !fi.Anonymous {
|
||||
continue
|
||||
}
|
||||
|
||||
return f
|
||||
vi := v.Field(i)
|
||||
if acceptsScalar(fi.Type) {
|
||||
f = append(f, scalarFieldValues(fi.Name, fi.Type, vi)...)
|
||||
continue
|
||||
}
|
||||
|
||||
ffi := fieldValues(vi)
|
||||
if !fi.Anonymous {
|
||||
prependFieldName(fi.Name, ffi)
|
||||
}
|
||||
|
||||
if acceptsList(fi.Type) {
|
||||
for i := range ffi {
|
||||
ffi[i].list = true
|
||||
}
|
||||
}
|
||||
|
||||
f = append(f, ffi...)
|
||||
}
|
||||
|
||||
if isScalarMap(t) {
|
||||
return scalarMapFields(v)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
if !isStruct(t) {
|
||||
func fieldValues(v reflect.Value) []Field {
|
||||
if !v.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
tfi := t.Field(i)
|
||||
if !exported(tfi.Name) && !tfi.Anonymous {
|
||||
var values []reflect.Value
|
||||
allValues := unpackAllValues(v)
|
||||
for _, vi := range allValues {
|
||||
if acceptsFields(vi.Type()) {
|
||||
values = append(values, vi)
|
||||
}
|
||||
}
|
||||
|
||||
var f []Field
|
||||
for _, vi := range values {
|
||||
ti := vi.Type()
|
||||
if isScalarMap(ti) {
|
||||
f = append(f, scalarMapFields(vi)...)
|
||||
continue
|
||||
}
|
||||
|
||||
vfi := v.Field(i)
|
||||
if tfi.Anonymous && !vfi.CanAddr() {
|
||||
continue
|
||||
}
|
||||
|
||||
vfi = unpackValue(vfi, pointer|iface|anytype)
|
||||
switch {
|
||||
case tfi.Anonymous:
|
||||
ff := fieldValues(vfi)
|
||||
f = append(f, ff...)
|
||||
default:
|
||||
ff := fieldValues(vfi)
|
||||
prependFieldName(tfi.Name, ff)
|
||||
f = append(f, ff...)
|
||||
}
|
||||
f = append(f, structFields(vi)...)
|
||||
}
|
||||
|
||||
return f
|
||||
@ -218,11 +214,11 @@ func takeFieldValues(f []Field) []any {
|
||||
}
|
||||
|
||||
func bindScalarField(receiver reflect.Value, values []Field) bool {
|
||||
v := takeFieldValues(values)
|
||||
if !receiver.CanSet() {
|
||||
return bindScalar(receiver, v)
|
||||
return false
|
||||
}
|
||||
|
||||
v := takeFieldValues(values)
|
||||
rv, ok := allocate(receiver.Type(), len(v))
|
||||
if !ok {
|
||||
return false
|
||||
@ -379,7 +375,8 @@ func bindField(receiver reflect.Value, values []Field) bool {
|
||||
}
|
||||
|
||||
if values[0].name == "" && len(values[0].path) == 0 {
|
||||
return bindScalarField(receiver, values)
|
||||
ret := bindScalarField(receiver, values)
|
||||
return ret
|
||||
}
|
||||
|
||||
listReceiver := unpackValue(receiver, pointer|iface|anytype)
|
||||
|
||||
173
field_test.go
173
field_test.go
@ -51,45 +51,47 @@ func TestField(t *testing.T) {
|
||||
Hola []int
|
||||
}
|
||||
|
||||
f := bind.Fields[s2]()
|
||||
if len(f) != 6 {
|
||||
t.Fatal(notation.Sprintwt(f))
|
||||
}
|
||||
t.Run("mixed", func(t *testing.T) {
|
||||
f := bind.Fields[s2]()
|
||||
if len(f) != 6 {
|
||||
t.Fatal(notation.Sprintwt(f))
|
||||
}
|
||||
|
||||
m := make(map[string]bind.Field)
|
||||
for _, fi := range f {
|
||||
m[fi.Name()] = fi
|
||||
}
|
||||
m := make(map[string]bind.Field)
|
||||
for _, fi := range f {
|
||||
m[fi.Name()] = fi
|
||||
}
|
||||
|
||||
fieldOne := m["field-one"]
|
||||
if !slices.Equal(fieldOne.Path(), []string{"FieldOne"}) {
|
||||
t.Fatal(fieldOne.Name())
|
||||
}
|
||||
fieldOne := m["field-one"]
|
||||
if !slices.Equal(fieldOne.Path(), []string{"FieldOne"}) {
|
||||
t.Fatal(fieldOne.Name())
|
||||
}
|
||||
|
||||
fooBar := m["foo-bar"]
|
||||
if !slices.Equal(fooBar.Path(), []string{"FooBar"}) {
|
||||
t.Fatal(fooBar.Name())
|
||||
}
|
||||
fooBar := m["foo-bar"]
|
||||
if !slices.Equal(fooBar.Path(), []string{"FooBar"}) {
|
||||
t.Fatal(fooBar.Name())
|
||||
}
|
||||
|
||||
bazFoo := m["baz-foo"]
|
||||
if !slices.Equal(bazFoo.Path(), []string{"Baz", "Foo"}) {
|
||||
t.Fatal(bazFoo.Name())
|
||||
}
|
||||
bazFoo := m["baz-foo"]
|
||||
if !slices.Equal(bazFoo.Path(), []string{"Baz", "Foo"}) {
|
||||
t.Fatal(bazFoo.Name())
|
||||
}
|
||||
|
||||
quxFoo := m["qux-foo"]
|
||||
if !slices.Equal(quxFoo.Path(), []string{"Qux", "Foo"}) {
|
||||
t.Fatal(quxFoo.Name())
|
||||
}
|
||||
quxFoo := m["qux-foo"]
|
||||
if !slices.Equal(quxFoo.Path(), []string{"Qux", "Foo"}) {
|
||||
t.Fatal(quxFoo.Name())
|
||||
}
|
||||
|
||||
que := m["que"]
|
||||
if !slices.Equal(que.Path(), []string{"Que"}) || que.Type() != bind.Bool {
|
||||
t.Fatal(que.Name())
|
||||
}
|
||||
que := m["que"]
|
||||
if !slices.Equal(que.Path(), []string{"Que"}) || que.Type() != bind.Bool {
|
||||
t.Fatal(que.Name())
|
||||
}
|
||||
|
||||
hola := m["hola"]
|
||||
if !slices.Equal(hola.Path(), []string{"Hola"}) || !hola.List() {
|
||||
t.Fatal(hola.Name())
|
||||
}
|
||||
hola := m["hola"]
|
||||
if !slices.Equal(hola.Path(), []string{"Hola"}) || !hola.List() {
|
||||
t.Fatal(hola.Name())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("cannot have fields", func(t *testing.T) {
|
||||
type i []int
|
||||
@ -109,19 +111,33 @@ func TestField(t *testing.T) {
|
||||
t.Run("list", func(t *testing.T) {
|
||||
type s struct{ Foo int }
|
||||
f := bind.Fields[[]s]()
|
||||
if len(f) != 1 || !f[0].List() {
|
||||
if len(f) != 1 || f[0].List() {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("anonymous fields", func(t *testing.T) {
|
||||
type s0 struct{ Foo int }
|
||||
type s1 struct {
|
||||
s0
|
||||
Bar int
|
||||
}
|
||||
|
||||
f := bind.Fields[s1]()
|
||||
if len(f) != 2 || f[0].Name() != "foo" || f[1].Name() != "bar" {
|
||||
t.Fatal(notation.Sprint(f))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("field values", func(t *testing.T) {
|
||||
t.Run("has circular reference", func(t *testing.T) {
|
||||
type s struct{ Foo any }
|
||||
var v s
|
||||
v.Foo = v
|
||||
if len(bind.FieldValues(v)) != 0 {
|
||||
t.Fatal()
|
||||
v.Foo = &v
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 0 {
|
||||
t.Fatal(notation.Sprint(f))
|
||||
}
|
||||
})
|
||||
|
||||
@ -158,7 +174,7 @@ func TestField(t *testing.T) {
|
||||
t.Run("not a struct", func(t *testing.T) {
|
||||
v := []int{21, 42, 84}
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 3 || f[0].Value() != 21 || f[1].Value() != 42 || f[2].Value() != 84 {
|
||||
if len(f) != 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
@ -217,7 +233,7 @@ func TestField(t *testing.T) {
|
||||
type s struct{ Foo any }
|
||||
v := s{Foo: []any{map[string]int{"foo": 42}}}
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 1 || f[0].Name() != "foo-foo" || f[0].Value() != 42 {
|
||||
if len(f) != 1 || f[0].Name() != "foo" || f[0].Value() != nil {
|
||||
t.Fatal(notation.Sprintwt(f))
|
||||
}
|
||||
})
|
||||
@ -226,9 +242,8 @@ func TestField(t *testing.T) {
|
||||
type s struct{ Foo any }
|
||||
v := s{Foo: []any{s{Foo: 21}, s{Foo: 42}}}
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 2 ||
|
||||
f[0].Name() != "foo-foo" || f[0].Value() != 21 ||
|
||||
f[1].Name() != "foo-foo" || f[1].Value() != 42 {
|
||||
if len(f) != 1 ||
|
||||
f[0].Name() != "foo" || f[0].Value() != nil {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
@ -248,12 +263,13 @@ func TestField(t *testing.T) {
|
||||
s0
|
||||
Bar int
|
||||
}
|
||||
|
||||
var v s1
|
||||
v.Foo = 21
|
||||
v.Bar = 42
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 1 || f[0].Name() != "bar" || f[0].Value() != 42 {
|
||||
t.Fatal()
|
||||
if len(f) != 2 || f[0].Name() != "foo" || f[0].Value() != 21 || f[1].Name() != "bar" || f[1].Value() != 42 {
|
||||
t.Fatal(notation.Sprint(f))
|
||||
}
|
||||
})
|
||||
|
||||
@ -273,6 +289,75 @@ func TestField(t *testing.T) {
|
||||
t.Fatal(notation.Sprintwt(f))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("any type field nil", func(t *testing.T) {
|
||||
type s struct{ Foo any }
|
||||
var v s
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 1 || f[0].Name() != "foo" || f[0].Type() != bind.Any {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("any type field not nil", func(t *testing.T) {
|
||||
type s struct{ Foo any }
|
||||
v := s{42}
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 1 || f[0].Name() != "foo" || f[0].Type() != bind.Any || f[0].Value() != 42 {
|
||||
t.Fatal(notation.Sprintt(v))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice field empty", func(t *testing.T) {
|
||||
type s struct{ Foo []int }
|
||||
var v s
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 1 || f[0].Name() != "foo" || !f[0].List() || f[0].Type() != bind.Int || f[0].Value() != nil {
|
||||
t.Fatal(notation.Sprint(f))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice field not empty", func(t *testing.T) {
|
||||
type s struct{ Foo []int }
|
||||
v := s{[]int{21, 42, 84}}
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 3 ||
|
||||
f[0].Name() != "foo" || !f[0].List() || f[0].Type() != bind.Int || f[0].Value() != 21 ||
|
||||
f[1].Name() != "foo" || !f[1].List() || f[1].Type() != bind.Int || f[1].Value() != 42 ||
|
||||
f[2].Name() != "foo" || !f[2].List() || f[2].Type() != bind.Int || f[2].Value() != 84 {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice field with any type empty", func(t *testing.T) {
|
||||
type s struct{ Foo []any }
|
||||
var v s
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 1 || f[0].Name() != "foo" || !f[0].List() || f[0].Type() != bind.Any || f[0].Value() != nil {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slice field with any type not empty", func(t *testing.T) {
|
||||
type s struct{ Foo []any }
|
||||
v := s{[]any{21, 42, 84}}
|
||||
f := bind.FieldValues(v)
|
||||
if len(f) != 3 ||
|
||||
f[0].Name() != "foo" || !f[0].List() || f[0].Type() != bind.Any || f[0].Value() != 21 ||
|
||||
f[1].Name() != "foo" || !f[1].List() || f[1].Type() != bind.Any || f[1].Value() != 42 ||
|
||||
f[2].Name() != "foo" || !f[2].List() || f[2].Type() != bind.Any || f[2].Value() != 84 {
|
||||
t.Fatal(notation.Sprintw(f))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("struct in its own field via interface", func(t *testing.T) {
|
||||
var s struct{ F any }
|
||||
s.F = s
|
||||
f := bind.FieldValues(s)
|
||||
if len(f) != 1 || f[0].Name() != "f" || f[0].Value() != nil {
|
||||
t.Fatal(notation.Sprint(f))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("bind fields", func(t *testing.T) {
|
||||
@ -375,7 +460,7 @@ func TestField(t *testing.T) {
|
||||
len(v.Foo[0].Bar) != 1 || v.Foo[0].Bar[0] != 21 ||
|
||||
len(v.Foo[1].Bar) != 1 || v.Foo[1].Bar[0] != 42 ||
|
||||
len(v.Foo[2].Bar) != 1 || v.Foo[2].Bar[0] != 84 {
|
||||
t.Fatal()
|
||||
t.Fatal(notation.Sprintw(v))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
81
lib.go
81
lib.go
@ -1,18 +1,30 @@
|
||||
// Package bind provides functions to work with flattened lists of structure fields.
|
||||
package bind
|
||||
|
||||
// Scalar defines scalar types that can be used as field values and are not traversed.
|
||||
type Scalar int
|
||||
import "reflect"
|
||||
|
||||
// Field type represents the possible types of a field.
|
||||
type FieldType = reflect.Kind
|
||||
|
||||
const (
|
||||
Any Scalar = iota
|
||||
Bool
|
||||
Int
|
||||
Uint
|
||||
Float
|
||||
String
|
||||
Duration
|
||||
Time
|
||||
Bool FieldType = reflect.Bool
|
||||
Int FieldType = reflect.Int
|
||||
Int8 FieldType = reflect.Int8
|
||||
Int16 FieldType = reflect.Int16
|
||||
Int32 FieldType = reflect.Int32
|
||||
Int64 FieldType = reflect.Int64
|
||||
Uint FieldType = reflect.Uint
|
||||
Uint8 FieldType = reflect.Uint8
|
||||
Uint16 FieldType = reflect.Uint16
|
||||
Uint32 FieldType = reflect.Uint32
|
||||
Uint64 FieldType = reflect.Uint64
|
||||
Uintptr FieldType = reflect.Uintptr
|
||||
Float32 FieldType = reflect.Float32
|
||||
Float64 FieldType = reflect.Float64
|
||||
Any FieldType = reflect.Interface
|
||||
Struct FieldType = reflect.Struct
|
||||
Duration FieldType = 0xfffe
|
||||
Time FieldType = 0xffff
|
||||
)
|
||||
|
||||
// Field is a field of structure or a compatible map. It is used as the output of the inspection functions, and
|
||||
@ -22,8 +34,7 @@ type Field struct {
|
||||
path []string
|
||||
name string
|
||||
list bool
|
||||
typ Scalar
|
||||
size int
|
||||
typ reflect.Kind
|
||||
free bool
|
||||
value any
|
||||
}
|
||||
@ -41,6 +52,11 @@ func BindScalarCreate[T any](value ...any) (T, bool) {
|
||||
return bindScalarCreateReflect[T](value)
|
||||
}
|
||||
|
||||
// BindScalarCreate is like BindScalar, but it allocates the receiver from the type t.
|
||||
func BindScalarCreateWith(t reflect.Type, value ...any) (reflect.Value, bool) {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
// ValueByPath defines a field for input to BindFields or BindFieldsCreate. It defines the field by its exact
|
||||
// field path in the receiver structure.
|
||||
func ValueByPath(path []string, value any) Field {
|
||||
@ -77,7 +93,7 @@ func (f Field) Name() string {
|
||||
func (f Field) List() bool { return f.list }
|
||||
|
||||
// Type returns the scalar type of the field.
|
||||
func (f Field) Type() Scalar {
|
||||
func (f Field) Type() reflect.Kind {
|
||||
if !f.input {
|
||||
return f.typ
|
||||
}
|
||||
@ -85,15 +101,6 @@ func (f Field) Type() Scalar {
|
||||
return scalarTypeReflect(f.value)
|
||||
}
|
||||
|
||||
// Size returns the bitsize of the field.
|
||||
func (f Field) Size() int {
|
||||
if !f.input {
|
||||
return f.size
|
||||
}
|
||||
|
||||
return valueSizeReflect(f.value)
|
||||
}
|
||||
|
||||
// Free indicates that the field was found in a map.
|
||||
func (f Field) Free() bool {
|
||||
return f.free
|
||||
@ -109,6 +116,11 @@ func Fields[T any]() []Field {
|
||||
return fieldsReflect[T]()
|
||||
}
|
||||
|
||||
// Fields of is like Fields but uses type t as the input.
|
||||
func FieldsOf(t reflect.Type) []Field {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FieldValues returns the fields of a structure value recursively. It traverses through pointers, slices and
|
||||
// interfaces.
|
||||
func FieldValues(structure any) []Field {
|
||||
@ -127,22 +139,47 @@ func BindFieldsCreate[T any](values ...Field) (T, []Field) {
|
||||
return bindFieldsCreateReflect[T](values)
|
||||
}
|
||||
|
||||
// BindFieldsCreate is like BindFields, but it allocates the receiver from type t.
|
||||
func BindFieldsCreateWith(t reflect.Type, values ...Field) (reflect.Value, []Field) {
|
||||
return reflect.Value{}, nil
|
||||
}
|
||||
|
||||
// AcceptsScalar checks if a type can be used with BindScalarCreate or the values of the type with BindScalar.
|
||||
func AcceptsScalar[T any]() bool {
|
||||
return acceptsScalarReflect[T]()
|
||||
}
|
||||
|
||||
// TypeAcceptsScalar is like AcceptsScalar, but uses type t as input.
|
||||
func TypeAcceptsScalar(t reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AcceptsFields checks if a type can be used with BindFieldsCreate or the values of the type with BindFields.
|
||||
func AcceptsFields[T any]() bool {
|
||||
return acceptsFieldsReflect[T]()
|
||||
}
|
||||
|
||||
// TypeAcceptsFields is like AcceptsFields, but uses type t as input.
|
||||
func TypeAcceptsFields(t reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AcceptsList checks if a type can be used to bind multiple values.
|
||||
func AcceptsList[T any]() bool {
|
||||
return acceptsListReflect[T]()
|
||||
}
|
||||
|
||||
// TypeAcceptsList is like AcceptsList, but uses type t as input.
|
||||
func TypeAcceptsList(t reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Bindable is the same as AcceptsScalar[T]() || AcceptsFields[T]().
|
||||
func Bindable[T any]() bool {
|
||||
return bindableReflect[T]()
|
||||
}
|
||||
|
||||
// BindableType is like Bindable, but uses type t as input.
|
||||
func BindableType(t reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
12
scalar.go
12
scalar.go
@ -17,23 +17,23 @@ func bindScalar(receiver reflect.Value, values []any) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
r := reflect.ValueOf(values[0])
|
||||
r = unpackValue(r, pointer|iface|slice)
|
||||
if !r.IsValid() && !isAny(receiver.Type()) {
|
||||
v := reflect.ValueOf(values[0])
|
||||
v = unpackValue(v, pointer|iface)
|
||||
if !v.IsValid() && !isAny(receiver.Type()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !r.IsValid() {
|
||||
if !v.IsValid() {
|
||||
receiver.Set(reflect.Zero(receiver.Type()))
|
||||
return true
|
||||
}
|
||||
|
||||
v, ok := scan(receiver.Type(), r.Interface())
|
||||
vv, ok := scan(receiver.Type(), v.Interface())
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
receiver.Set(reflect.ValueOf(v))
|
||||
receiver.Set(reflect.ValueOf(vv))
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ func TestScalar(t *testing.T) {
|
||||
|
||||
t.Run("cannot scan", func(t *testing.T) {
|
||||
var i int
|
||||
if bind.BindScalar(i, "foo") {
|
||||
if bind.BindScalar(&i, "foo") {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
2
scan.go
2
scan.go
@ -90,7 +90,7 @@ func scanString(t reflect.Type, s string) (any, bool) {
|
||||
v, err = time.ParseDuration(s)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if !isDuration(t) || err != nil {
|
||||
v, err = parseInt(s, int(t.Size()))
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
|
||||
@ -115,9 +115,10 @@ func TestScan(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("duration convert", func(t *testing.T) {
|
||||
// not supported, otherwise any integer field would become convertible:
|
||||
type dur time.Duration
|
||||
var d dur
|
||||
if !bind.BindScalar(&d, "9s") || d != dur(9*time.Second) {
|
||||
if bind.BindScalar(&d, "9s") {
|
||||
t.Fatal(d)
|
||||
}
|
||||
})
|
||||
|
||||
63
type.go
63
type.go
@ -20,7 +20,7 @@ func (f unpackFlag) has(v unpackFlag) bool {
|
||||
return f&v > 0
|
||||
}
|
||||
|
||||
func scalarType(t reflect.Type) Scalar {
|
||||
func scalarType(t reflect.Type) reflect.Kind {
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return Bool
|
||||
@ -29,13 +29,13 @@ func scalarType(t reflect.Type) Scalar {
|
||||
return Duration
|
||||
}
|
||||
|
||||
return Int
|
||||
return FieldType(t.Kind())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return Uint
|
||||
return FieldType(t.Kind())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return Float
|
||||
return FieldType(t.Kind())
|
||||
case reflect.String:
|
||||
return String
|
||||
return FieldType(t.Kind())
|
||||
default:
|
||||
if isTime(t) {
|
||||
return Time
|
||||
@ -45,41 +45,20 @@ func scalarType(t reflect.Type) Scalar {
|
||||
}
|
||||
}
|
||||
|
||||
func scalarTypeReflect(v any) Scalar {
|
||||
func scalarTypeReflect(v any) FieldType {
|
||||
t := reflect.TypeOf(v)
|
||||
if t == nil {
|
||||
return Any
|
||||
}
|
||||
|
||||
if hasCircularType(t) {
|
||||
return Any
|
||||
}
|
||||
|
||||
t = unpackType(t, pointer|slice)
|
||||
return scalarType(t)
|
||||
}
|
||||
|
||||
func scalarSize(t reflect.Type) int {
|
||||
return int(t.Size()) * 8
|
||||
}
|
||||
|
||||
func valueSize(v reflect.Value) int {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
return v.Len() * 8
|
||||
case reflect.Interface:
|
||||
return valueSize(unpackValue(v, pointer|slice|iface|anytype))
|
||||
case reflect.Invalid:
|
||||
return 0
|
||||
default:
|
||||
return scalarSize(v.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func valueSizeReflect(v any) int {
|
||||
r := reflect.ValueOf(v)
|
||||
if hasCircularReference(r) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return valueSize(r)
|
||||
}
|
||||
|
||||
func setVisited[T comparable](visited map[T]bool, k T) map[T]bool {
|
||||
s := make(map[T]bool)
|
||||
for v := range visited {
|
||||
@ -191,7 +170,7 @@ func isTime(t reflect.Type) bool {
|
||||
}
|
||||
|
||||
func isDuration(t reflect.Type) bool {
|
||||
return t.ConvertibleTo(reflect.TypeFor[time.Duration]())
|
||||
return t == reflect.TypeFor[time.Duration]()
|
||||
}
|
||||
|
||||
func isScalar(t reflect.Type) bool {
|
||||
@ -271,6 +250,24 @@ func unpackValue(v reflect.Value, unpack unpackFlag) reflect.Value {
|
||||
return v
|
||||
}
|
||||
|
||||
func unpackAllValues(v reflect.Value) []reflect.Value {
|
||||
if !v.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
v = unpackValue(v, pointer|iface|anytype)
|
||||
if v.Kind() != reflect.Slice {
|
||||
return []reflect.Value{v}
|
||||
}
|
||||
|
||||
var all []reflect.Value
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
all = append(all, unpackAllValues(v.Index(i))...)
|
||||
}
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
func acceptsScalar(t reflect.Type) bool {
|
||||
t = unpackType(t, pointer|slice)
|
||||
return isAny(t) || isScalar(t)
|
||||
|
||||
@ -349,14 +349,6 @@ func TestTypeChecks(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("via struct field and interface", func(t *testing.T) {
|
||||
var s struct{ F any }
|
||||
s.F = s
|
||||
if len(bind.FieldValues(s)) > 0 {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("value with circular type", func(t *testing.T) {
|
||||
type p *p
|
||||
var v p
|
||||
|
||||
Loading…
Reference in New Issue
Block a user