💯Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
validator/util.go

161 lines
3.1 KiB

package validator
import (
"reflect"
"strconv"
"strings"
)
const (
namespaceSeparator = "."
leftBracket = "["
rightBracket = "]"
)
func (v *Validate) extractType(current reflect.Value) (reflect.Value, reflect.Kind) {
switch current.Kind() {
case reflect.Ptr:
if current.IsNil() {
return current, reflect.Ptr
}
return v.extractType(current.Elem())
case reflect.Interface:
if current.IsNil() {
return current, reflect.Interface
}
return v.extractType(current.Elem())
case reflect.Invalid:
return current, reflect.Invalid
default:
if v.config.hasCustomFuncs {
if fn, ok := v.config.CustomTypeFuncs[current.Type()]; ok {
return v.extractType(reflect.ValueOf(fn(current)))
}
}
return current, current.Kind()
}
}
func (v *Validate) getStructFieldOK(current reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
current, kind := v.extractType(current)
// fmt.Println("SOK:", current, kind, namespace)
// if len(namespace) == 0 {
// // if kind == reflect.Invalid {
// // return current, kind, false
// // }
// return current, kind, true
// }
if kind == reflect.Invalid {
return current, kind, false
}
if len(namespace) == 0 {
return current, kind, true
}
switch kind {
// case reflect.Invalid:
// return current, kind, false
case reflect.Ptr, reflect.Interface:
return current, kind, false
case reflect.Struct:
typ := current.Type()
fld := namespace
ns := namespace
if typ != timeType && typ != timePtrType {
idx := strings.Index(namespace, namespaceSeparator)
if idx != -1 {
fld = namespace[:idx]
ns = namespace[idx+1:]
} else {
ns = ""
idx = len(namespace)
}
// ns := namespace[idx+1:]
bracketIdx := strings.Index(fld, leftBracket)
if bracketIdx != -1 {
fld = fld[:bracketIdx]
ns = namespace[bracketIdx:]
// if idx == -1 {
// ns = namespace[bracketIdx:]
// } else {
// ns = namespace[bracketIdx:]
// }
}
current = current.FieldByName(fld)
// if current.Kind() == reflect.Invalid {
// return current, reflect.Invalid, false
// }
// fmt.Println("NS:", ns, idx)
return v.getStructFieldOK(current, ns)
}
case reflect.Array, reflect.Slice:
idx := strings.Index(namespace, leftBracket)
idx2 := strings.Index(namespace, rightBracket)
arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
if arrIdx >= current.Len() {
return current, kind, false
}
startIdx := idx2 + 1
if startIdx < len(namespace) {
if namespace[startIdx:startIdx+1] == namespaceSeparator {
startIdx++
}
}
return v.getStructFieldOK(current.Index(arrIdx), namespace[startIdx:])
case reflect.Map:
idx := strings.Index(namespace, leftBracket) + 1
idx2 := strings.Index(namespace, rightBracket)
endIdx := idx2
if endIdx+1 < len(namespace) {
if namespace[endIdx+1:endIdx+2] == namespaceSeparator {
endIdx++
}
}
return v.getStructFieldOK(current.MapIndex(reflect.ValueOf(namespace[idx:idx2])), namespace[endIdx+1:])
}
// if got here there was more namespace, cannot go any deeper
panic("Invalid field namespace")
// return current, kind, false
}