validator/util.go

289 lines
6.4 KiB
Go
Raw Permalink Normal View History

package validator
import (
"reflect"
"strconv"
"strings"
2020-07-25 23:00:27 -03:00
"time"
)
2016-08-03 14:46:35 -04:00
// extractTypeInternal gets the actual underlying type of field value.
// It will dive into pointers, customTypes and return you the
// underlying value and it's kind.
2016-08-03 14:46:35 -04:00
func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) {
2016-09-17 14:12:20 -04:00
BEGIN:
switch current.Kind() {
case reflect.Ptr:
nullable = true
if current.IsNil() {
return current, reflect.Ptr, nullable
}
2016-09-17 14:12:20 -04:00
current = current.Elem()
goto BEGIN
case reflect.Interface:
nullable = true
if current.IsNil() {
return current, reflect.Interface, nullable
}
2016-09-17 14:12:20 -04:00
current = current.Elem()
goto BEGIN
case reflect.Invalid:
return current, reflect.Invalid, nullable
default:
2016-08-03 14:46:35 -04:00
if v.v.hasCustomFuncs {
2016-08-03 14:46:35 -04:00
if fn, ok := v.v.customFuncs[current.Type()]; ok {
2016-09-17 14:12:20 -04:00
current = reflect.ValueOf(fn(current))
goto BEGIN
}
}
return current, current.Kind(), nullable
}
}
2016-08-03 14:46:35 -04:00
// getStructFieldOKInternal traverses a struct to retrieve a specific field denoted by the provided namespace and
// returns the field, field kind and whether is was successful in retrieving the field at all.
2016-08-03 14:46:35 -04:00
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
2019-11-17 13:02:10 -08:00
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
2016-09-17 14:12:20 -04:00
BEGIN:
2019-11-17 13:02:10 -08:00
current, kind, nullable = v.ExtractType(val)
2015-08-14 21:02:01 -04:00
if kind == reflect.Invalid {
2016-09-17 14:12:20 -04:00
return
2015-08-14 21:02:01 -04:00
}
2016-08-03 14:46:35 -04:00
if namespace == "" {
2016-09-17 14:12:20 -04:00
found = true
return
2015-08-14 21:02:01 -04:00
}
switch kind {
2015-08-14 09:06:52 -04:00
2015-08-14 21:02:01 -04:00
case reflect.Ptr, reflect.Interface:
2016-09-17 14:12:20 -04:00
return
case reflect.Struct:
typ := current.Type()
fld := namespace
2018-02-15 08:50:04 -08:00
var ns string
2016-08-03 14:46:35 -04:00
if typ != timeType {
idx := strings.Index(namespace, namespaceSeparator)
if idx != -1 {
fld = namespace[:idx]
2015-08-14 21:02:01 -04:00
ns = namespace[idx+1:]
} else {
2016-08-03 14:46:35 -04:00
ns = ""
}
2015-08-14 09:06:52 -04:00
bracketIdx := strings.Index(fld, leftBracket)
if bracketIdx != -1 {
fld = fld[:bracketIdx]
2015-08-14 21:02:01 -04:00
ns = namespace[bracketIdx:]
}
2016-09-17 14:12:20 -04:00
val = current.FieldByName(fld)
namespace = ns
goto BEGIN
}
case reflect.Array, reflect.Slice:
2015-08-14 09:06:52 -04:00
idx := strings.Index(namespace, leftBracket)
idx2 := strings.Index(namespace, rightBracket)
arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
if arrIdx >= current.Len() {
2019-11-17 13:02:10 -08:00
return
}
startIdx := idx2 + 1
if startIdx < len(namespace) {
2015-08-14 09:06:52 -04:00
if namespace[startIdx:startIdx+1] == namespaceSeparator {
startIdx++
}
}
2016-09-17 14:12:20 -04:00
val = current.Index(arrIdx)
namespace = namespace[startIdx:]
goto BEGIN
case reflect.Map:
2015-08-14 09:06:52 -04:00
idx := strings.Index(namespace, leftBracket) + 1
idx2 := strings.Index(namespace, rightBracket)
endIdx := idx2
if endIdx+1 < len(namespace) {
2015-08-14 09:06:52 -04:00
if namespace[endIdx+1:endIdx+2] == namespaceSeparator {
endIdx++
}
}
key := namespace[idx:idx2]
switch current.Type().Key().Kind() {
case reflect.Int:
i, _ := strconv.Atoi(key)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(i))
namespace = namespace[endIdx+1:]
case reflect.Int8:
i, _ := strconv.ParseInt(key, 10, 8)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(int8(i)))
namespace = namespace[endIdx+1:]
case reflect.Int16:
i, _ := strconv.ParseInt(key, 10, 16)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(int16(i)))
namespace = namespace[endIdx+1:]
case reflect.Int32:
i, _ := strconv.ParseInt(key, 10, 32)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(int32(i)))
namespace = namespace[endIdx+1:]
case reflect.Int64:
i, _ := strconv.ParseInt(key, 10, 64)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(i))
namespace = namespace[endIdx+1:]
case reflect.Uint:
i, _ := strconv.ParseUint(key, 10, 0)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(uint(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint8:
i, _ := strconv.ParseUint(key, 10, 8)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(uint8(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint16:
i, _ := strconv.ParseUint(key, 10, 16)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(uint16(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint32:
i, _ := strconv.ParseUint(key, 10, 32)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(uint32(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint64:
i, _ := strconv.ParseUint(key, 10, 64)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(i))
namespace = namespace[endIdx+1:]
case reflect.Float32:
f, _ := strconv.ParseFloat(key, 32)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(float32(f)))
namespace = namespace[endIdx+1:]
case reflect.Float64:
f, _ := strconv.ParseFloat(key, 64)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(f))
namespace = namespace[endIdx+1:]
case reflect.Bool:
b, _ := strconv.ParseBool(key)
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(b))
namespace = namespace[endIdx+1:]
// reflect.Type = string
default:
2016-09-17 14:12:20 -04:00
val = current.MapIndex(reflect.ValueOf(key))
namespace = namespace[endIdx+1:]
}
2016-09-17 14:12:20 -04:00
goto BEGIN
}
2015-08-14 21:02:01 -04:00
// if got here there was more namespace, cannot go any deeper
panic("Invalid field namespace")
}
2015-08-16 15:37:47 -04:00
// asInt returns the parameter as a int64
2015-08-16 15:37:47 -04:00
// or panics if it can't convert
func asInt(param string) int64 {
i, err := strconv.ParseInt(param, 0, 64)
panicIf(err)
return i
}
2020-07-25 23:00:27 -03:00
// asIntFromTimeDuration parses param as time.Duration and returns it as int64
// or panics on error.
func asIntFromTimeDuration(param string) int64 {
d, err := time.ParseDuration(param)
if err != nil {
// attempt parsing as an an integer assuming nanosecond precision
return asInt(param)
}
2020-07-25 23:00:27 -03:00
return int64(d)
}
2020-07-28 11:47:26 -03:00
// asIntFromType calls the proper function to parse param as int64,
// given a field's Type t.
func asIntFromType(t reflect.Type, param string) int64 {
switch t {
case timeDurationType:
return asIntFromTimeDuration(param)
default:
return asInt(param)
}
}
2015-08-16 15:37:47 -04:00
// asUint returns the parameter as a uint64
// or panics if it can't convert
func asUint(param string) uint64 {
i, err := strconv.ParseUint(param, 0, 64)
panicIf(err)
return i
}
// asFloat returns the parameter as a float64
// or panics if it can't convert
func asFloat(param string) float64 {
i, err := strconv.ParseFloat(param, 64)
panicIf(err)
return i
}
// asBool returns the parameter as a bool
// or panics if it can't convert
func asBool(param string) bool {
i, err := strconv.ParseBool(param)
panicIf(err)
return i
}
2015-08-16 15:37:47 -04:00
func panicIf(err error) {
if err != nil {
panic(err.Error())
}
}