@ -1,6 +1,7 @@
package validator
import (
"context"
"errors"
"fmt"
"reflect"
@ -58,10 +59,10 @@ type Validate struct {
hasCustomFuncs bool
hasTagNameFunc bool
tagNameFunc TagNameFunc
structLevelFuncs map [ reflect . Type ] StructLevelFunc
structLevelFuncs map [ reflect . Type ] StructLevelFuncCtx
customFuncs map [ reflect . Type ] CustomTypeFunc
aliases map [ string ] string
validations map [ string ] Func
validations map [ string ] FuncCtx
transTagFunc map [ ut . Translator ] map [ string ] TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
tagCache * tagCache
structCache * structCache
@ -79,7 +80,7 @@ func New() *Validate {
v := & Validate {
tagName : defaultTagName ,
aliases : make ( map [ string ] string , len ( bakedInAliases ) ) ,
validations : make ( map [ string ] Func , len ( bakedInValidators ) ) ,
validations : make ( map [ string ] FuncCtx , len ( bakedInValidators ) ) ,
tagCache : tc ,
structCache : sc ,
}
@ -92,8 +93,8 @@ func New() *Validate {
// must copy validators for separate validations to be used in each instance
for k , val := range bakedInValidators {
// no need to error check here, baked in will alwaa ys be valid
v . registerValidation ( k , val , true )
// no need to error check here, baked in will always be valid
v . registerValidation ( k , wrapFunc ( val ) , true )
}
v . pool = & sync . Pool {
@ -128,10 +129,16 @@ func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
// - if the key already exists, the previous validation function will be replaced.
// - this method is not thread-safe it is intended that these all be registered prior to any validation
func ( v * Validate ) RegisterValidation ( tag string , fn Func ) error {
return v . RegisterValidationCtx ( tag , wrapFunc ( fn ) )
}
// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
// allowing context.Context validation support.
func ( v * Validate ) RegisterValidationCtx ( tag string , fn FuncCtx ) error {
return v . registerValidation ( tag , fn , false )
}
func ( v * Validate ) registerValidation ( tag string , fn Func , bakedIn bool ) error {
func ( v * Validate ) registerValidation ( tag string , fn FuncCtx , bakedIn bool ) error {
if len ( tag ) == 0 {
return errors . New ( "Function Key cannot be empty" )
@ -177,9 +184,22 @@ func (v *Validate) RegisterAlias(alias, tags string) {
// a struct out of your control's validation to be overridden
// - this method is not thread-safe it is intended that these all be registered prior to any validation
func ( v * Validate ) RegisterStructValidation ( fn StructLevelFunc , types ... interface { } ) {
v . RegisterStructValidationCtx ( wrapStructLevelFunc ( fn ) , types ... )
}
// RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing
// of contextual validation information via context.Context.
// This is akin to implementing a 'Validatable' interface, but for structs for which
// you may not have access or rights to change.
//
// NOTES:
// - if this and the 'Validatable' interface are implemented the Struct Level takes precedence as to enable
// a struct out of your control's validation to be overridden
// - this method is not thread-safe it is intended that these all be registered prior to any validation
func ( v * Validate ) RegisterStructValidationCtx ( fn StructLevelFuncCtx , types ... interface { } ) {
if v . structLevelFuncs == nil {
v . structLevelFuncs = make ( map [ reflect . Type ] StructLevelFunc )
v . structLevelFuncs = make ( map [ reflect . Type ] StructLevelFuncCtx )
}
for _ , t := range types {
@ -229,7 +249,16 @@ func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, register
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) Struct ( s interface { } ) ( err error ) {
func ( v * Validate ) Struct ( s interface { } ) error {
return v . StructCtx ( context . Background ( ) , s )
}
// StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified
// and also allows passing of context.Context for contextual validation information.
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) StructCtx ( ctx context . Context , s interface { } ) ( err error ) {
val := reflect . ValueOf ( s )
top := val
@ -248,7 +277,7 @@ func (v *Validate) Struct(s interface{}) (err error) {
vd . isPartial = false
// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
vd . validateStruct ( top , val , val . Type ( ) , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
vd . validateStruct ( ctx , top , val , val . Type ( ) , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
if len ( vd . errs ) > 0 {
err = vd . errs
@ -265,8 +294,17 @@ func (v *Validate) Struct(s interface{}) (err error) {
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) StructFiltered ( s interface { } , fn FilterFunc ) ( err error ) {
func ( v * Validate ) StructFiltered ( s interface { } , fn FilterFunc ) error {
return v . StructFilteredCtx ( context . Background ( ) , s , fn )
}
// StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates
// nested structs, unless otherwise specified and also allows passing of contextual validation information via
// context.Context
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) StructFilteredCtx ( ctx context . Context , s interface { } , fn FilterFunc ) ( err error ) {
val := reflect . ValueOf ( s )
top := val
@ -285,7 +323,7 @@ func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) (err error) {
vd . ffn = fn
// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
vd . validateStruct ( top , val , val . Type ( ) , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
vd . validateStruct ( context . Background ( ) , top , val , val . Type ( ) , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
if len ( vd . errs ) > 0 {
err = vd . errs
@ -303,8 +341,18 @@ func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) (err error) {
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) StructPartial ( s interface { } , fields ... string ) ( err error ) {
func ( v * Validate ) StructPartial ( s interface { } , fields ... string ) error {
return v . StructPartialCtx ( context . Background ( ) , s , fields ... )
}
// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
// validation validation information via context.Context
// Fields may be provided in a namespaced fashion relative to the struct provided
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) StructPartialCtx ( ctx context . Context , s interface { } , fields ... string ) ( err error ) {
val := reflect . ValueOf ( s )
top := val
@ -364,7 +412,7 @@ func (v *Validate) StructPartial(s interface{}, fields ...string) (err error) {
}
}
vd . validateStruct ( top , val , typ , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
vd . validateStruct ( ctx , top , val , typ , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
if len ( vd . errs ) > 0 {
err = vd . errs
@ -382,8 +430,18 @@ func (v *Validate) StructPartial(s interface{}, fields ...string) (err error) {
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) StructExcept ( s interface { } , fields ... string ) ( err error ) {
func ( v * Validate ) StructExcept ( s interface { } , fields ... string ) error {
return v . StructExceptCtx ( context . Background ( ) , s , fields ... )
}
// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
// validation validation information via context.Context
// Fields may be provided in a namespaced fashion relative to the struct provided
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func ( v * Validate ) StructExceptCtx ( ctx context . Context , s interface { } , fields ... string ) ( err error ) {
val := reflect . ValueOf ( s )
top := val
@ -419,7 +477,7 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) {
vd . includeExclude [ string ( vd . misc ) ] = struct { } { }
}
vd . validateStruct ( top , val , typ , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
vd . validateStruct ( ctx , top , val , typ , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , nil )
if len ( vd . errs ) > 0 {
err = vd . errs
@ -443,8 +501,24 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) {
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
// validate Array, Slice and maps fields which may contain more than one error
func ( v * Validate ) Var ( field interface { } , tag string ) ( err error ) {
func ( v * Validate ) Var ( field interface { } , tag string ) error {
return v . VarCtx ( context . Background ( ) , field , tag )
}
// VarCtx validates a single variable using tag style validation and allows passing of contextual
// validation validation information via context.Context.
// eg.
// var i int
// validate.Var(i, "gt=1,lt=10")
//
// WARNING: a struct can be passed for validation eg. time.Time is a struct or if you have a custom type and have registered
// a custom type handler, so must allow it; however unforseen validations will occur if trying to validate a struct
// that is meant to be passed to 'validate.Struct'
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
// validate Array, Slice and maps fields which may contain more than one error
func ( v * Validate ) VarCtx ( ctx context . Context , field interface { } , tag string ) ( err error ) {
if len ( tag ) == 0 || tag == skipValidationTag {
return nil
}
@ -470,7 +544,7 @@ func (v *Validate) Var(field interface{}, tag string) (err error) {
vd . top = val
vd . isPartial = false
vd . traverseField ( val , val , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , defaultCField , ctag )
vd . traverseField ( ctx , val , val , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , defaultCField , ctag )
if len ( vd . errs ) > 0 {
err = vd . errs
@ -495,8 +569,25 @@ func (v *Validate) Var(field interface{}, tag string) (err error) {
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
// validate Array, Slice and maps fields which may contain more than one error
func ( v * Validate ) VarWithValue ( field interface { } , other interface { } , tag string ) ( err error ) {
func ( v * Validate ) VarWithValue ( field interface { } , other interface { } , tag string ) error {
return v . VarWithValueCtx ( context . Background ( ) , field , other , tag )
}
// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
// allows passing of contextual validation validation information via context.Context.
// eg.
// s1 := "abcd"
// s2 := "abcd"
// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
//
// WARNING: a struct can be passed for validation eg. time.Time is a struct or if you have a custom type and have registered
// a custom type handler, so must allow it; however unforseen validations will occur if trying to validate a struct
// that is meant to be passed to 'validate.Struct'
//
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
// validate Array, Slice and maps fields which may contain more than one error
func ( v * Validate ) VarWithValueCtx ( ctx context . Context , field interface { } , other interface { } , tag string ) ( err error ) {
if len ( tag ) == 0 || tag == skipValidationTag {
return nil
}
@ -522,7 +613,7 @@ func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string
vd . top = otherVal
vd . isPartial = false
vd . traverseField ( otherVal , reflect . ValueOf ( field ) , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , defaultCField , ctag )
vd . traverseField ( ctx , otherVal , reflect . ValueOf ( field ) , vd . ns [ 0 : 0 ] , vd . actualNs [ 0 : 0 ] , defaultCField , ctag )
if len ( vd . errs ) > 0 {
err = vd . errs