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 }