gotosocial/vendor/github.com/jackc/pgx/v5/pgtype/convert.go

109 lines
3.1 KiB
Go

package pgtype
import (
"reflect"
)
func NullAssignTo(dst any) error {
dstPtr := reflect.ValueOf(dst)
// AssignTo dst must always be a pointer
if dstPtr.Kind() != reflect.Ptr {
return &nullAssignmentError{dst: dst}
}
dstVal := dstPtr.Elem()
switch dstVal.Kind() {
case reflect.Ptr, reflect.Slice, reflect.Map:
dstVal.Set(reflect.Zero(dstVal.Type()))
return nil
}
return &nullAssignmentError{dst: dst}
}
var kindTypes map[reflect.Kind]reflect.Type
func toInterface(dst reflect.Value, t reflect.Type) (any, bool) {
nextDst := dst.Convert(t)
return nextDst.Interface(), dst.Type() != nextDst.Type()
}
// GetAssignToDstType attempts to convert dst to something AssignTo can assign
// to. If dst is a pointer to pointer it allocates a value and returns the
// dereferences pointer. If dst is a named type such as *Foo where Foo is type
// Foo int16, it converts dst to *int16.
//
// GetAssignToDstType returns the converted dst and a bool representing if any
// change was made.
func GetAssignToDstType(dst any) (any, bool) {
dstPtr := reflect.ValueOf(dst)
// AssignTo dst must always be a pointer
if dstPtr.Kind() != reflect.Ptr {
return nil, false
}
dstVal := dstPtr.Elem()
// if dst is a pointer to pointer, allocate space try again with the dereferenced pointer
if dstVal.Kind() == reflect.Ptr {
dstVal.Set(reflect.New(dstVal.Type().Elem()))
return dstVal.Interface(), true
}
// if dst is pointer to a base type that has been renamed
if baseValType, ok := kindTypes[dstVal.Kind()]; ok {
return toInterface(dstPtr, reflect.PtrTo(baseValType))
}
if dstVal.Kind() == reflect.Slice {
if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok {
return toInterface(dstPtr, reflect.PtrTo(reflect.SliceOf(baseElemType)))
}
}
if dstVal.Kind() == reflect.Array {
if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok {
return toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(dstVal.Len(), baseElemType)))
}
}
if dstVal.Kind() == reflect.Struct {
if dstVal.Type().NumField() == 1 && dstVal.Type().Field(0).Anonymous {
dstPtr = dstVal.Field(0).Addr()
nested := dstVal.Type().Field(0).Type
if nested.Kind() == reflect.Array {
if baseElemType, ok := kindTypes[nested.Elem().Kind()]; ok {
return toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(nested.Len(), baseElemType)))
}
}
if _, ok := kindTypes[nested.Kind()]; ok && dstPtr.CanInterface() {
return dstPtr.Interface(), true
}
}
}
return nil, false
}
func init() {
kindTypes = map[reflect.Kind]reflect.Type{
reflect.Bool: reflect.TypeOf(false),
reflect.Float32: reflect.TypeOf(float32(0)),
reflect.Float64: reflect.TypeOf(float64(0)),
reflect.Int: reflect.TypeOf(int(0)),
reflect.Int8: reflect.TypeOf(int8(0)),
reflect.Int16: reflect.TypeOf(int16(0)),
reflect.Int32: reflect.TypeOf(int32(0)),
reflect.Int64: reflect.TypeOf(int64(0)),
reflect.Uint: reflect.TypeOf(uint(0)),
reflect.Uint8: reflect.TypeOf(uint8(0)),
reflect.Uint16: reflect.TypeOf(uint16(0)),
reflect.Uint32: reflect.TypeOf(uint32(0)),
reflect.Uint64: reflect.TypeOf(uint64(0)),
reflect.String: reflect.TypeOf(""),
}
}