wand/reflect.go

414 lines
7.1 KiB
Go
Raw Normal View History

2025-08-18 14:24:31 +02:00
package wand
import (
2025-09-01 02:07:48 +02:00
"code.squareroundforest.org/arpio/bind"
"io"
2025-09-01 04:10:35 +02:00
"reflect"
2025-08-24 01:45:25 +02:00
"strings"
2025-09-01 04:10:35 +02:00
"time"
2025-08-18 14:24:31 +02:00
)
2025-09-01 02:07:48 +02:00
func filter[T any](list []T, predicate func(T) bool) []T {
var filtered []T
for _, item := range list {
if predicate(item) {
filtered = append(filtered, item)
}
}
return filtered
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func not[T any](p func(T) bool) func(T) bool {
return func(v T) bool {
return !p(v)
}
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func and[T any](p ...func(T) bool) func(T) bool {
return func(v T) bool {
for _, pi := range p {
if !pi(v) {
return false
}
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
return true
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
func or[T any](p ...func(T) bool) func(T) bool {
return func(v T) bool {
for _, pi := range p {
if pi(v) {
return true
}
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
return false
}
2025-08-18 14:24:31 +02:00
}
2025-09-04 01:09:24 +02:00
func circularChecked(visited map[reflect.Type]bool, t reflect.Type) bool {
2025-09-01 02:07:48 +02:00
if t == nil {
2025-09-04 01:09:24 +02:00
return false
2025-08-18 14:24:31 +02:00
}
2025-08-24 04:46:54 +02:00
2025-09-01 02:07:48 +02:00
if visited[t] {
2025-09-04 01:09:24 +02:00
return true
2025-08-24 04:46:54 +02:00
}
2025-09-01 02:07:48 +02:00
if visited == nil {
visited = make(map[reflect.Type]bool)
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
visited[t] = true
switch t.Kind() {
case reflect.Pointer, reflect.Slice:
2025-09-04 01:09:24 +02:00
return circularChecked(visited, t.Elem())
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
svisited := make(map[reflect.Type]bool)
for t := range visited {
svisited[t] = true
}
if circularChecked(svisited, t.Field(i).Type) {
return true
}
}
return false
2025-09-01 02:07:48 +02:00
default:
2025-09-04 01:09:24 +02:00
return false
2025-09-01 02:07:48 +02:00
}
2025-08-18 14:24:31 +02:00
}
2025-09-04 01:09:24 +02:00
func circular(t reflect.Type) bool {
return circularChecked(nil, t)
}
2025-09-01 02:07:48 +02:00
func unpackType(t reflect.Type) reflect.Type {
2025-09-04 01:09:24 +02:00
if t == nil {
return nil
}
switch t.Kind() {
case reflect.Pointer, reflect.Slice:
return unpackType(t.Elem())
default:
return t
}
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func unpackValueChecked(visited map[uintptr]bool, v reflect.Value) reflect.Value {
if !v.IsValid() {
return v
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
switch v.Kind() {
case reflect.Pointer:
p := v.Pointer()
if visited[p] {
return v
}
if visited == nil {
visited = make(map[uintptr]bool)
}
visited[p] = true
return unpackValueChecked(visited, v.Elem())
case reflect.Interface:
if v.IsNil() {
return v
}
return unpackValueChecked(visited, v.Elem())
2025-08-24 01:45:25 +02:00
default:
2025-09-01 02:07:48 +02:00
return v
2025-08-24 01:45:25 +02:00
}
}
2025-09-01 02:07:48 +02:00
func unpackValue(v reflect.Value) reflect.Value {
return unpackValueChecked(nil, v)
2025-08-24 01:45:25 +02:00
}
2025-09-01 02:07:48 +02:00
func isFunc(v any) bool {
r := reflect.ValueOf(v)
r = unpackValue(r)
return r.Kind() == reflect.Func
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func isTime(t reflect.Type) bool {
if t == nil {
return false
2025-08-18 14:24:31 +02:00
}
2025-09-04 01:09:24 +02:00
t = unpackType(t)
2025-09-01 02:07:48 +02:00
return t.ConvertibleTo(reflect.TypeFor[time.Time]())
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func isStruct(t reflect.Type) bool {
if t == nil {
return false
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
t = unpackType(t)
return !isTime(t) && t.Kind() == reflect.Struct
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
func isReader(t reflect.Type) bool {
if t == nil || t.Kind() != reflect.Interface {
return false
}
2025-08-24 01:45:25 +02:00
2025-09-01 02:07:48 +02:00
return t.NumMethod() == 1 && t.Implements(reflect.TypeFor[io.Reader]())
}
2025-08-24 01:45:25 +02:00
2025-09-01 02:07:48 +02:00
func isWriter(t reflect.Type) bool {
if t == nil || t.Kind() != reflect.Interface {
return false
}
2025-08-24 01:45:25 +02:00
2025-09-01 02:07:48 +02:00
return t.NumMethod() == 1 && t.Implements(reflect.TypeFor[io.Writer]())
}
2025-08-18 14:24:31 +02:00
2025-09-04 01:09:24 +02:00
func compatibleTypes(t ...bind.FieldType) bool {
2025-09-01 02:07:48 +02:00
if len(t) == 0 {
return false
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
if len(t) == 1 {
return true
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
switch t[0] {
case bind.Any:
return compatibleTypes(t[1:]...)
default:
return t[0] == t[1] && compatibleTypes(t[1:]...)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
func parameters(f any) []reflect.Type {
r := reflect.ValueOf(f)
r = unpackValue(r)
if r.Kind() != reflect.Func {
return nil
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
var p []reflect.Type
t := r.Type()
for i := 0; i < t.NumIn(); i++ {
p = append(p, t.In(i))
2025-08-24 01:45:25 +02:00
}
2025-09-01 02:07:48 +02:00
return p
2025-08-24 01:45:25 +02:00
}
2025-09-01 02:07:48 +02:00
func structParameters(f any) []reflect.Type {
return filter(parameters(f), isStruct)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func structFields(s reflect.Type) []bind.Field {
s = unpackType(s)
2025-09-04 01:09:24 +02:00
return bind.FieldsOf(s)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func fields(f any) []bind.Field {
var fields []bind.Field
2025-09-01 03:25:18 +02:00
s := structParameters(f)
2025-09-01 02:07:48 +02:00
for _, si := range s {
fields = append(fields, structFields(si)...)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
return fields
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func mapFields(f any) map[string][]bind.Field {
2025-09-01 03:25:18 +02:00
fields := fields(f)
2025-09-01 02:07:48 +02:00
m := make(map[string][]bind.Field)
for _, fi := range fields {
m[fi.Name()] = append(m[fi.Name()], fi)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
return m
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func boolFields(f []bind.Field) []bind.Field {
return filter(
fields(f),
func(f bind.Field) bool { return f.Type() == bind.Bool },
)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func positional(f any) ([]reflect.Type, bool) {
p := filter(
parameters(f),
not(or(isReader, isWriter, isStruct)),
2025-08-18 14:24:31 +02:00
)
2025-09-01 02:07:48 +02:00
r := reflect.ValueOf(f)
r = unpackValue(r)
t := r.Type()
return p, t.IsVariadic()
}
func positionalIndices(f any) []int {
r := reflect.ValueOf(f)
r = unpackValue(r)
if r.Kind() != reflect.Func {
return nil
}
var indices []int
t := r.Type()
for i := 0; i < t.NumIn(); i++ {
p := t.In(i)
if isTime(p) || isStruct(p) || isReader(p) || isWriter(p) {
continue
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
indices = append(indices, i)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
return indices
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func ioParameters(f any) ([]reflect.Type, []reflect.Type) {
p := parameters(f)
return filter(p, isReader), filter(p, isWriter)
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
func bindable(t reflect.Type) bool {
if t == nil {
return false
}
2025-09-04 01:09:24 +02:00
if circular(t) {
return false
}
2025-09-01 03:25:18 +02:00
t = unpackType(t)
2025-09-01 02:07:48 +02:00
if isTime(t) {
2025-08-18 14:24:31 +02:00
return true
}
2025-09-01 02:07:48 +02:00
if isStruct(t) {
return true
}
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.Float32,
reflect.Float64,
reflect.String:
return true
2025-08-18 14:24:31 +02:00
case reflect.Interface:
2025-09-01 02:07:48 +02:00
return t.NumMethod() == 0
case reflect.Slice:
return bindable(t.Elem())
2025-08-18 14:24:31 +02:00
default:
return false
}
}
2025-09-04 01:09:24 +02:00
func scalarTypeString(t bind.FieldType) string {
2025-09-01 02:07:48 +02:00
r := reflect.TypeOf(t)
p := strings.Split(r.Name(), ".")
2025-09-01 04:10:35 +02:00
n := p[len(p)-1]
2025-09-01 02:07:48 +02:00
n = strings.ToLower(n)
return n
}
2025-09-04 01:09:24 +02:00
func canScan(t bind.FieldType, v any) bool {
2025-09-01 02:07:48 +02:00
switch t {
case bind.Any:
2025-08-18 14:24:31 +02:00
return true
2025-09-01 02:07:48 +02:00
case bind.Bool:
2025-09-04 01:09:24 +02:00
_, ok := bind.CreateAndBindScalar[bool](v)
2025-09-01 02:07:48 +02:00
return ok
2025-09-04 01:09:24 +02:00
case bind.Int, bind.Int8, bind.Int16, bind.Int32, bind.Int64:
_, ok := bind.CreateAndBindScalar[int](v)
2025-09-01 02:07:48 +02:00
return ok
2025-09-04 01:09:24 +02:00
case bind.Uint, bind.Uint8, bind.Uint16, bind.Uint32, bind.Uint64:
_, ok := bind.CreateAndBindScalar[uint](v)
2025-09-01 02:07:48 +02:00
return ok
2025-09-04 01:09:24 +02:00
case bind.Float32, bind.Float64:
_, ok := bind.CreateAndBindScalar[float64](v)
2025-09-01 02:07:48 +02:00
return ok
case bind.String:
2025-09-04 01:09:24 +02:00
_, ok := bind.CreateAndBindScalar[string](v)
2025-09-01 02:07:48 +02:00
return ok
case bind.Duration:
2025-09-04 01:09:24 +02:00
_, ok := bind.CreateAndBindScalar[time.Duration](v)
2025-09-01 02:07:48 +02:00
return ok
case bind.Time:
2025-09-04 01:09:24 +02:00
_, ok := bind.CreateAndBindScalar[time.Time](v)
2025-09-01 02:07:48 +02:00
return ok
default:
return false
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
}
2025-08-18 14:24:31 +02:00
2025-09-01 02:07:48 +02:00
func canScanType(t reflect.Type, v any) bool {
if t == nil {
return false
}
2025-09-01 03:25:18 +02:00
r := allocate(reflect.PointerTo(t))
2025-09-01 02:07:48 +02:00
return bind.BindScalar(r.Interface(), v)
}
func allocate(t reflect.Type) reflect.Value {
2025-08-18 14:24:31 +02:00
switch t.Kind() {
2025-08-26 14:12:18 +02:00
case reflect.Pointer:
2025-09-01 02:07:48 +02:00
et := t.Elem()
v := allocate(et)
p := reflect.New(et)
p.Elem().Set(v)
return p
case reflect.Slice:
v := allocate(t.Elem())
s := reflect.MakeSlice(t, 1, 1)
s.Index(0).Set(v)
return s
2025-08-18 14:24:31 +02:00
default:
2025-09-01 03:25:18 +02:00
p := reflect.New(t)
return p.Elem()
2025-09-01 02:07:48 +02:00
}
}
func bindScalar(receiver reflect.Value, value any) {
bind.BindScalar(receiver.Interface(), value)
}
func bindFields(receiver reflect.Value, values map[string][]any) []string {
var f []bind.Field
2025-09-01 03:25:18 +02:00
for name, vals := range values {
for _, v := range vals {
f = append(f, bind.NamedValue(name, v))
}
2025-08-18 14:24:31 +02:00
}
2025-09-01 02:07:48 +02:00
2025-09-04 01:09:24 +02:00
unmapped := bind.Bind(receiver.Interface(), f...)
2025-09-01 02:07:48 +02:00
var names []string
for _, um := range unmapped {
names = append(names, um.Name())
}
return names
2025-08-18 14:24:31 +02:00
}