Merge branch 'v3' into v3-development

pull/16/head
Dean Karn 10 years ago
commit 773ef71208
  1. 2
      README.md
  2. 37
      doc.go
  3. 51
      validator.go
  4. 430
      validator_test.go

@ -1,6 +1,6 @@
Package go-validate-yourself Package go-validate-yourself
================ ================
[![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v3-development)](https://travis-ci.org/joeybloggs/go-validate-yourself) [![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v3)](https://travis-ci.org/joeybloggs/go-validate-yourself)
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.

@ -3,15 +3,12 @@ Package validator implements value validations for structs and individual fields
Built In Validator Built In Validator
The package contains a built in Validator instance for use, v3 no longer contains a built in Validator instance.
but you may also create a new instance if needed.
// built in myValidator = validator.New("validate", validator.BakedInFunctions)
errs := validator.ValidateStruct(//your struct)
valErr := validator.ValidateFieldByTag(field, "omitempty,min=1,max=10")
// new errs := myValidator.ValidateStruct(//your struct)
newValidator = validator.New("struct tag name", validator.BakedInFunctions) valErr := myValidator.ValidateFieldByTag(field, "omitempty,min=1,max=10")
A simple example usage: A simple example usage:
@ -32,7 +29,7 @@ A simple example usage:
// errs will contain a hierarchical list of errors // errs will contain a hierarchical list of errors
// using the StructValidationErrors struct // using the StructValidationErrors struct
// or nil if no errors exist // or nil if no errors exist
errs := validator.ValidateStruct(user) errs := myValidator.ValidateStruct(user)
// in this case 1 error Name is required // in this case 1 error Name is required
errs.Struct will be "User" errs.Struct will be "User"
@ -68,7 +65,7 @@ I needed to know the field and what validation failed so that I could provide an
return "Translated string based on field" return "Translated string based on field"
} }
The hierarchical structure is hard to work with sometimes.. Agreed Flatten function to the rescue! The hierarchical error structure is hard to work with sometimes.. Agreed Flatten function to the rescue!
Flatten will return a map of FieldValidationError's but the field name will be namespaced. Flatten will return a map of FieldValidationError's but the field name will be namespaced.
// if UserDetail Details field failed validation // if UserDetail Details field failed validation
@ -91,7 +88,7 @@ Custom functions can be added
return true return true
} }
validator.AddFunction("custom tag name", customFunc) myValidator.AddFunction("custom tag name", customFunc)
// NOTES: using the same tag name as an existing function // NOTES: using the same tag name as an existing function
// will overwrite the existing one // will overwrite the existing one
@ -99,16 +96,16 @@ Cross Field Validation
Cross Field Validation can be implemented, for example Start & End Date range validation Cross Field Validation can be implemented, for example Start & End Date range validation
// NOTE: when calling validator.validateStruct(val) val will be the top level struct passed // NOTE: when calling myValidator.validateStruct(val) val will be the top level struct passed
// into the function // into the function
// when calling validator.ValidateFieldByTagAndValue(val, field, tag) val will be // when calling myValidator.ValidateFieldByTagAndValue(val, field, tag) val will be
// whatever you pass, struct, field... // whatever you pass, struct, field...
// when calling validator.ValidateFieldByTag(field, tag) val will be nil // when calling myValidator.ValidateFieldByTag(field, tag) val will be nil
// //
// Because of the specific requirements and field names within each persons project that // Because of the specific requirements and field names within each persons project that
// uses this library it is unlikely that any baked in function for this type of validation // uses this library it is likely that custom functions will need to be created.
// would be added, but you can add your own custom ones and keep all your validation logic // however there are some build in Generic Cross Field validation, see Baked In Validators and
// in one place. // Tags below
func isDateRangeValid(val interface{}, field interface{}, param string) bool { func isDateRangeValid(val interface{}, field interface{}, param string) bool {
@ -121,12 +118,6 @@ Cross Field Validation can be implemented, for example Start & End Date range va
return true return true
} }
Custom Tag Name
A custom tag name can be set to avoid conficts, or just have a shorter name
validator.SetTag("valid")
Multiple Validators Multiple Validators
Multiple validators on a field will process in the order defined Multiple validators on a field will process in the order defined
@ -325,6 +316,6 @@ This package panics when bad input is provided, this is by design, bad code like
TestField: "Test" TestField: "Test"
} }
validator.ValidateStruct(t) // this will panic myValidator.ValidateStruct(t) // this will panic
*/ */
package validator package validator

