add extra param of current struct.
pull/23/head v4.0
Dean Karn 10 years ago
parent 37ced95e7a
commit a3cb430fa1
  1. 9
      README.md
  2. 198
      baked_in.go
  3. 8
      doc.go
  4. 36
      validator.go
  5. 6
      validator_test.go

@ -1,6 +1,7 @@
Package go-validate-yourself Package go-validate-yourself
================ ================
[![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v4-development)](https://travis-ci.org/joeybloggs/go-validate-yourself) [![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v4)](https://travis-ci.org/joeybloggs/go-validate-yourself)
[![GoDoc](https://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v4?status.svg)](https://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v4)
Package validator implements value validations for structs and individual fields based on tags. Package validator implements value validations for structs and individual fields based on tags.
@ -22,7 +23,7 @@ And then just import the package into your own code.
Usage Usage
===== =====
Please see http://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v3 for detailed usage docs. Please see http://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v4 for detailed usage docs.
Contributing Contributing
============ ============
@ -36,3 +37,7 @@ however, there will also be a v2-development brach even though v2 doesn't exist
I strongly encourage everyone whom creates a custom validation function to contribute them and I strongly encourage everyone whom creates a custom validation function to contribute them and
help make this package even better. help make this package even better.
License
=======
Distributed under MIT License, please see license file in code for more details.

@ -38,7 +38,7 @@ var BakedInValidators = map[string]ValidationFunc{
"uri": isURI, "uri": isURI,
} }
func isURI(val interface{}, field interface{}, param string) bool { func isURI(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -53,7 +53,7 @@ func isURI(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isURL(val interface{}, field interface{}, param string) bool { func isURL(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -76,7 +76,7 @@ func isURL(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isEmail(val interface{}, field interface{}, param string) bool { func isEmail(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -89,7 +89,7 @@ func isEmail(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHsla(val interface{}, field interface{}, param string) bool { func isHsla(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -102,7 +102,7 @@ func isHsla(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHsl(val interface{}, field interface{}, param string) bool { func isHsl(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -115,7 +115,7 @@ func isHsl(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isRgba(val interface{}, field interface{}, param string) bool { func isRgba(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -128,7 +128,7 @@ func isRgba(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isRgb(val interface{}, field interface{}, param string) bool { func isRgb(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -141,7 +141,7 @@ func isRgb(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHexcolor(val interface{}, field interface{}, param string) bool { func isHexcolor(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -154,7 +154,7 @@ func isHexcolor(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isHexadecimal(val interface{}, field interface{}, param string) bool { func isHexadecimal(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -167,7 +167,7 @@ func isHexadecimal(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isNumber(val interface{}, field interface{}, param string) bool { func isNumber(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -180,7 +180,7 @@ func isNumber(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isNumeric(val interface{}, field interface{}, param string) bool { func isNumeric(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -193,7 +193,7 @@ func isNumeric(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isAlphanum(val interface{}, field interface{}, param string) bool { func isAlphanum(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -206,7 +206,7 @@ func isAlphanum(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isAlpha(val interface{}, field interface{}, param string) bool { func isAlpha(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -219,7 +219,7 @@ func isAlpha(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func hasValue(val interface{}, field interface{}, param string) bool { func hasValue(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -233,45 +233,45 @@ func hasValue(val interface{}, field interface{}, param string) bool {
} }
} }
func isGteField(val interface{}, field interface{}, param string) bool { func isGteField(top interface{}, current interface{}, field interface{}, param string) bool {
if val == nil { if current == nil {
panic("struct not passed for cross validation") panic("struct not passed for cross validation")
} }
topVal := reflect.ValueOf(val) currentVal := reflect.ValueOf(current)
if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
topVal = reflect.ValueOf(topVal.Elem().Interface()) currentVal = reflect.ValueOf(currentVal.Elem().Interface())
} }
var topFieldVal reflect.Value var currentFielVal reflect.Value
switch topVal.Kind() { switch currentVal.Kind() {
case reflect.Struct: case reflect.Struct:
if topVal.Type() == reflect.TypeOf(time.Time{}) { if currentVal.Type() == reflect.TypeOf(time.Time{}) {
topFieldVal = topVal currentFielVal = currentVal
break break
} }
f := topVal.FieldByName(param) f := currentVal.FieldByName(param)
if f.Kind() == reflect.Invalid { if f.Kind() == reflect.Invalid {
panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
} }
topFieldVal = f currentFielVal = f
default: default:
topFieldVal = topVal currentFielVal = currentVal
} }
if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
} }
fv := reflect.ValueOf(field) fv := reflect.ValueOf(field)
@ -280,25 +280,25 @@ func isGteField(val interface{}, field interface{}, param string) bool {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fv.Int() >= topFieldVal.Int() return fv.Int() >= currentFielVal.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return fv.Uint() >= topFieldVal.Uint() return fv.Uint() >= currentFielVal.Uint()
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return fv.Float() >= topFieldVal.Float() return fv.Float() >= currentFielVal.Float()
case reflect.Struct: case reflect.Struct:
if fv.Type() == reflect.TypeOf(time.Time{}) { if fv.Type() == reflect.TypeOf(time.Time{}) {
if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
panic("Bad Top Level field type") panic("Bad Top Level field type")
} }
t := topFieldVal.Interface().(time.Time) t := currentFielVal.Interface().(time.Time)
fieldTime := field.(time.Time) fieldTime := field.(time.Time)
return fieldTime.After(t) || fieldTime.Equal(t) return fieldTime.After(t) || fieldTime.Equal(t)
@ -308,45 +308,45 @@ func isGteField(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isGtField(val interface{}, field interface{}, param string) bool { func isGtField(top interface{}, current interface{}, field interface{}, param string) bool {
if val == nil { if current == nil {
panic("struct not passed for cross validation") panic("struct not passed for cross validation")
} }
topVal := reflect.ValueOf(val) currentVal := reflect.ValueOf(current)
if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
topVal = reflect.ValueOf(topVal.Elem().Interface()) currentVal = reflect.ValueOf(currentVal.Elem().Interface())
} }
var topFieldVal reflect.Value var currentFielVal reflect.Value
switch topVal.Kind() { switch currentVal.Kind() {
case reflect.Struct: case reflect.Struct:
if topVal.Type() == reflect.TypeOf(time.Time{}) { if currentVal.Type() == reflect.TypeOf(time.Time{}) {
topFieldVal = topVal currentFielVal = currentVal
break break
} }
f := topVal.FieldByName(param) f := currentVal.FieldByName(param)
if f.Kind() == reflect.Invalid { if f.Kind() == reflect.Invalid {
panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
} }
topFieldVal = f currentFielVal = f
default: default:
topFieldVal = topVal currentFielVal = currentVal
} }
if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
} }
fv := reflect.ValueOf(field) fv := reflect.ValueOf(field)
@ -355,25 +355,25 @@ func isGtField(val interface{}, field interface{}, param string) bool {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fv.Int() > topFieldVal.Int() return fv.Int() > currentFielVal.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return fv.Uint() > topFieldVal.Uint() return fv.Uint() > currentFielVal.Uint()
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return fv.Float() > topFieldVal.Float() return fv.Float() > currentFielVal.Float()
case reflect.Struct: case reflect.Struct:
if fv.Type() == reflect.TypeOf(time.Time{}) { if fv.Type() == reflect.TypeOf(time.Time{}) {
if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
panic("Bad Top Level field type") panic("Bad Top Level field type")
} }
t := topFieldVal.Interface().(time.Time) t := currentFielVal.Interface().(time.Time)
fieldTime := field.(time.Time) fieldTime := field.(time.Time)
return fieldTime.After(t) return fieldTime.After(t)
@ -383,7 +383,7 @@ func isGtField(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isGte(val interface{}, field interface{}, param string) bool { func isGte(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -428,7 +428,7 @@ func isGte(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isGt(val interface{}, field interface{}, param string) bool { func isGt(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -472,7 +472,7 @@ func isGt(val interface{}, field interface{}, param string) bool {
// length tests whether a variable's length is equal to a given // length tests whether a variable's length is equal to a given
// value. For strings it tests the number of characters whereas // value. For strings it tests the number of characters whereas
// for maps and slices it tests the number of items. // for maps and slices it tests the number of items.
func hasLengthOf(val interface{}, field interface{}, param string) bool { func hasLengthOf(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -511,50 +511,50 @@ func hasLengthOf(val interface{}, field interface{}, param string) bool {
// number. For number types, it's a simple lesser-than test; for // number. For number types, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps // strings it tests the number of characters whereas for maps
// and slices it tests the number of items. // and slices it tests the number of items.
func hasMinOf(val interface{}, field interface{}, param string) bool { func hasMinOf(top interface{}, current interface{}, field interface{}, param string) bool {
return isGte(val, field, param) return isGte(top, current, field, param)
} }
func isLteField(val interface{}, field interface{}, param string) bool { func isLteField(top interface{}, current interface{}, field interface{}, param string) bool {
if val == nil { if current == nil {
panic("struct not passed for cross validation") panic("struct not passed for cross validation")
} }
topVal := reflect.ValueOf(val) currentVal := reflect.ValueOf(current)
if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
topVal = reflect.ValueOf(topVal.Elem().Interface()) currentVal = reflect.ValueOf(currentVal.Elem().Interface())
} }
var topFieldVal reflect.Value var currentFielVal reflect.Value
switch topVal.Kind() { switch currentVal.Kind() {
case reflect.Struct: case reflect.Struct:
if topVal.Type() == reflect.TypeOf(time.Time{}) { if currentVal.Type() == reflect.TypeOf(time.Time{}) {
topFieldVal = topVal currentFielVal = currentVal
break break
} }
f := topVal.FieldByName(param) f := currentVal.FieldByName(param)
if f.Kind() == reflect.Invalid { if f.Kind() == reflect.Invalid {
panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
} }
topFieldVal = f currentFielVal = f
default: default:
topFieldVal = topVal currentFielVal = currentVal
} }
if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
} }
fv := reflect.ValueOf(field) fv := reflect.ValueOf(field)
@ -563,25 +563,25 @@ func isLteField(val interface{}, field interface{}, param string) bool {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fv.Int() <= topFieldVal.Int() return fv.Int() <= currentFielVal.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return fv.Uint() <= topFieldVal.Uint() return fv.Uint() <= currentFielVal.Uint()
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return fv.Float() <= topFieldVal.Float() return fv.Float() <= currentFielVal.Float()
case reflect.Struct: case reflect.Struct:
if fv.Type() == reflect.TypeOf(time.Time{}) { if fv.Type() == reflect.TypeOf(time.Time{}) {
if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
panic("Bad Top Level field type") panic("Bad Top Level field type")
} }
t := topFieldVal.Interface().(time.Time) t := currentFielVal.Interface().(time.Time)
fieldTime := field.(time.Time) fieldTime := field.(time.Time)
return fieldTime.Before(t) || fieldTime.Equal(t) return fieldTime.Before(t) || fieldTime.Equal(t)
@ -591,45 +591,45 @@ func isLteField(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isLtField(val interface{}, field interface{}, param string) bool { func isLtField(top interface{}, current interface{}, field interface{}, param string) bool {
if val == nil { if current == nil {
panic("struct not passed for cross validation") panic("struct not passed for cross validation")
} }
topVal := reflect.ValueOf(val) currentVal := reflect.ValueOf(current)
if topVal.Kind() == reflect.Ptr && !topVal.IsNil() { if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
topVal = reflect.ValueOf(topVal.Elem().Interface()) currentVal = reflect.ValueOf(currentVal.Elem().Interface())
} }
var topFieldVal reflect.Value var currentFielVal reflect.Value
switch topVal.Kind() { switch currentVal.Kind() {
case reflect.Struct: case reflect.Struct:
if topVal.Type() == reflect.TypeOf(time.Time{}) { if currentVal.Type() == reflect.TypeOf(time.Time{}) {
topFieldVal = topVal currentFielVal = currentVal
break break
} }
f := topVal.FieldByName(param) f := currentVal.FieldByName(param)
if f.Kind() == reflect.Invalid { if f.Kind() == reflect.Invalid {
panic(fmt.Sprintf("Field \"%s\" not found in struct", param)) panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
} }
topFieldVal = f currentFielVal = f
default: default:
topFieldVal = topVal currentFielVal = currentVal
} }
if topFieldVal.Kind() == reflect.Ptr && !topFieldVal.IsNil() { if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
topFieldVal = reflect.ValueOf(topFieldVal.Elem().Interface()) currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
} }
fv := reflect.ValueOf(field) fv := reflect.ValueOf(field)
@ -638,25 +638,25 @@ func isLtField(val interface{}, field interface{}, param string) bool {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return fv.Int() < topFieldVal.Int() return fv.Int() < currentFielVal.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return fv.Uint() < topFieldVal.Uint() return fv.Uint() < currentFielVal.Uint()
case reflect.Float32, reflect.Float64: case reflect.Float32, reflect.Float64:
return fv.Float() < topFieldVal.Float() return fv.Float() < currentFielVal.Float()
case reflect.Struct: case reflect.Struct:
if fv.Type() == reflect.TypeOf(time.Time{}) { if fv.Type() == reflect.TypeOf(time.Time{}) {
if topFieldVal.Type() != reflect.TypeOf(time.Time{}) { if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
panic("Bad Top Level field type") panic("Bad Top Level field type")
} }
t := topFieldVal.Interface().(time.Time) t := currentFielVal.Interface().(time.Time)
fieldTime := field.(time.Time) fieldTime := field.(time.Time)
return fieldTime.Before(t) return fieldTime.Before(t)
@ -666,7 +666,7 @@ func isLtField(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isLte(val interface{}, field interface{}, param string) bool { func isLte(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -711,7 +711,7 @@ func isLte(val interface{}, field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field)) panic(fmt.Sprintf("Bad field type %T", field))
} }
func isLt(val interface{}, field interface{}, param string) bool { func isLt(top interface{}, current interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field) st := reflect.ValueOf(field)
@ -757,9 +757,9 @@ func isLt(val interface{}, field interface{}, param string) bool {
// value. For numbers, it's a simple lesser-than test; for // value. For numbers, it's a simple lesser-than test; for
// strings it tests the number of characters whereas for maps // strings it tests the number of characters whereas for maps
// and slices it tests the number of items. // and slices it tests the number of items.
func hasMaxOf(val interface{}, field interface{}, param string) bool { func hasMaxOf(top interface{}, current interface{}, field interface{}, param string) bool {
return isLte(val, field, param) return isLte(top, current, field, param)
} }
// asInt retuns the parameter as a int64 // asInt retuns the parameter as a int64

@ -3,8 +3,6 @@ Package validator implements value validations for structs and individual fields
Built In Validator Built In Validator
v3 no longer contains a built in Validator instance.
myValidator = validator.New("validate", validator.BakedInFunctions) myValidator = validator.New("validate", validator.BakedInFunctions)
errs := myValidator.ValidateStruct(//your struct) errs := myValidator.ValidateStruct(//your struct)
@ -79,7 +77,7 @@ Custom Functions
Custom functions can be added Custom functions can be added
//Structure //Structure
func customFunc(val interface{}, field interface{}, param string) bool { func customFunc(top interface{}, current interface{}, field interface{}, param string) bool {
if whatever { if whatever {
return false return false
@ -138,6 +136,10 @@ Bad Validator definitions are not handled by the library
Baked In Validators and Tags Baked In Validators and Tags
NOTE: Baked In Cross field validation only compares fields on the same struct,
if cross field + cross struct validation is needed your own custom validator
should be implemented.
Here is a list of the current built in validators: Here is a list of the current built in validators:
- -

@ -101,8 +101,12 @@ func (e *StructValidationErrors) Flatten() map[string]*FieldValidationError {
return errs return errs
} }
// ValidationFunc that accepts a value(optional usage), a field and parameter(optional usage) for use in validation // ValidationFunc accepts all values needed for file and cross field validation
type ValidationFunc func(val interface{}, v interface{}, param string) bool // top = top level struct when validating by struct otherwise nil
// current = current level struct when validating by struct otherwise optional comparison value
// f = field value for validation
// param = parameter used in validation i.e. gt=0 param would be 0
type ValidationFunc func(top interface{}, current interface{}, f interface{}, param string) bool
// Validator implements the Validator Struct // Validator implements the Validator Struct
// NOTE: Fields within are not thread safe and that is on purpose // NOTE: Fields within are not thread safe and that is on purpose
@ -153,11 +157,11 @@ func (v *Validator) AddFunction(key string, f ValidationFunc) error {
// ValidateStruct validates a struct and returns a struct containing the errors // ValidateStruct validates a struct and returns a struct containing the errors
func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors { func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
return v.validateStructRecursive(s, s) return v.validateStructRecursive(s, s, s)
} }
// validateStructRecursive validates a struct recursivly and passes the top level struct around for use in validator functions and returns a struct containing the errors // validateStructRecursive validates a struct recursivly and passes the top level struct around for use in validator functions and returns a struct containing the errors
func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *StructValidationErrors { func (v *Validator) validateStructRecursive(top interface{}, current interface{}, s interface{}) *StructValidationErrors {
structValue := reflect.ValueOf(s) structValue := reflect.ValueOf(s)
structType := reflect.TypeOf(s) structType := reflect.TypeOf(s)
@ -170,7 +174,7 @@ func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *Str
} }
if structValue.Kind() == reflect.Ptr && !structValue.IsNil() { if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
return v.validateStructRecursive(top, structValue.Elem().Interface()) return v.validateStructRecursive(top, current, structValue.Elem().Interface())
} }
if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface { if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
@ -209,7 +213,7 @@ func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *Str
if valueField.Type() == reflect.TypeOf(time.Time{}) { if valueField.Type() == reflect.TypeOf(time.Time{}) {
if fieldError := v.validateFieldByNameAndTagAndValue(top, valueField.Interface(), typeField.Name, tag); fieldError != nil { if fieldError := v.validateFieldByNameAndTagAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference // free up memory reference
fieldError = nil fieldError = nil
@ -221,7 +225,7 @@ func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *Str
continue continue
} }
if structErrors := v.validateStructRecursive(top, valueField.Interface()); structErrors != nil { if structErrors := v.validateStructRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
validationErrors.StructErrors[typeField.Name] = structErrors validationErrors.StructErrors[typeField.Name] = structErrors
// free up memory map no longer needed // free up memory map no longer needed
structErrors = nil structErrors = nil
@ -230,7 +234,7 @@ func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *Str
default: default:
if fieldError := v.validateFieldByNameAndTagAndValue(top, valueField.Interface(), typeField.Name, tag); fieldError != nil { if fieldError := v.validateFieldByNameAndTagAndValue(top, current, valueField.Interface(), typeField.Name, tag); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference // free up memory reference
fieldError = nil fieldError = nil
@ -254,17 +258,17 @@ func (v *Validator) ValidateFieldByTag(f interface{}, tag string) *FieldValidati
// ValidateFieldByTagAndValue allows validation of a single field, still using tag style validation to check multiple errors // ValidateFieldByTagAndValue allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validator) ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError { func (v *Validator) ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError {
return v.validateFieldByNameAndTagAndValue(val, f, "", tag) return v.validateFieldByNameAndTagAndValue(nil, val, f, "", tag)
} }
func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interface{}, name string, tag string) *FieldValidationError { func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, current interface{}, f interface{}, name string, tag string) *FieldValidationError {
// This is a double check if coming from ValidateStruct but need to be here in case function is called directly // This is a double check if coming from ValidateStruct but need to be here in case function is called directly
if tag == noValidationTag { if tag == noValidationTag {
return nil return nil
} }
if strings.Contains(tag, omitempty) && !hasValue(val, f, "") { if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") {
return nil return nil
} }
@ -272,7 +276,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interfa
fieldKind := valueField.Kind() fieldKind := valueField.Kind()
if fieldKind == reflect.Ptr && !valueField.IsNil() { if fieldKind == reflect.Ptr && !valueField.IsNil() {
return v.validateFieldByNameAndTagAndValue(val, valueField.Elem().Interface(), name, tag) return v.validateFieldByNameAndTagAndValue(val, current, valueField.Elem().Interface(), name, tag)
} }
fieldType := valueField.Type() fieldType := valueField.Type()
@ -300,7 +304,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interfa
for _, val := range orVals { for _, val := range orVals {
valErr, err = v.validateFieldByNameAndSingleTag(val, f, name, val) valErr, err = v.validateFieldByNameAndSingleTag(val, current, f, name, val)
if err == nil { if err == nil {
return nil return nil
@ -318,7 +322,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interfa
return valErr return valErr
} }
if valErr, err = v.validateFieldByNameAndSingleTag(val, f, name, valTag); err != nil { if valErr, err = v.validateFieldByNameAndSingleTag(val, current, f, name, valTag); err != nil {
valErr.Kind = valueField.Kind() valErr.Kind = valueField.Kind()
valErr.Type = fieldType valErr.Type = fieldType
@ -330,7 +334,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interfa
return nil return nil
} }
func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, f interface{}, name string, valTag string) (*FieldValidationError, error) { func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, current interface{}, f interface{}, name string, valTag string) (*FieldValidationError, error) {
vals := strings.Split(valTag, tagKeySeparator) vals := strings.Split(valTag, tagKeySeparator)
key := strings.Trim(vals[0], " ") key := strings.Trim(vals[0], " ")
@ -361,7 +365,7 @@ func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, f interface
param = strings.Trim(vals[1], " ") param = strings.Trim(vals[1], " ")
} }
if err := valFunc(val, f, param); !err { if err := valFunc(val, current, f, param); !err {
valErr.Param = param valErr.Param = param
return valErr, errors.New(key) return valErr, errors.New(key)
} }

@ -127,14 +127,14 @@ func AssertMapFieldError(s map[string]*validator.FieldValidationError, field str
c.Assert(val.ErrorTag, Equals, expectedTag) c.Assert(val.ErrorTag, Equals, expectedTag)
} }
func newValidatorFunc(val interface{}, field interface{}, param string) bool { func newValidatorFunc(val interface{}, current interface{}, field interface{}, param string) bool {
return true return true
} }
func isEqualFunc(val interface{}, field interface{}, param string) bool { func isEqualFunc(val interface{}, current interface{}, field interface{}, param string) bool {
return val.(string) == field.(string) return current.(string) == field.(string)
} }
func (ms *MySuite) TestStructOnlyValidation(c *C) { func (ms *MySuite) TestStructOnlyValidation(c *C) {

Loading…
Cancel
Save