pgx/internal/anynil/anynil.go

67 lines
1.9 KiB
Go

package anynil
import (
"database/sql/driver"
"reflect"
)
// valuerReflectType is a reflect.Type for driver.Valuer. It has confusing syntax because reflect.TypeOf returns nil
// when it's argument is a nil interface value. So we use a pointer to the interface and call Elem to get the actual
// type. Yuck.
//
// This can be simplified in Go 1.22 with reflect.TypeFor.
//
// var valuerReflectType = reflect.TypeFor[driver.Valuer]()
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
// Is returns true if value is any type of nil except a pointer that directly implements driver.Valuer. e.g. nil,
// []byte(nil), and a *T where T implements driver.Valuer get normalized to nil but a *T where *T implements
// driver.Valuer does not.
func Is(value any) bool {
if value == nil {
return true
}
refVal := reflect.ValueOf(value)
kind := refVal.Kind()
switch kind {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
if !refVal.IsNil() {
return false
}
if kind == reflect.Ptr {
if _, ok := value.(driver.Valuer); ok {
// The pointer will be considered to implement driver.Valuer even if it is actually implemented on the value.
// But we only want to consider it nil if it is implemented on the pointer. So check if what the pointer points
// to implements driver.Valuer.
if !refVal.Type().Elem().Implements(valuerReflectType) {
return false
}
}
}
return true
default:
return false
}
}
// Normalize converts typed nils (e.g. []byte(nil)) into untyped nil. Other values are returned unmodified.
func Normalize(v any) any {
if Is(v) {
return nil
}
return v
}
// NormalizeSlice converts all typed nils (e.g. []byte(nil)) in s into untyped nils. Other values are unmodified. s is
// mutated in place.
func NormalizeSlice(s []any) {
for i := range s {
if Is(s[i]) {
s[i] = nil
}
}
}