@ -18,7 +18,10 @@ import (
) )
const ( const (
defaultTagName = "validate" tagSeparator = ","
orSeparator = "|"
noValidationTag = "-"
tagKeySeparator = "="
omitempty = "omitempty" omitempty = "omitempty"
validationFieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n" validationFieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag\n"
validationStructErrMsg = "Struct:%s\n" validationStructErrMsg = "Struct:%s\n"
@ -111,10 +114,6 @@ type Validator struct {
validationFuncs map[string]ValidationFunc validationFuncs map[string]ValidationFunc
} }
// var bakedInValidators = map[string]ValidationFunc{}
var internalValidator = NewValidator(defaultTagName, BakedInValidators)
// NewValidator creates a new Validator instance // NewValidator creates a new Validator instance
// NOTE: it is not necessary to create a new validator as the internal one will do in 99.9% of cases, but the option is there. // NOTE: it is not necessary to create a new validator as the internal one will do in 99.9% of cases, but the option is there.
func NewValidator(tagName string, funcs map[string]ValidationFunc) *Validator { func NewValidator(tagName string, funcs map[string]ValidationFunc) *Validator {
@ -124,21 +123,11 @@ func NewValidator(tagName string, funcs map[string]ValidationFunc) *Validator {
} }
} }
// SetTag sets the baked in Validator's tagName to one of your choosing
func SetTag(tagName string) {
internalValidator.SetTag(tagName)
}
// SetTag sets tagName of the Validator to one of your choosing // SetTag sets tagName of the Validator to one of your choosing
func (v *Validator) SetTag(tagName string) { func (v *Validator) SetTag(tagName string) {
v.tagName = tagName v.tagName = tagName
} }
// AddFunction adds a ValidationFunc to the baked in Validator's map of validators denoted by the key
func AddFunction(key string, f ValidationFunc) error {
return internalValidator.AddFunction(key, f)
}
// AddFunction adds a ValidationFunc to a Validator's map of validators denoted by the key // AddFunction adds a ValidationFunc to a Validator's map of validators denoted by the key
func (v *Validator) AddFunction(key string, f ValidationFunc) error { func (v *Validator) AddFunction(key string, f ValidationFunc) error {
@ -160,12 +149,6 @@ func (v *Validator) AddFunction(key string, f ValidationFunc) error {
return nil return nil
} }
// ValidateStruct validates a struct and returns a struct containing the errors
func ValidateStruct(s interface{}) *StructValidationErrors {
return internalValidator.ValidateStruct(s)
}
// 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 {
@ -206,7 +189,7 @@ func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *Str
tag := typeField.Tag.Get(v.tagName) tag := typeField.Tag.Get(v.tagName)
if tag == "-" { if tag == noValidationTag {
continue continue
} }
@ -257,24 +240,12 @@ func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *Str
return validationErrors return validationErrors
} }
// ValidateFieldByTag allows validation of a single field with the internal validator, still using tag style validation to check multiple errors
func ValidateFieldByTag(f interface{}, tag string) *FieldValidationError {
return internalValidator.ValidateFieldByTag(f, tag)
}
// ValidateFieldByTag allows validation of a single field, still using tag style validation to check multiple errors // ValidateFieldByTag allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validator) ValidateFieldByTag(f interface{}, tag string) *FieldValidationError { func (v *Validator) ValidateFieldByTag(f interface{}, tag string) *FieldValidationError {
return v.ValidateFieldByTagAndValue(nil, f, tag) return v.ValidateFieldByTagAndValue(nil, f, tag)
} }
// ValidateFieldByTagAndValue allows validation of a single field with the internal validator, still using tag style validation to check multiple errors
func ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError {
return internalValidator.ValidateFieldByTagAndValue(val, f, tag)
}
// 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 {
@ -284,7 +255,7 @@ func (v *Validator) ValidateFieldByTagAndValue(val interface{}, f interface{}, t
func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interface{}, name string, tag string) *FieldValidationError { func (v *Validator) validateFieldByNameAndTagAndValue(val 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 == "-" { if tag == noValidationTag {
return nil return nil
} }
@ -312,11 +283,11 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interfa
var valErr *FieldValidationError var valErr *FieldValidationError
var err error var err error
valTags := strings.Split(tag, ",") valTags := strings.Split(tag, tagSeparator)
for _, valTag := range valTags { for _, valTag := range valTags {
orVals := strings.Split(valTag, "|") orVals := strings.Split(valTag, orSeparator)
if len(orVals) > 1 { if len(orVals) > 1 {
@ -330,11 +301,11 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interfa
return nil return nil
} }
errTag += "|" + valErr.ErrorTag errTag += orSeparator + valErr.ErrorTag
} }
errTag = strings.TrimLeft(errTag, "|") errTag = strings.TrimLeft(errTag, orSeparator)
valErr.ErrorTag = errTag valErr.ErrorTag = errTag
valErr.Kind = fieldKind valErr.Kind = fieldKind
@ -356,7 +327,7 @@ func (v *Validator) validateFieldByNameAndTagAndValue(val interface{}, f interfa
func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, f interface{}, name string, valTag string) (*FieldValidationError, error) { func (v *Validator) validateFieldByNameAndSingleTag(val interface{}, f interface{}, name string, valTag string) (*FieldValidationError, error) {
vals := strings.Split(valTag, "=") vals := strings.Split(valTag, tagKeySeparator)
key := strings.Trim(vals[0], " ") key := strings.Trim(vals[0], " ")
if len(key) == 0 { if len(key) == 0 {

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save