1
0
bind/type.go

384 lines
7.0 KiB
Go
Raw Permalink Normal View History

2025-08-27 21:03:25 +02:00
package bind
2025-08-31 01:23:21 +02:00
import (
"reflect"
"time"
)
2025-08-27 21:03:25 +02:00
type unpackFlag int
const (
pointer unpackFlag = 1 << iota
slice
anytype
iface
)
2025-08-31 00:40:47 +02:00
var typeCirc = make(map[reflect.Type]bool)
2025-08-27 21:03:25 +02:00
func (f unpackFlag) has(v unpackFlag) bool {
return f&v > 0
}
func scalarType(t reflect.Type) reflect.Kind {
2025-08-31 17:11:09 +02:00
switch t.Kind() {
2025-08-31 02:56:26 +02:00
case reflect.Bool:
return Bool
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2025-08-31 17:11:09 +02:00
if isDuration(t) {
return Duration
}
return FieldType(t.Kind())
2025-08-31 02:56:26 +02:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return FieldType(t.Kind())
2025-08-31 02:56:26 +02:00
case reflect.Float32, reflect.Float64:
return FieldType(t.Kind())
2025-08-31 02:56:26 +02:00
case reflect.String:
return FieldType(t.Kind())
2025-08-31 02:56:26 +02:00
default:
2025-08-31 17:11:09 +02:00
if isTime(t) {
return Time
}
2025-08-31 02:56:26 +02:00
return Any
}
}
func scalarTypeReflect(v any) FieldType {
2025-08-31 17:16:19 +02:00
t := reflect.TypeOf(v)
if t == nil {
return Any
}
if hasCircularType(t) {
return Any
2025-08-31 17:16:19 +02:00
}
t = unpackType(t, pointer|slice)
return scalarType(t)
2025-08-31 17:11:09 +02:00
}
2025-08-28 05:04:06 +02:00
func setVisited[T comparable](visited map[T]bool, k T) map[T]bool {
s := make(map[T]bool)
for v := range visited {
s[v] = true
}
s[k] = true
return s
}
2025-08-31 00:40:47 +02:00
func checkHasCircularType(visited map[reflect.Type]bool, t reflect.Type) bool {
2025-08-28 05:04:06 +02:00
if visited[t] {
return true
}
2025-09-01 01:59:03 +02:00
switch {
case t.Kind() == reflect.Pointer || t.Kind() == reflect.Slice:
2025-08-28 05:04:06 +02:00
visited = setVisited(visited, t)
2025-08-31 00:40:47 +02:00
return checkHasCircularType(visited, t.Elem())
2025-09-01 01:59:03 +02:00
case isStruct(t):
2025-08-28 05:04:06 +02:00
visited = setVisited(visited, t)
for i := 0; i < t.NumField(); i++ {
2025-08-31 00:40:47 +02:00
if checkHasCircularType(visited, t.Field(i).Type) {
2025-08-28 05:04:06 +02:00
return true
}
}
return false
default:
return false
}
}
2025-08-31 00:40:47 +02:00
func hasCircularType(t reflect.Type) bool {
if has, cached := typeCirc[t]; cached {
return has
}
has := checkHasCircularType(nil, t)
typeCirc[t] = has
return has
}
func checkHasCircularReference(visited map[uintptr]bool, v reflect.Value) bool {
if !v.IsValid() {
return false
}
if hasCircularType(v.Type()) {
2025-08-28 05:04:06 +02:00
return true
}
switch v.Kind() {
case reflect.Pointer:
p := v.Pointer()
if visited[p] {
return true
}
visited = setVisited(visited, v.Pointer())
2025-08-31 00:40:47 +02:00
return checkHasCircularReference(visited, v.Elem())
2025-08-28 05:04:06 +02:00
case reflect.Slice:
p := v.Pointer()
if visited[p] {
return true
}
visited = setVisited(visited, v.Pointer())
for i := 0; i < v.Len(); i++ {
2025-08-31 00:40:47 +02:00
if checkHasCircularReference(visited, v.Index(i)) {
2025-08-28 05:04:06 +02:00
return true
}
}
return false
case reflect.Interface:
if v.IsNil() {
return false
}
2025-08-31 00:40:47 +02:00
return checkHasCircularReference(visited, v.Elem())
2025-08-28 05:04:06 +02:00
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
2025-08-31 00:40:47 +02:00
if checkHasCircularReference(visited, v.Field(i)) {
2025-08-28 05:04:06 +02:00
return true
}
}
return false
}
return false
}
2025-08-31 00:40:47 +02:00
func hasCircularReference(v reflect.Value) bool {
return checkHasCircularReference(nil, v)
}
2025-08-27 21:03:25 +02:00
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
}
2025-08-31 01:23:21 +02:00
func isTime(t reflect.Type) bool {
return t.ConvertibleTo(reflect.TypeFor[time.Time]())
}
func isDuration(t reflect.Type) bool {
return t == reflect.TypeFor[time.Duration]()
2025-08-31 01:23:21 +02:00
}
2025-08-27 21:03:25 +02:00
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:
2025-08-31 01:23:21 +02:00
return isTime(t)
2025-08-27 21:03:25 +02:00
}
}
2025-09-01 01:59:03 +02:00
func isStruct(t reflect.Type) bool {
return !isTime(t) && t.Kind() == reflect.Struct
}
2025-08-27 21:03:25 +02:00
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 {
2025-08-31 17:11:09 +02:00
if !v.IsValid() || v.IsZero() {
2025-08-31 00:40:47 +02:00
return v
}
2025-08-27 21:03:25 +02:00
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)
}
2025-08-31 00:40:47 +02:00
if unpack.has(anytype) && isAny(v.Type()) && !v.IsZero() && !v.IsNil() {
2025-08-27 21:03:25 +02:00
return unpackValue(v.Elem(), unpack)
}
2025-08-31 00:40:47 +02:00
if unpack.has(iface) && isInterface(v.Type()) && !v.IsZero() && !v.IsNil() {
2025-08-27 21:03:25 +02:00
return unpackValue(v.Elem(), unpack)
}
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
}
2025-08-27 21:03:25 +02:00
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)
2025-09-01 01:59:03 +02:00
return isStruct(t) || isScalarMap(t)
2025-08-27 21:03:25 +02:00
}
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)
}
2025-08-28 05:04:06 +02:00
func acceptsScalarChecked(t reflect.Type) bool {
2025-08-31 00:40:47 +02:00
if hasCircularType(t) {
2025-08-28 05:04:06 +02:00
return false
}
return acceptsScalar(t)
}
func acceptsFieldsChecked(t reflect.Type) bool {
2025-08-31 00:40:47 +02:00
if hasCircularType(t) {
2025-08-28 05:04:06 +02:00
return false
}
return acceptsFields(t)
}
func acceptsListChecked(t reflect.Type) bool {
2025-08-31 00:40:47 +02:00
if hasCircularType(t) {
2025-08-28 05:04:06 +02:00
return false
}
return acceptsList(t)
}
func bindableChecked(t reflect.Type) bool {
2025-08-31 00:40:47 +02:00
if hasCircularType(t) {
2025-08-28 05:04:06 +02:00
return false
}
return bindable(t)
}
2025-08-27 21:03:25 +02:00
func acceptsScalarReflect[T any]() bool {
2025-08-28 05:04:06 +02:00
return acceptsScalarChecked(reflect.TypeFor[T]())
2025-08-27 21:03:25 +02:00
}
func acceptsFieldsReflect[T any]() bool {
2025-08-28 05:04:06 +02:00
return acceptsFieldsChecked(reflect.TypeFor[T]())
2025-08-27 21:03:25 +02:00
}
func acceptsListReflect[T any]() bool {
2025-08-28 05:04:06 +02:00
return acceptsListChecked(reflect.TypeFor[T]())
2025-08-27 21:03:25 +02:00
}
func bindableReflect[T any]() bool {
2025-08-28 05:04:06 +02:00
return bindableChecked(reflect.TypeFor[T]())
2025-08-27 21:03:25 +02:00
}
// 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
}
2025-09-01 01:59:03 +02:00
if isAny(t) || isScalar(t) || isStruct(t) {
2025-08-27 21:03:25 +02:00
p := reflect.New(t)
return p.Elem(), len == 1
}
2025-08-31 00:40:47 +02:00
if isScalarMap(t) {
return reflect.MakeMap(t), len == 1
}
2025-08-27 21:03:25 +02:00
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
}
2025-08-31 00:40:47 +02:00
if t.Kind() == reflect.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
2025-08-27 21:03:25 +02:00
}
2025-08-31 00:40:47 +02:00
return reflect.Zero(t), false
2025-08-27 21:03:25 +02:00
}