package api
|
|
|
|
import (
|
|
"math"
|
|
"reflect"
|
|
)
|
|
|
|
// isIntegerType returns whether the type is an integer and if it's unsigned.
|
|
// See: https://github.com/Kangaroux/go-map-schema/blob/master/schema.go#L328
|
|
func isIntegerType(t reflect.Type) (yes bool, unsigned bool) {
|
|
switch t.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
yes = true
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
yes = true
|
|
unsigned = true
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// isFloatType returns true if the type is a floating point. Note that this doesn't
|
|
// care about the value -- unmarshaling the number "0" gives a float, not an int.
|
|
// See: https://github.com/Kangaroux/go-map-schema/blob/master/schema.go#L319
|
|
func isFloatType(t reflect.Type) (yes bool) {
|
|
switch t.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
yes = true
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// CanConvert returns whether value v is convertible to type t.
|
|
//
|
|
// If t is a pointer and v is not nil, it checks if v is convertible to the type that
|
|
// t points to.
|
|
// Modified due to not handling slices (DefaultCanConvert fails on PhotoUrls and Tags)
|
|
// See: https://github.com/Kangaroux/go-map-schema/blob/master/schema.go#L191
|
|
func CanConvert(t reflect.Type, v reflect.Value) bool {
|
|
isPtr := t.Kind() == reflect.Ptr
|
|
isStruct := t.Kind() == reflect.Struct
|
|
dstType := t
|
|
|
|
// Check if v is a nil value.
|
|
if !v.IsValid() || (v.CanAddr() && v.IsNil()) {
|
|
return isPtr
|
|
}
|
|
|
|
// If the dst is a pointer, check if we can convert to the type it's pointing to.
|
|
if isPtr {
|
|
dstType = t.Elem()
|
|
isStruct = t.Elem().Kind() == reflect.Struct
|
|
}
|
|
|
|
// If the dst is a struct, we should check its nested fields.
|
|
if isStruct {
|
|
return v.Kind() == reflect.Map
|
|
}
|
|
|
|
if t.Kind() == reflect.Slice {
|
|
return v.Kind() == reflect.Slice
|
|
}
|
|
|
|
if !v.Type().ConvertibleTo(dstType) {
|
|
return false
|
|
}
|
|
|
|
// Handle converting to an integer type.
|
|
if dstInt, unsigned := isIntegerType(dstType); dstInt {
|
|
if isFloatType(v.Type()) {
|
|
f := v.Float()
|
|
|
|
if math.Trunc(f) != f {
|
|
return false
|
|
} else if unsigned && f < 0 {
|
|
return false
|
|
}
|
|
} else if srcInt, _ := isIntegerType(v.Type()); srcInt {
|
|
if unsigned && v.Int() < 0 {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|