documentation
This commit is contained in:
parent
cfa9b6e6f7
commit
9e0dcd6677
93
field.go
93
field.go
@ -47,6 +47,31 @@ func hasPath(f Field) bool {
|
||||
return len(f.path) > 0
|
||||
}
|
||||
|
||||
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
|
||||
@ -63,13 +88,7 @@ func fields(t reflect.Type) []Field {
|
||||
}
|
||||
|
||||
if acceptsScalar(tfi.Type) {
|
||||
var fi Field
|
||||
ft := unpackType(tfi.Type, pointer|slice)
|
||||
fi.typ = scalarType(ft.Kind())
|
||||
fi.size = scalarSize(ft.Kind())
|
||||
fi.list = acceptsList(tfi.Type)
|
||||
fi.name = strcase.ToKebab(tfi.Name)
|
||||
fi.path = []string{tfi.Name}
|
||||
fi := fieldFromType(tfi.Name, tfi.Type)
|
||||
f = append(f, fi)
|
||||
continue
|
||||
}
|
||||
@ -102,14 +121,6 @@ func fields(t reflect.Type) []Field {
|
||||
return f
|
||||
}
|
||||
|
||||
func fieldFromValue(v reflect.Value) Field {
|
||||
var fi Field
|
||||
fi.value = v.Interface()
|
||||
fi.typ = scalarType(v.Kind())
|
||||
fi.size = valueSize(v)
|
||||
return fi
|
||||
}
|
||||
|
||||
func prependFieldName(name string, f []Field) {
|
||||
for i := range f {
|
||||
if f[i].name == "" {
|
||||
@ -144,6 +155,10 @@ func scalarMapFields(v reflect.Value) []Field {
|
||||
}
|
||||
|
||||
func fieldValues(v reflect.Value) []Field {
|
||||
if !v.IsValid() {
|
||||
return []Field{nilField()}
|
||||
}
|
||||
|
||||
var f []Field
|
||||
v = unpackValue(v, pointer|anytype|iface)
|
||||
t := v.Type()
|
||||
@ -287,7 +302,6 @@ func bindMapField(receiver reflect.Value, values []Field) bool {
|
||||
}
|
||||
|
||||
func trimNameAndPath(name string, values []Field) []Field {
|
||||
name = strcase.ToKebab(name)
|
||||
v := make([]Field, len(values))
|
||||
copy(v, values)
|
||||
for i := range v {
|
||||
@ -308,7 +322,11 @@ func trimNameAndPath(name string, values []Field) []Field {
|
||||
}
|
||||
|
||||
func bindStructField(receiver reflect.Value, values []Field) bool {
|
||||
var name, pathName string
|
||||
var (
|
||||
name, pathName string
|
||||
bound bool
|
||||
)
|
||||
|
||||
fp, nfp := filterFields(hasPath, values)
|
||||
if len(fp) > 0 {
|
||||
pathName = fp[0].path[0]
|
||||
@ -326,15 +344,15 @@ func bindStructField(receiver reflect.Value, values []Field) bool {
|
||||
}
|
||||
|
||||
if sf.Name == pathName {
|
||||
values = trimNameAndPath(pathName, values)
|
||||
return bindField(receiver.Field(i), values)
|
||||
b := bindField(receiver.Field(i), trimNameAndPath(pathName, values))
|
||||
bound = bound || b
|
||||
continue
|
||||
}
|
||||
|
||||
sfn := strcase.ToKebab(sf.Name)
|
||||
if name == sfn ||
|
||||
strings.HasPrefix(name, fmt.Sprintf("%s-", sfn)) {
|
||||
values = trimNameAndPath(sfn, values)
|
||||
return bindField(receiver.Field(i), values)
|
||||
if name == sfn || strings.HasPrefix(name, fmt.Sprintf("%s-", sfn)) {
|
||||
b := bindField(receiver.Field(i), trimNameAndPath(sfn, values))
|
||||
bound = bound || b
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,15 +362,18 @@ func bindStructField(receiver reflect.Value, values []Field) bool {
|
||||
continue
|
||||
}
|
||||
|
||||
if bindField(receiver.Field(i), values) {
|
||||
return true
|
||||
}
|
||||
b := bindField(receiver.Field(i), values)
|
||||
bound = bound || b
|
||||
}
|
||||
|
||||
return false
|
||||
return bound
|
||||
}
|
||||
|
||||
func bindField(receiver reflect.Value, values []Field) bool {
|
||||
if !receiver.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
if values[0].name == "" && len(values[0].path) == 0 {
|
||||
return bindScalarField(receiver, values)
|
||||
}
|
||||
@ -420,28 +441,38 @@ func fieldValuesReflect(structure any) []Field {
|
||||
}
|
||||
|
||||
func groupFields(f []Field) [][]Field {
|
||||
var pathsOrdered, namesOrdered []string
|
||||
withPath, withoutPath := filterFields(hasPath, f)
|
||||
paths := make(map[string][]Field)
|
||||
for _, ff := range withPath {
|
||||
ps := pathString(ff)
|
||||
if _, set := paths[ps]; !set {
|
||||
pathsOrdered = append(pathsOrdered, ps)
|
||||
}
|
||||
|
||||
paths[ps] = append(paths[ps], ff)
|
||||
}
|
||||
|
||||
names := make(map[string][]Field)
|
||||
for _, ff := range withoutPath {
|
||||
if _, set := names[ff.name]; !set {
|
||||
namesOrdered = append(namesOrdered, ff.name)
|
||||
}
|
||||
|
||||
names[ff.name] = append(names[ff.name], ff)
|
||||
}
|
||||
|
||||
var groups [][]Field
|
||||
for _, group := range paths {
|
||||
for _, pname := range pathsOrdered {
|
||||
group := paths[pname]
|
||||
nfp := nameFromPath(group[0].path)
|
||||
group = append(group, names[nfp]...)
|
||||
delete(names, nfp)
|
||||
groups = append(groups, group)
|
||||
}
|
||||
|
||||
for _, group := range names {
|
||||
groups = append(groups, group)
|
||||
for _, name := range namesOrdered {
|
||||
groups = append(groups, names[name])
|
||||
}
|
||||
|
||||
return groups
|
||||
@ -465,7 +496,7 @@ func bindFieldsReflect(structure any, values []Field) []Field {
|
||||
return values
|
||||
}
|
||||
|
||||
if !acceptsFields(receiver.Type()) {
|
||||
if !receiver.IsValid() || !acceptsFields(receiver.Type()) {
|
||||
return values
|
||||
}
|
||||
|
||||
|
||||
109
field_test.go
109
field_test.go
@ -5,10 +5,32 @@ import (
|
||||
"code.squareroundforest.org/arpio/notation"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestField(t *testing.T) {
|
||||
sortFields := func(f []bind.Field, p ...func(int, int) bool) {
|
||||
sort.Slice(f, func(i, j int) bool {
|
||||
pi, pj := strings.Join(f[i].Path(), ":"), strings.Join(f[j].Path(), ":")
|
||||
if pi < pj {
|
||||
return true
|
||||
}
|
||||
|
||||
if f[i].Name() < f[j].Name() {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, pi := range p {
|
||||
if pi(i, j) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("fields", func(t *testing.T) {
|
||||
type s0 struct {
|
||||
FieldOne int
|
||||
@ -113,44 +135,22 @@ func TestField(t *testing.T) {
|
||||
|
||||
t.Run("scalar map", func(t *testing.T) {
|
||||
f := bind.FieldValues(map[string]int{"foo": 21, "bar": 42})
|
||||
sort.Slice(f, func(i, j int) bool {
|
||||
if f[i].Name() > f[j].Name() {
|
||||
return true
|
||||
}
|
||||
|
||||
if f[i].Value().(int) < f[j].Value().(int) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
sortFields(f)
|
||||
if len(f) != 2 ||
|
||||
f[0].Name() != "foo" || f[0].Value() != 21 || !f[0].Free() ||
|
||||
f[1].Name() != "bar" || f[1].Value() != 42 || !f[1].Free() {
|
||||
f[0].Name() != "bar" || f[0].Value() != 42 || !f[0].Free() ||
|
||||
f[1].Name() != "foo" || f[1].Value() != 21 || !f[1].Free() {
|
||||
t.Fatal(notation.Sprint(f))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("scalar map with list values", func(t *testing.T) {
|
||||
f := bind.FieldValues(map[string][]int{"foo": []int{21, 36}, "bar": []int{42, 72}})
|
||||
sort.Slice(f, func(i, j int) bool {
|
||||
if f[i].Name() > f[j].Name() {
|
||||
return true
|
||||
}
|
||||
|
||||
if f[i].Value().(int) < f[j].Value().(int) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
sortFields(f)
|
||||
if len(f) != 4 ||
|
||||
f[0].Name() != "foo" || f[0].Value() != 21 || !f[0].Free() ||
|
||||
f[1].Name() != "foo" || f[1].Value() != 36 || !f[1].Free() ||
|
||||
f[2].Name() != "bar" || f[2].Value() != 42 || !f[2].Free() ||
|
||||
f[3].Name() != "bar" || f[3].Value() != 72 || !f[3].Free() {
|
||||
f[0].Name() != "bar" || f[0].Value() != 42 || !f[0].Free() ||
|
||||
f[1].Name() != "bar" || f[1].Value() != 72 || !f[1].Free() ||
|
||||
f[2].Name() != "foo" || f[2].Value() != 21 || !f[2].Free() ||
|
||||
f[3].Name() != "foo" || f[3].Value() != 36 || !f[3].Free() {
|
||||
t.Fatal(notation.Sprint(f))
|
||||
}
|
||||
})
|
||||
@ -614,6 +614,7 @@ func TestField(t *testing.T) {
|
||||
Bar *struct{ Baz *string }
|
||||
Qux *[]struct{ Quux string }
|
||||
}
|
||||
|
||||
var v s
|
||||
u := bind.BindFields(
|
||||
&v,
|
||||
@ -632,6 +633,7 @@ func TestField(t *testing.T) {
|
||||
Foo *int
|
||||
Bar *chan int
|
||||
}
|
||||
|
||||
var v s
|
||||
u := bind.BindFields(
|
||||
&v,
|
||||
@ -640,8 +642,9 @@ func TestField(t *testing.T) {
|
||||
bind.NamedValue("bar-baz", "corge"),
|
||||
)
|
||||
|
||||
sortFields(u)
|
||||
if len(u) != 2 || u[0].Name() != "bar" || u[1].Name() != "bar-baz" || *v.Foo != 42 {
|
||||
t.Fatal()
|
||||
t.Fatal(notation.Sprint(u), notation.Sprint(v))
|
||||
}
|
||||
})
|
||||
|
||||
@ -700,6 +703,43 @@ func TestField(t *testing.T) {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty receiver", func(t *testing.T) {
|
||||
var v any
|
||||
u := bind.BindFields(&v, bind.NamedValue("foo", 42))
|
||||
if len(u) != 1 {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil value", func(t *testing.T) {
|
||||
type s struct{ Foo any }
|
||||
v := s{42}
|
||||
u := bind.BindFields(&v, bind.NamedValue("foo", nil))
|
||||
if len(u) != 0 || v.Foo != nil {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil receiver", func(t *testing.T) {
|
||||
u := bind.BindFields(nil, bind.NamedValue("foo", nil))
|
||||
if len(u) != 1 {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ambigously named fields", func(t *testing.T) {
|
||||
type s struct {
|
||||
FooBar int
|
||||
Foo struct{ Bar int }
|
||||
}
|
||||
|
||||
var v s
|
||||
u := bind.BindFields(&v, bind.NamedValue("foo-bar", 42))
|
||||
if len(u) != 0 || v.FooBar != 42 || v.Foo.Bar != 42 {
|
||||
t.Fatal(notation.Sprint(u), notation.Sprint(v))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("bind fields create", func(t *testing.T) {
|
||||
@ -736,5 +776,12 @@ func TestField(t *testing.T) {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("any type", func(t *testing.T) {
|
||||
_, u := bind.BindFieldsCreate[any](bind.NamedValue("foo", 42))
|
||||
if len(u) != 1 {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
79
lib.go
79
lib.go
@ -1,10 +1,7 @@
|
||||
// provides more flexible and permissive ways of setting values than reflect.Value.Set
|
||||
// circular type structures not supported
|
||||
// it handles scalar fields
|
||||
// primary use cases by design are: command line options, environment variables, ini file fields, URL query parameters, HTTP form values
|
||||
// traverses pointers, slices, wrapper interfaces
|
||||
// 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
|
||||
|
||||
const (
|
||||
@ -14,9 +11,14 @@ const (
|
||||
Uint
|
||||
Float
|
||||
String
|
||||
Duration
|
||||
Time
|
||||
)
|
||||
|
||||
// Field is a field of structure or a compatible map. It is used as the output of the inspection functions, and
|
||||
// as the input of the binding functions.
|
||||
type Field struct {
|
||||
input bool
|
||||
path []string
|
||||
name string
|
||||
list bool
|
||||
@ -26,29 +28,43 @@ type Field struct {
|
||||
value any
|
||||
}
|
||||
|
||||
// the receiver must be addressable
|
||||
// BindScalar sets the receiver to the provided value or values. It traverses the receiver through wrappers like
|
||||
// pointers, slices and interfaces. If the receiver contains a slice which is greater or equal length as the
|
||||
// number of the provided values, N, it sets the first N slice items to the values. It returns true, if the
|
||||
// values were set. It returns false, if the values were not set, e.g. due to incompatible types.
|
||||
func BindScalar(receiver any, value ...any) bool {
|
||||
return bindScalarReflect(receiver, value)
|
||||
}
|
||||
|
||||
// BindScalarCreate is like BindScalar, but it allocates the receiver from the type T.
|
||||
func BindScalarCreate[T any](value ...any) (T, bool) {
|
||||
return bindScalarCreateReflect[T](value)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return Field{path: path, value: value}
|
||||
return Field{input: true, path: path, value: value}
|
||||
}
|
||||
|
||||
// NamedValue defines a field for inptu to BindFields or BindFieldsCreate. The name is the kebab case
|
||||
// representation of the field path. The struct boundaries in the field path are not represented in the name,
|
||||
// which leads to potentially ambigous field references, depending on the receiver type structure and naming. In
|
||||
// case of ambigous fields, the field that can match multiple structure fields, will be bound to each matched
|
||||
// structure field.
|
||||
func NamedValue(name string, value any) Field {
|
||||
return Field{name: name, value: value}
|
||||
return Field{input: true, name: name, value: value}
|
||||
}
|
||||
|
||||
// Path returns the structure path of a field.
|
||||
func (f Field) Path() []string {
|
||||
p := make([]string, len(f.path))
|
||||
copy(p, f.path)
|
||||
return p
|
||||
}
|
||||
|
||||
// Name returns the structure path of a field concatenated and in kebab casing, unless defined otherwise by
|
||||
// NamedValue.
|
||||
func (f Field) Name() string {
|
||||
if f.name != "" || len(f.path) == 0 {
|
||||
return f.name
|
||||
@ -57,43 +73,76 @@ func (f Field) Name() string {
|
||||
return nameFromPath(f.path)
|
||||
}
|
||||
|
||||
// List indicates whether a field is wrapped in a slice and accpets multiple values.
|
||||
func (f Field) List() bool { return f.list }
|
||||
func (f Field) Type() Scalar { return f.typ }
|
||||
func (f Field) Size() int { return f.size }
|
||||
func (f Field) Free() bool { return f.free }
|
||||
func (f Field) Value() any { return f.value }
|
||||
|
||||
// it does not return fields with free keys, however, this should be obvious
|
||||
// non-struct and non-named map values return unnamed fields
|
||||
// Type returns the scalar type of the field.
|
||||
func (f Field) Type() Scalar {
|
||||
if !f.input {
|
||||
return f.typ
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Value returns the value of a field.
|
||||
func (f Field) Value() any {
|
||||
return f.value
|
||||
}
|
||||
|
||||
// Fields returns the fields of a structure type recursively. It traverses through pointers and slices.
|
||||
func Fields[T any]() []Field {
|
||||
return fieldsReflect[T]()
|
||||
}
|
||||
|
||||
// the list and bool flags are not set because it is not possible if they are defined by the root type
|
||||
// FieldValues returns the fields of a structure value recursively. It traverses through pointers, slices and
|
||||
// interfaces.
|
||||
func FieldValues(structure any) []Field {
|
||||
return fieldValuesReflect(structure)
|
||||
}
|
||||
|
||||
// BindFields sets structure fields recursively. It traverses through poitners, slices and interfaces. It
|
||||
// returns the values for which it is not possible to find a compatible matching field. It supports maps that
|
||||
// have string keys and scalar values.
|
||||
func BindFields(structure any, values ...Field) []Field {
|
||||
return bindFieldsReflect(structure, values)
|
||||
}
|
||||
|
||||
// BindFieldsCreate is like BindFields, but it allocates the receiver from type T.
|
||||
func BindFieldsCreate[T any](values ...Field) (T, []Field) {
|
||||
return bindFieldsCreateReflect[T](values)
|
||||
}
|
||||
|
||||
// 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]()
|
||||
}
|
||||
|
||||
// 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]()
|
||||
}
|
||||
|
||||
// AcceptsList checks if a type can be used to bind multiple values.
|
||||
func AcceptsList[T any]() bool {
|
||||
return acceptsListReflect[T]()
|
||||
}
|
||||
|
||||
// Bindable is the same as AcceptsScalar[T]() || AcceptsFields[T]().
|
||||
func Bindable[T any]() bool {
|
||||
return bindableReflect[T]()
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
add the kind to the field type but with time and duration support. Simplified for ints, uints and floats, with bit size. Skipping the unused ones. With different name, e.g. ScalarType
|
||||
track down the cases when reflect can panic
|
||||
documentation:
|
||||
- give a short description for every exported symbol
|
||||
- start from collecting the docs from the test cases
|
||||
- extrace the common doc items from the function doc items
|
||||
doc/test/code triangle
|
||||
7
readme.md
Normal file
7
readme.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Bind
|
||||
|
||||
Package bind provides functions to work with flattened lists of structure fields.
|
||||
|
||||
See documentation at: https://godocs.io/code.squareroundforest.org/arpio/bind
|
||||
|
||||
*Made in Berlin, DE*
|
||||
21
scalar.go
21
scalar.go
@ -3,7 +3,7 @@ package bind
|
||||
import "reflect"
|
||||
|
||||
func bindScalar(receiver reflect.Value, values []any) bool {
|
||||
if len(values) == 0 {
|
||||
if !receiver.IsValid() || len(values) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -13,14 +13,23 @@ func bindScalar(receiver reflect.Value, values []any) bool {
|
||||
|
||||
if len(values) == 1 {
|
||||
receiver = unpackValue(receiver, pointer|iface|slice)
|
||||
r := reflect.ValueOf(values[0])
|
||||
r = unpackValue(r, pointer|iface|slice)
|
||||
v, ok := scan(receiver.Type(), r.Interface())
|
||||
if !ok {
|
||||
if !receiver.CanSet() {
|
||||
return false
|
||||
}
|
||||
|
||||
if !receiver.CanSet() {
|
||||
r := reflect.ValueOf(values[0])
|
||||
r = unpackValue(r, pointer|iface|slice)
|
||||
if !r.IsValid() && !isAny(receiver.Type()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !r.IsValid() {
|
||||
receiver.Set(reflect.Zero(receiver.Type()))
|
||||
return true
|
||||
}
|
||||
|
||||
v, ok := scan(receiver.Type(), r.Interface())
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@ -156,6 +156,20 @@ func TestScalar(t *testing.T) {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil value", func(t *testing.T) {
|
||||
var v any
|
||||
v = 42
|
||||
if !bind.BindScalar(&v, nil) || v != nil {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil receiver", func(t *testing.T) {
|
||||
if bind.BindScalar(nil, 42) {
|
||||
t.Fatal()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("bind scalar with create", func(t *testing.T) {
|
||||
|
||||
4
scan.go
4
scan.go
@ -54,6 +54,10 @@ func parseUint(s string, byteSize int) (uint64, error) {
|
||||
}
|
||||
|
||||
func scanConvert(t reflect.Type, v any) (any, bool) {
|
||||
if isAny(t) {
|
||||
return v, true
|
||||
}
|
||||
|
||||
r := reflect.ValueOf(v)
|
||||
if !r.CanConvert(t) {
|
||||
return nil, false
|
||||
|
||||
61
type.go
61
type.go
@ -20,11 +20,15 @@ func (f unpackFlag) has(v unpackFlag) bool {
|
||||
return f&v > 0
|
||||
}
|
||||
|
||||
func scalarType(k reflect.Kind) Scalar {
|
||||
switch k {
|
||||
func scalarType(t reflect.Type) Scalar {
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return Bool
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if isDuration(t) {
|
||||
return Duration
|
||||
}
|
||||
|
||||
return Int
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return Uint
|
||||
@ -33,43 +37,20 @@ func scalarType(k reflect.Kind) Scalar {
|
||||
case reflect.String:
|
||||
return String
|
||||
default:
|
||||
if isTime(t) {
|
||||
return Time
|
||||
}
|
||||
|
||||
return Any
|
||||
}
|
||||
}
|
||||
|
||||
func scalarSize(k reflect.Kind) int {
|
||||
switch k {
|
||||
case reflect.Bool:
|
||||
return 1
|
||||
case reflect.Int:
|
||||
return int(reflect.TypeFor[int]().Size()) * 8
|
||||
case reflect.Int8:
|
||||
return 8
|
||||
case reflect.Int16:
|
||||
return 16
|
||||
case reflect.Int32:
|
||||
return 32
|
||||
case reflect.Int64:
|
||||
return 64
|
||||
case reflect.Uint:
|
||||
return int(reflect.TypeFor[uint]().Size()) * 8
|
||||
case reflect.Uint8:
|
||||
return 8
|
||||
case reflect.Uint16:
|
||||
return 16
|
||||
case reflect.Uint32:
|
||||
return 32
|
||||
case reflect.Uint64:
|
||||
return 64
|
||||
case reflect.Float32:
|
||||
return 32
|
||||
case reflect.Float64:
|
||||
return 64
|
||||
case reflect.String:
|
||||
return -1
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
func scalarTypeReflect(v any) Scalar {
|
||||
return scalarType(reflect.TypeOf(v))
|
||||
}
|
||||
|
||||
func scalarSize(t reflect.Type) int {
|
||||
return int(t.Size()) * 8
|
||||
}
|
||||
|
||||
func valueSize(v reflect.Value) int {
|
||||
@ -78,11 +59,17 @@ func valueSize(v reflect.Value) int {
|
||||
return v.Len() * 8
|
||||
case reflect.Interface:
|
||||
return valueSize(unpackValue(v, pointer|slice|iface|anytype))
|
||||
case reflect.Invalid:
|
||||
return 0
|
||||
default:
|
||||
return scalarSize(v.Kind())
|
||||
return scalarSize(v.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func valueSizeReflect(v any) int {
|
||||
return valueSize(reflect.ValueOf(v))
|
||||
}
|
||||
|
||||
func setVisited[T comparable](visited map[T]bool, k T) map[T]bool {
|
||||
s := make(map[T]bool)
|
||||
for v := range visited {
|
||||
@ -247,7 +234,7 @@ func unpackType(t reflect.Type, unpack unpackFlag) reflect.Type {
|
||||
}
|
||||
|
||||
func unpackValue(v reflect.Value, unpack unpackFlag) reflect.Value {
|
||||
if v.IsZero() {
|
||||
if !v.IsValid() || v.IsZero() {
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user