Split out Type checking.
Add Cross Field determination function for future automatic cross struct
namespace field validation.
pull/161/head
joeybloggs 9 years ago
parent 494e1360ec
commit 3ab458c80c
  1. 126
      baked_in.go
  2. 124
      util.go
  3. 190
      validator.go
  4. 57
      validator_test.go

@ -71,32 +71,32 @@ var BakedInValidators = map[string]Func{
"mac": isMac,
}
func isMac(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isMac(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
_, err := net.ParseMAC(field.String())
return err == nil
}
func isIPv4(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isIPv4(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
ip := net.ParseIP(field.String())
return ip != nil && ip.To4() != nil
}
func isIPv6(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isIPv6(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
ip := net.ParseIP(field.String())
return ip != nil && ip.To4() == nil
}
func isIP(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isIP(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
ip := net.ParseIP(field.String())
return ip != nil
}
func isSSN(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isSSN(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if field.Len() != 11 {
return false
@ -105,15 +105,15 @@ func isSSN(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V
return matchesRegex(sSNRegex, field.String())
}
func isLongitude(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isLongitude(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(longitudeRegex, field.String())
}
func isLatitude(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isLatitude(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(latitudeRegex, field.String())
}
func isDataURI(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isDataURI(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
uri := strings.SplitN(field.String(), ",", 2)
@ -127,10 +127,10 @@ func isDataURI(topStruct reflect.Value, currentStruct reflect.Value, field refle
fld := reflect.ValueOf(uri[1])
return isBase64(topStruct, currentStruct, fld, fld.Type(), fld.Kind(), param)
return isBase64(v, topStruct, currentStruct, fld, fld.Type(), fld.Kind(), param)
}
func hasMultiByteCharacter(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func hasMultiByteCharacter(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if field.Len() == 0 {
return true
@ -139,35 +139,35 @@ func hasMultiByteCharacter(topStruct reflect.Value, currentStruct reflect.Value,
return matchesRegex(multibyteRegex, field.String())
}
func isPrintableASCII(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isPrintableASCII(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(printableASCIIRegex, field.String())
}
func isASCII(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isASCII(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(aSCIIRegex, field.String())
}
func isUUID5(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isUUID5(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUID5Regex, field.String())
}
func isUUID4(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isUUID4(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUID4Regex, field.String())
}
func isUUID3(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isUUID3(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUID3Regex, field.String())
}
func isUUID(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isUUID(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUIDRegex, field.String())
}
func isISBN(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return isISBN10(topStruct, currentStruct, field, fieldType, fieldKind, param) || isISBN13(topStruct, currentStruct, field, fieldType, fieldKind, param)
func isISBN(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return isISBN10(v, topStruct, currentStruct, field, fieldType, fieldKind, param) || isISBN13(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
func isISBN13(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isISBN13(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
s := strings.Replace(strings.Replace(field.String(), "-", "", 4), " ", "", 4)
@ -191,7 +191,7 @@ func isISBN13(topStruct reflect.Value, currentStruct reflect.Value, field reflec
return false
}
func isISBN10(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isISBN10(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
s := strings.Replace(strings.Replace(field.String(), "-", "", 3), " ", "", 3)
@ -219,41 +219,41 @@ func isISBN10(topStruct reflect.Value, currentStruct reflect.Value, field reflec
return false
}
func excludesRune(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !containsRune(topStruct, currentStruct, field, fieldType, fieldKind, param)
func excludesRune(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !containsRune(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
func excludesAll(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !containsAny(topStruct, currentStruct, field, fieldType, fieldKind, param)
func excludesAll(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !containsAny(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
func excludes(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !contains(topStruct, currentStruct, field, fieldType, fieldKind, param)
func excludes(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !contains(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
func containsRune(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func containsRune(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
r, _ := utf8.DecodeRuneInString(param)
return strings.ContainsRune(field.String(), r)
}
func containsAny(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func containsAny(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return strings.ContainsAny(field.String(), param)
}
func contains(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func contains(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return strings.Contains(field.String(), param)
}
func isNeField(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !isEqField(topStruct, currentStruct, field, fieldType, fieldKind, param)
func isNeField(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !isEqField(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
func isNe(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !isEq(topStruct, currentStruct, field, fieldType, fieldKind, param)
func isNe(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return !isEq(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
func isEqField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isEqField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
// if current == nil {
if !current.IsValid() {
@ -317,7 +317,7 @@ func isEqField(topStruct reflect.Value, current reflect.Value, field reflect.Val
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isEq(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isEq(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -348,11 +348,11 @@ func isEq(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Va
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isBase64(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isBase64(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(base64Regex, field.String())
}
func isURI(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isURI(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -365,7 +365,7 @@ func isURI(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isURL(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isURL(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -386,51 +386,51 @@ func isURL(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isEmail(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isEmail(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(emailRegex, field.String())
}
func isHsla(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isHsla(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hslaRegex, field.String())
}
func isHsl(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isHsl(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hslRegex, field.String())
}
func isRgba(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isRgba(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(rgbaRegex, field.String())
}
func isRgb(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isRgb(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(rgbRegex, field.String())
}
func isHexcolor(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isHexcolor(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hexcolorRegex, field.String())
}
func isHexadecimal(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isHexadecimal(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hexadecimalRegex, field.String())
}
func isNumber(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isNumber(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(numberRegex, field.String())
}
func isNumeric(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isNumeric(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(numericRegex, field.String())
}
func isAlphanum(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isAlphanum(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(alphaNumericRegex, field.String())
}
func isAlpha(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isAlpha(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(alphaRegex, field.String())
}
func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func hasValue(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
@ -440,7 +440,7 @@ func hasValue(topStruct reflect.Value, currentStruct reflect.Value, field reflec
}
}
func isGteField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isGteField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if !current.IsValid() {
panic("struct not passed for cross validation")
@ -501,7 +501,7 @@ func isGteField(topStruct reflect.Value, current reflect.Value, field reflect.Va
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isGtField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isGtField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if !current.IsValid() {
panic("struct not passed for cross validation")
@ -562,7 +562,7 @@ func isGtField(topStruct reflect.Value, current reflect.Value, field reflect.Val
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isGte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isGte(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -605,7 +605,7 @@ func isGte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isGt(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isGt(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -647,7 +647,7 @@ func isGt(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Va
// length tests whether a variable's length is equal to a given
// value. For strings it tests the number of characters whereas
// for maps and slices it tests the number of items.
func hasLengthOf(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func hasLengthOf(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -684,12 +684,12 @@ func hasLengthOf(topStruct reflect.Value, currentStruct reflect.Value, field ref
// number. For number types, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps
// and slices it tests the number of items.
func hasMinOf(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func hasMinOf(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return isGte(topStruct, currentStruct, field, fieldType, fieldKind, param)
return isGte(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
func isLteField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isLteField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if !current.IsValid() {
panic("struct not passed for cross validation")
@ -750,7 +750,7 @@ func isLteField(topStruct reflect.Value, current reflect.Value, field reflect.Va
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isLtField(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isLtField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if !current.IsValid() {
panic("struct not passed for cross validation")
@ -811,7 +811,7 @@ func isLtField(topStruct reflect.Value, current reflect.Value, field reflect.Val
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isLte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isLte(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -854,7 +854,7 @@ func isLte(topStruct reflect.Value, currentStruct reflect.Value, field reflect.V
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
func isLt(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func isLt(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
switch fieldKind {
@ -898,9 +898,9 @@ func isLt(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Va
// value. For numbers, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps
// and slices it tests the number of items.
func hasMaxOf(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
func hasMaxOf(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return isLte(topStruct, currentStruct, field, fieldType, fieldKind, param)
return isLte(v, topStruct, currentStruct, field, fieldType, fieldKind, param)
}
// asInt retuns the parameter as a int64

@ -0,0 +1,124 @@
package validator
import (
"reflect"
"strconv"
"strings"
)
func (v *Validate) determineType(current reflect.Value) (reflect.Value, reflect.Kind) {
switch current.Kind() {
case reflect.Ptr:
if current.IsNil() {
return current, reflect.Ptr
}
return v.determineType(current.Elem())
case reflect.Interface:
if current.IsNil() {
return current, reflect.Interface
}
return v.determineType(current.Elem())
case reflect.Invalid:
return current, reflect.Invalid
default:
// fmt.Println(current.Kind())
if v.config.hasCustomFuncs {
if fn, ok := v.config.CustomTypeFuncs[current.Type()]; ok {
return v.determineType(reflect.ValueOf(fn(current)))
}
}
return current, current.Kind()
}
}
func (v *Validate) getStructFieldOK(current reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
// fmt.Println("NS:", namespace)
current, kind := v.determineType(current)
// fmt.Println("getStructFieldOK - ", current, kind)
switch kind {
case reflect.Ptr, reflect.Interface, reflect.Invalid:
return current, kind, false
case reflect.Struct:
typ := current.Type()
fld := namespace
if typ != timeType && typ != timePtrType {
idx := strings.Index(namespace, namespaceSeparator)
// fmt.Println("IDX:", namespace, idx)
if idx != -1 {
fld = namespace[:idx]
}
ns := namespace[idx+1:]
bracketIdx := strings.Index(fld, "[")
if bracketIdx != -1 {
fld = fld[:bracketIdx]
// fmt.Println("NSS:", ns)
if idx == -1 {
ns = namespace[bracketIdx:]
} else {
ns = namespace[idx+bracketIdx:]
}
}
// fmt.Println("Looking for field:", fld)
current = current.FieldByName(fld)
// fmt.Println("Current:", current)
return v.getStructFieldOK(current, ns)
}
case reflect.Array, reflect.Slice:
idx := strings.Index(namespace, "[")
idx2 := strings.Index(namespace, "]")
arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
// fmt.Println("ArrayIndex:", arrIdx)
// fmt.Println("LEN:", current.Len())
if arrIdx >= current.Len() {
return current, kind, false
}
return v.getStructFieldOK(current.Index(arrIdx), namespace[idx2+1:])
case reflect.Map:
idx := strings.Index(namespace, "[")
idx2 := strings.Index(namespace, "]")
// key, _ := strconv.Atoi(namespace[idx+1 : idx2])
// fmt.Println("ArrayIndex:", arrIdx)
// fmt.Println("LEN:", current.Len())
// if arrIdx >= current.Len() {
// return current, kind, false
// }
return v.getStructFieldOK(current.MapIndex(reflect.ValueOf(namespace[idx+1:idx2])), namespace[idx2+1:])
}
// fmt.Println("Returning field")
return current, kind, true
}

@ -22,6 +22,7 @@ import (
const (
utf8HexComma = "0x2C"
utf8Pipe = "0x7C"
namespaceSeparator = "."
tagSeparator = ","
orSeparator = "|"
tagKeySeparator = "="
@ -96,7 +97,7 @@ type CustomTypeFunc func(field reflect.Value) interface{}
// currentStruct = current level struct when validating by struct otherwise optional comparison value
// field = field value for validation
// param = parameter used in validation i.e. gt=0 param would be 0
type Func func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool
type Func func(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool
// ValidationErrors is a type of map[string]*FieldError
// it exists to allow for multiple errors to be passed from this library
@ -263,18 +264,11 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
return
}
kind := current.Kind()
if kind == reflect.Ptr && !current.IsNil() {
current = current.Elem()
kind = current.Kind()
}
// this also allows for tags 'required' and 'omitempty' to be used on
// nested struct fields because when len(tags) > 0 below and the value is nil
// then required failes and we check for omitempty just before that
if ((kind == reflect.Ptr || kind == reflect.Interface) && current.IsNil()) || kind == reflect.Invalid {
current, kind := v.determineType(current)
var typ reflect.Type
switch kind {
case reflect.Ptr, reflect.Interface, reflect.Invalid:
if strings.Contains(tag, omitempty) {
return
}
@ -299,6 +293,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
return
}
// fmt.Println(kind)
errs[errPrefix+name] = &FieldError{
Field: name,
Tag: vals[0],
@ -314,41 +309,12 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
if kind == reflect.Invalid {
return
}
}
typ := current.Type()
switch kind {
case reflect.Struct, reflect.Interface:
if kind == reflect.Interface {
current = current.Elem()
kind = current.Kind()
if kind == reflect.Ptr && !current.IsNil() {
current = current.Elem()
kind = current.Kind()
}
// changed current, so have to get inner type again
case reflect.Struct:
typ = current.Type()
if kind != reflect.Struct {
goto FALLTHROUGH
}
}
if typ != timeType && typ != timePtrType {
if kind == reflect.Struct {
if v.config.hasCustomFuncs {
if fn, ok := v.config.CustomTypeFuncs[typ]; ok {
v.traverseField(topStruct, currentStruct, reflect.ValueOf(fn(current)), errPrefix, errs, isStructField, tag, name)
return
}
}
// goto FALLTHROUGH
// required passed validation above so stop here
// if only validating the structs existance.
@ -359,21 +325,133 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false)
return
}
// FALLTHROUGH:
// fallthrough
// default:
// fmt.Println(tag)
// if len(tag) == 0 {
// return
// }
}
FALLTHROUGH:
fallthrough
default:
if len(tag) == 0 {
return
}
}
if v.config.hasCustomFuncs {
if fn, ok := v.config.CustomTypeFuncs[typ]; ok {
v.traverseField(topStruct, currentStruct, reflect.ValueOf(fn(current)), errPrefix, errs, isStructField, tag, name)
return
}
}
typ = current.Type()
// fmt.Println("Kind:", k)
// kind := current.Kind()
// if kind == reflect.Ptr && !current.IsNil() {
// current = current.Elem()
// kind = current.Kind()
// }
// this also allows for tags 'required' and 'omitempty' to be used on
// nested struct fields because when len(tags) > 0 below and the value is nil
// then required failes and we check for omitempty just before that
// if ((kind == reflect.Ptr || kind == reflect.Interface) && current.IsNil()) || kind == reflect.Invalid {
// if strings.Contains(tag, omitempty) {
// return
// }
// if len(tag) > 0 {
// tags := strings.Split(tag, tagSeparator)
// var param string
// vals := strings.SplitN(tags[0], tagKeySeparator, 2)
// if len(vals) > 1 {
// param = vals[1]
// }
// if kind == reflect.Invalid {
// errs[errPrefix+name] = &FieldError{
// Field: name,
// Tag: vals[0],
// Param: param,
// Kind: kind,
// }
// return
// }
// errs[errPrefix+name] = &FieldError{
// Field: name,
// Tag: vals[0],
// Param: param,
// Value: current.Interface(),
// Kind: kind,
// Type: current.Type(),
// }
// return
// }
// // if we get here tag length is zero and we can leave
// if kind == reflect.Invalid {
// return
// }
// }
// typ := current.Type()
// switch kind {
// case reflect.Struct, reflect.Interface:
// if kind == reflect.Interface {
// current = current.Elem()
// kind = current.Kind()
// if kind == reflect.Ptr && !current.IsNil() {
// current = current.Elem()
// kind = current.Kind()
// }
// // changed current, so have to get inner type again
// typ = current.Type()
// if kind != reflect.Struct {
// goto FALLTHROUGH
// }
// }
// if typ != timeType && typ != timePtrType {
// if kind == reflect.Struct {
// if v.config.hasCustomFuncs {
// if fn, ok := v.config.CustomTypeFuncs[typ]; ok {
// v.traverseField(topStruct, currentStruct, reflect.ValueOf(fn(current)), errPrefix, errs, isStructField, tag, name)
// return
// }
// }
// // required passed validation above so stop here
// // if only validating the structs existance.
// if strings.Contains(tag, structOnlyTag) {
// return
// }
// v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false)
// return
// }
// }
// FALLTHROUGH:
// fallthrough
// default:
// if len(tag) == 0 {
// return
// }
// }
// if v.config.hasCustomFuncs {
// if fn, ok := v.config.CustomTypeFuncs[typ]; ok {
// v.traverseField(topStruct, currentStruct, reflect.ValueOf(fn(current)), errPrefix, errs, isStructField, tag, name)
// return
// }
// }
tags, isCached := tagsCache.Get(tag)
@ -431,7 +509,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
if cTag.tagVals[0][0] == omitempty {
if !hasValue(topStruct, currentStruct, current, typ, kind, "") {
if !hasValue(v, topStruct, currentStruct, current, typ, kind, "") {
return
}
continue
@ -491,7 +569,7 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
}
if valFunc(topStruct, currentStruct, current, currentType, currentKind, val[1]) {
if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, val[1]) {
return false
}
@ -514,7 +592,7 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
}
if valFunc(topStruct, currentStruct, current, currentType, currentKind, cTag.tagVals[0][1]) {
if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, cTag.tagVals[0][1]) {
return false
}

@ -192,6 +192,59 @@ func ValidateValuerType(field reflect.Value) interface{} {
return nil
}
func TestCrossNamespaceFieldValidation(t *testing.T) {
type Inner struct {
CreatedAt *time.Time
Slice []string
Map map[string]string
}
type Test struct {
Inner *Inner
CreatedAt *time.Time
}
now := time.Now()
inner := &Inner{
CreatedAt: &now,
Slice: []string{"val1", "val2", "val3"},
Map: map[string]string{"key1": "val1", "key2": "val2", "key3": "val3"},
}
test := &Test{
Inner: inner,
CreatedAt: &now,
}
val := reflect.ValueOf(test)
current, kind, ok := validate.getStructFieldOK(val, "Inner.CreatedAt")
Equal(t, ok, true)
Equal(t, kind, reflect.Struct)
tm, ok := current.Interface().(time.Time)
Equal(t, ok, true)
Equal(t, tm, now)
current, kind, ok = validate.getStructFieldOK(val, "Inner.Slice[1]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = validate.getStructFieldOK(val, "Inner.Slice[101]")
Equal(t, ok, false)
current, kind, ok = validate.getStructFieldOK(val, "Inner.Map[key3]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val3")
// fmt.Println(ok)
// fmt.Println(current)
// fmt.Println(kind)
}
func TestExistsValidation(t *testing.T) {
jsonText := "{ \"truthiness2\": true }"
@ -2710,7 +2763,7 @@ func TestValidateByTagAndValue(t *testing.T) {
errs := validate.FieldWithValue(val, field, "required")
Equal(t, errs, nil)
fn := func(topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
fn := func(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return current.String() == field.String()
}
@ -2729,7 +2782,7 @@ func TestValidateByTagAndValue(t *testing.T) {
func TestAddFunctions(t *testing.T) {
fn := func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
fn := func(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return true
}

Loading…
Cancel
Save