@ -2,7 +2,7 @@
* Package validator
* Package validator
*
*
* MISC :
* MISC :
* - anonymous structs - they don ' t have names so expect the Struct name within StructValidation Errors to be blank
* - anonymous structs - they don ' t have names so expect the Struct name within StructErrors to be blank
*
*
* /
* /
@ -25,15 +25,15 @@ const (
tagKeySeparator = "="
tagKeySeparator = "="
structOnlyTag = "structonly"
structOnlyTag = "structonly"
omitempty = "omitempty"
omitempty = "omitempty"
validationFieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n"
fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n"
validationStructErrMsg = "Struct:%s\n"
structErrMsg = "Struct:%s\n"
)
)
// FieldValidation Error contains a single field's validation error along
// FieldError contains a single field's validation error along
// with other properties that may be needed for error message creation
// with other properties that may be needed for error message creation
type FieldValidation Error struct {
type FieldError struct {
Field string
Field string
Error Tag string
Tag string
Kind reflect . Kind
Kind reflect . Kind
Type reflect . Type
Type reflect . Type
Param string
Param string
@ -41,27 +41,27 @@ type FieldValidationError struct {
}
}
// This is intended for use in development + debugging and not intended to be a production error message.
// This is intended for use in development + debugging and not intended to be a production error message.
// it also allows FieldValidation Error to be used as an Error interface
// it also allows FieldError to be used as an Error interface
func ( e * FieldValidation Error ) Error ( ) string {
func ( e * FieldError ) Error ( ) string {
return fmt . Sprintf ( validationF ieldErrMsg, e . Field , e . Error Tag)
return fmt . Sprintf ( f ieldErrMsg, e . Field , e . Tag )
}
}
// StructValidation Errors is hierarchical list of field and struct validation errors
// StructErrors is hierarchical list of field and struct validation errors
// for a non hierarchical representation please see the Flatten method for StructValidation Errors
// for a non hierarchical representation please see the Flatten method for StructErrors
type StructValidation Errors struct {
type StructErrors struct {
// Name of the Struct
// Name of the Struct
Struct string
Struct string
// Struct Field Errors
// Struct Field Errors
Errors map [ string ] * FieldValidation Error
Errors map [ string ] * FieldError
// Struct Fields of type struct and their errors
// Struct Fields of type struct and their errors
// key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank
// key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank
StructErrors map [ string ] * StructValidation Errors
StructErrors map [ string ] * StructErrors
}
}
// This is intended for use in development + debugging and not intended to be a production error message.
// This is intended for use in development + debugging and not intended to be a production error message.
// it also allows StructValidation Errors to be used as an Error interface
// it also allows StructErrors to be used as an Error interface
func ( e * StructValidation Errors ) Error ( ) string {
func ( e * StructErrors ) Error ( ) string {
buff := bytes . NewBufferString ( fmt . Sprintf ( validationS tructErrMsg, e . Struct ) )
buff := bytes . NewBufferString ( fmt . Sprintf ( s tructErrMsg, e . Struct ) )
for _ , err := range e . Errors {
for _ , err := range e . Errors {
buff . WriteString ( err . Error ( ) )
buff . WriteString ( err . Error ( ) )
@ -74,15 +74,15 @@ func (e *StructValidationErrors) Error() string {
return buff . String ( )
return buff . String ( )
}
}
// Flatten flattens the StructValidation Errors hierarchical structure into a flat namespace style field name
// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name
// for those that want/need it
// for those that want/need it
func ( e * StructValidation Errors ) Flatten ( ) map [ string ] * FieldValidation Error {
func ( e * StructErrors ) Flatten ( ) map [ string ] * FieldError {
if e == nil {
if e == nil {
return nil
return nil
}
}
errs := map [ string ] * FieldValidation Error { }
errs := map [ string ] * FieldError { }
for _ , f := range e . Errors {
for _ , f := range e . Errors {
@ -103,27 +103,27 @@ func (e *StructValidationErrors) Flatten() map[string]*FieldValidationError {
return errs
return errs
}
}
// Validation Func accepts all values needed for file and cross field validation
// Func accepts all values needed for file and cross field validation
// top = top level struct when validating by struct otherwise nil
// top = top level struct when validating by struct otherwise nil
// current = current level struct when validating by struct otherwise optional comparison value
// current = current level struct when validating by struct otherwise optional comparison value
// f = field value for validation
// f = field value for validation
// param = parameter used in validation i.e. gt=0 param would be 0
// param = parameter used in validation i.e. gt=0 param would be 0
type Validation Func func ( top interface { } , current interface { } , f interface { } , param string ) bool
type Func func ( top interface { } , current interface { } , f interface { } , param string ) bool
// Validator implements the Validator Struct
// Validate implements the Validate 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
// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
// or make it thread safe on your end
// or make it thread safe on your end
type Validator struct {
type Validate struct {
// tagName being used.
// tagName being used.
tagName string
tagName string
// validation Funcs is a map of validation functions and the tag keys
// validate Funcs is a map of validation functions and the tag keys
validationFuncs map [ string ] Validation Func
validationFuncs map [ string ] Func
}
}
// NewValidator creates a new Validator instance for use.
// New creates a new Validate instance for use.
func NewValidator ( tagName string , funcs map [ string ] Validation Func) * Validator {
func New ( tagName string , funcs map [ string ] Func ) * Validate {
return & Validator {
return & Validate {
tagName : tagName ,
tagName : tagName ,
validationFuncs : funcs ,
validationFuncs : funcs ,
}
}
@ -131,13 +131,13 @@ func NewValidator(tagName string, funcs map[string]ValidationFunc) *Validator {
// SetTag sets tagName of the Validator to one of your choosing after creation
// SetTag sets tagName of the Validator to one of your choosing after creation
// perhaps to dodge a tag name conflict in a specific section of code
// perhaps to dodge a tag name conflict in a specific section of code
func ( v * Validator ) SetTag ( tagName string ) {
func ( v * Validate ) SetTag ( tagName string ) {
v . tagName = tagName
v . tagName = tagName
}
}
// AddFunction adds a ValidationFunc to a Validator 's map of validators denoted by the key
// AddFunction adds a validation Func to a Validate 's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced.
// NOTE: if the key already exists, it will get replaced.
func ( v * Validator ) AddFunction ( key string , f Validation Func) error {
func ( v * Validate ) AddFunction ( key string , f Func ) error {
if len ( key ) == 0 {
if len ( key ) == 0 {
return errors . New ( "Function Key cannot be empty" )
return errors . New ( "Function Key cannot be empty" )
@ -152,34 +152,34 @@ func (v *Validator) AddFunction(key string, f ValidationFunc) error {
return nil
return nil
}
}
// Validate Struct validates a struct, even it's nested structs, and returns a struct containing the errors
// Struct validates a struct, even it's nested structs, and returns a struct containing the errors
// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
// the Array or Map.
// the Array or Map.
func ( v * Validator ) Validate Struct ( s interface { } ) * StructValidation Errors {
func ( v * Validate ) Struct ( s interface { } ) * StructErrors {
return v . validateS tructRecursive( s , s , s )
return v . s tructRecursive( s , s , s )
}
}
// validateS tructRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors
// s tructRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors
func ( v * Validator ) validateS tructRecursive ( top interface { } , current interface { } , s interface { } ) * StructValidation Errors {
func ( v * Validate ) s tructRecursive ( top interface { } , current interface { } , s interface { } ) * StructErrors {
structValue := reflect . ValueOf ( s )
structValue := reflect . ValueOf ( s )
structType := reflect . TypeOf ( s )
structType := reflect . TypeOf ( s )
structName := structType . Name ( )
structName := structType . Name ( )
if structValue . Kind ( ) == reflect . Ptr && ! structValue . IsNil ( ) {
if structValue . Kind ( ) == reflect . Ptr && ! structValue . IsNil ( ) {
return v . validateS tructRecursive( top , current , structValue . Elem ( ) . Interface ( ) )
return v . s tructRecursive( top , current , structValue . Elem ( ) . Interface ( ) )
}
}
if structValue . Kind ( ) != reflect . Struct && structValue . Kind ( ) != reflect . Interface {
if structValue . Kind ( ) != reflect . Struct && structValue . Kind ( ) != reflect . Interface {
panic ( "interface passed for validation is not a struct" )
panic ( "interface passed for validation is not a struct" )
}
}
validationErrors := & StructValidation Errors {
validationErrors := & StructErrors {
Struct : structName ,
Struct : structName ,
Errors : map [ string ] * FieldValidation Error { } ,
Errors : map [ string ] * FieldError { } ,
StructErrors : map [ string ] * StructValidation Errors { } ,
StructErrors : map [ string ] * StructErrors { } ,
}
}
var numFields = structValue . NumField ( )
var numFields = structValue . NumField ( )
@ -214,7 +214,7 @@ func (v *Validator) validateStructRecursive(top interface{}, current interface{}
if valueField . Type ( ) == reflect . TypeOf ( time . Time { } ) {
if valueField . Type ( ) == reflect . TypeOf ( time . Time { } ) {
if fieldError := v . validateFieldByNameAndTag AndValue( top , current , valueField . Interface ( ) , typeField . Name , tag ) ; fieldError != nil {
if fieldError := v . fieldWithName AndValue( 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
@ -226,7 +226,7 @@ func (v *Validator) validateStructRecursive(top interface{}, current interface{}
continue
continue
}
}
if structErrors := v . validateS tructRecursive( top , valueField . Interface ( ) , valueField . Interface ( ) ) ; structErrors != nil {
if structErrors := v . s tructRecursive( 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
@ -235,7 +235,7 @@ func (v *Validator) validateStructRecursive(top interface{}, current interface{}
default :
default :
if fieldError := v . validateFieldByNameAndTag AndValue( top , current , valueField . Interface ( ) , typeField . Name , tag ) ; fieldError != nil {
if fieldError := v . fieldWithName AndValue( 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
@ -250,21 +250,21 @@ func (v *Validator) validateStructRecursive(top interface{}, current interface{}
return validationErrors
return validationErrors
}
}
// Validate FieldByTag allows validation of a single field, still using tag style validation to check multiple errors
// Field allows validation of a single field, still using tag style validation to check multiple errors
func ( v * Validator ) ValidateFieldByTag ( f interface { } , tag string ) * FieldValidation Error {
func ( v * Validate ) Field ( f interface { } , tag string ) * FieldError {
return v . ValidateFieldByTagAnd Value( nil , f , tag )
return v . FieldWith Value( nil , f , tag )
}
}
// ValidateFieldByTagAnd Value allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
// FieldWith Value allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
func ( v * Validator ) ValidateFieldByTagAnd Value ( val interface { } , f interface { } , tag string ) * FieldValidation Error {
func ( v * Validate ) FieldWith Value ( val interface { } , f interface { } , tag string ) * FieldError {
return v . validateFieldByNameAndTag AndValue( nil , val , f , "" , tag )
return v . fieldWithName AndValue( nil , val , f , "" , tag )
}
}
func ( v * Validator ) validateFieldByNameAndTag AndValue ( val interface { } , current interface { } , f interface { } , name string , tag string ) * FieldValidation Error {
func ( v * Validate ) fieldWithName AndValue ( val interface { } , current interface { } , f interface { } , name string , tag string ) * FieldError {
// This is a double check if coming from Validate Struct but need to be here in case function is called directly
// This is a double check if coming from validate. Struct but need to be here in case function is called directly
if tag == noValidationTag {
if tag == noValidationTag {
return nil
return nil
}
}
@ -277,7 +277,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, current i
fieldKind := valueField . Kind ( )
fieldKind := valueField . Kind ( )
if fieldKind == reflect . Ptr && ! valueField . IsNil ( ) {
if fieldKind == reflect . Ptr && ! valueField . IsNil ( ) {
return v . validateFieldByNameAndTag AndValue( val , current , valueField . Elem ( ) . Interface ( ) , name , tag )
return v . fieldWithName AndValue( val , current , valueField . Elem ( ) . Interface ( ) , name , tag )
}
}
fieldType := valueField . Type ( )
fieldType := valueField . Type ( )
@ -291,7 +291,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, current i
}
}
}
}
var valErr * FieldValidation Error
var valErr * FieldError
var err error
var err error
valTags := strings . Split ( tag , tagSeparator )
valTags := strings . Split ( tag , tagSeparator )
@ -305,25 +305,25 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, current i
for _ , val := range orVals {
for _ , val := range orVals {
valErr , err = v . validateFieldBy NameAndSingleTag( val , current , f , name , val )
valErr , err = v . fieldWith NameAndSingleTag( val , current , f , name , val )
if err == nil {
if err == nil {
return nil
return nil
}
}
errTag += orSeparator + valErr . Error Tag
errTag += orSeparator + valErr . Tag
}
}
errTag = strings . TrimLeft ( errTag , orSeparator )
errTag = strings . TrimLeft ( errTag , orSeparator )
valErr . Error Tag = errTag
valErr . Tag = errTag
valErr . Kind = fieldKind
valErr . Kind = fieldKind
return valErr
return valErr
}
}
if valErr , err = v . validateFieldBy NameAndSingleTag( val , current , f , name , valTag ) ; err != nil {
if valErr , err = v . fieldWith NameAndSingleTag( val , current , f , name , valTag ) ; err != nil {
valErr . Kind = valueField . Kind ( )
valErr . Kind = valueField . Kind ( )
valErr . Type = fieldType
valErr . Type = fieldType
@ -335,7 +335,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, current i
return nil
return nil
}
}
func ( v * Validator ) validateFieldBy NameAndSingleTag ( val interface { } , current interface { } , f interface { } , name string , valTag string ) ( * FieldValidation Error , error ) {
func ( v * Validate ) fieldWith NameAndSingleTag ( val interface { } , current interface { } , f interface { } , name string , valTag string ) ( * FieldError , error ) {
vals := strings . Split ( valTag , tagKeySeparator )
vals := strings . Split ( valTag , tagKeySeparator )
key := strings . Trim ( vals [ 0 ] , " " )
key := strings . Trim ( vals [ 0 ] , " " )
@ -344,9 +344,9 @@ func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, current int
panic ( fmt . Sprintf ( "Invalid validation tag on field %s" , name ) )
panic ( fmt . Sprintf ( "Invalid validation tag on field %s" , name ) )
}
}
valErr := & FieldValidation Error {
valErr := & FieldError {
Field : name ,
Field : name ,
Error Tag: key ,
Tag : key ,
Value : f ,
Value : f ,
Param : "" ,
Param : "" ,
}
}