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 }