169 lines
3.2 KiB
Go
169 lines
3.2 KiB
Go
package bind
|
|
|
|
import "reflect"
|
|
|
|
type unpackFlag int
|
|
|
|
const (
|
|
pointer unpackFlag = 1 << iota
|
|
slice
|
|
anytype
|
|
iface
|
|
)
|
|
|
|
func (f unpackFlag) has(v unpackFlag) bool {
|
|
return f&v > 0
|
|
}
|
|
|
|
func isAny(t reflect.Type) bool {
|
|
return t.Kind() == reflect.Interface && t.NumMethod() == 0
|
|
}
|
|
|
|
func isInterface(t reflect.Type) bool {
|
|
return t.Kind() == reflect.Interface && t.NumMethod() > 0
|
|
}
|
|
|
|
func isScalar(t reflect.Type) bool {
|
|
switch t.Kind() {
|
|
case reflect.Bool,
|
|
reflect.Int,
|
|
reflect.Int8,
|
|
reflect.Int16,
|
|
reflect.Int32,
|
|
reflect.Int64,
|
|
reflect.Uint,
|
|
reflect.Uint8,
|
|
reflect.Uint16,
|
|
reflect.Uint32,
|
|
reflect.Uint64,
|
|
reflect.Uintptr,
|
|
reflect.Float32,
|
|
reflect.Float64,
|
|
reflect.String:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isScalarMap(t reflect.Type) bool {
|
|
if t.Kind() != reflect.Map {
|
|
return false
|
|
}
|
|
|
|
key := unpackType(t.Key(), pointer)
|
|
if key.Kind() != reflect.String {
|
|
return false
|
|
}
|
|
|
|
value := unpackType(t.Elem(), pointer|slice)
|
|
return isAny(value) || isScalar(value)
|
|
}
|
|
|
|
func unpackType(t reflect.Type, unpack unpackFlag) reflect.Type {
|
|
if unpack.has(pointer) && t.Kind() == reflect.Pointer {
|
|
return unpackType(t.Elem(), unpack)
|
|
}
|
|
|
|
if unpack.has(slice) && t.Kind() == reflect.Slice {
|
|
return unpackType(t.Elem(), unpack)
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
func unpackValue(v reflect.Value, unpack unpackFlag) reflect.Value {
|
|
if unpack.has(pointer) && v.Kind() == reflect.Pointer {
|
|
return unpackValue(v.Elem(), unpack)
|
|
}
|
|
|
|
if unpack.has(slice) && v.Kind() == reflect.Slice && v.Len() > 0 {
|
|
return unpackValue(v.Index(0), unpack)
|
|
}
|
|
|
|
if unpack.has(anytype) && isAny(v.Type()) && !v.IsNil() {
|
|
return unpackValue(v.Elem(), unpack)
|
|
}
|
|
|
|
if unpack.has(iface) && isInterface(v.Type()) && !v.IsNil() {
|
|
return unpackValue(v.Elem(), unpack)
|
|
}
|
|
|
|
return v
|
|
}
|
|
|
|
func acceptsScalar(t reflect.Type) bool {
|
|
t = unpackType(t, pointer|slice)
|
|
return isAny(t) || isScalar(t)
|
|
}
|
|
|
|
func acceptsFields(t reflect.Type) bool {
|
|
t = unpackType(t, pointer|slice)
|
|
return t.Kind() == reflect.Struct || isScalarMap(t)
|
|
}
|
|
|
|
func acceptsList(t reflect.Type) bool {
|
|
if !bindable(t) {
|
|
return false
|
|
}
|
|
|
|
t = unpackType(t, pointer)
|
|
return t.Kind() == reflect.Slice
|
|
}
|
|
|
|
func bindable(t reflect.Type) bool {
|
|
return acceptsScalar(t) || acceptsFields(t)
|
|
}
|
|
|
|
func acceptsScalarReflect[T any]() bool {
|
|
return acceptsScalar(reflect.TypeFor[T]())
|
|
}
|
|
|
|
func acceptsFieldsReflect[T any]() bool {
|
|
return acceptsFields(reflect.TypeFor[T]())
|
|
}
|
|
|
|
func acceptsListReflect[T any]() bool {
|
|
return acceptsList(reflect.TypeFor[T]())
|
|
}
|
|
|
|
func bindableReflect[T any]() bool {
|
|
return bindable(reflect.TypeFor[T]())
|
|
}
|
|
|
|
// expected to be called with types that can pass the bindable check
|
|
func allocate(t reflect.Type, len int) (reflect.Value, bool) {
|
|
if len == 0 {
|
|
return reflect.Zero(t), false
|
|
}
|
|
|
|
if isAny(t) || isScalar(t) || t.Kind() == reflect.Struct || isScalarMap(t) {
|
|
p := reflect.New(t)
|
|
return p.Elem(), len == 1
|
|
}
|
|
|
|
if t.Kind() == reflect.Slice {
|
|
l := reflect.MakeSlice(t, len, len)
|
|
for i := 0; i < len; i++ {
|
|
e, ok := allocate(t.Elem(), 1)
|
|
if !ok {
|
|
return reflect.Zero(t), false
|
|
}
|
|
|
|
l.Index(i).Set(e)
|
|
}
|
|
|
|
return l, true
|
|
}
|
|
|
|
// must be pointer
|
|
e, ok := allocate(t.Elem(), len)
|
|
if !ok {
|
|
return reflect.Zero(t), false
|
|
}
|
|
|
|
p := reflect.New(t.Elem())
|
|
p.Elem().Set(e)
|
|
return p, true
|
|
}
|