Merge branch 'v2' into v2-development

pull/16/head
Dean Karn 9 years ago
commit e0bf69bca2
  1. 10
      README.md
  2. 46
      baked_in.go
  3. 32
      doc.go
  4. 52
      validator.go
  5. 26
      validator_test.go

@ -1,6 +1,6 @@
Package go-validate-yourself
================
[![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v2-development)](https://travis-ci.org/joeybloggs/go-validate-yourself)
[![Build Status](https://travis-ci.org/joeybloggs/go-validate-yourself.svg?branch=v2)](https://travis-ci.org/joeybloggs/go-validate-yourself)
Package validator implements value validations for structs and individual fields based on tags.
@ -9,20 +9,20 @@ Installation
Just use go get.
go get gopkg.in/joeybloggs/go-validate-yourself.v1
go get gopkg.in/joeybloggs/go-validate-yourself.v2
or to update
go get -u gopkg.in/joeybloggs/go-validate-yourself.v1
go get -u gopkg.in/joeybloggs/go-validate-yourself.v2
And then just import the package into your own code.
import "gopkg.in/joeybloggs/go-validate-yourself.v1"
import "gopkg.in/joeybloggs/go-validate-yourself.v2"
Usage
=====
Please see http://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v1 for detailed usage docs.
Please see http://godoc.org/gopkg.in/joeybloggs/go-validate-yourself.v2 for detailed usage docs.
Contributing
============

@ -34,7 +34,7 @@ var BakedInValidators = map[string]ValidationFunc{
"uri": isURI,
}
func isURI(field interface{}, param string) bool {
func isURI(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -49,7 +49,7 @@ func isURI(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isURL(field interface{}, param string) bool {
func isURL(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -72,7 +72,7 @@ func isURL(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isEmail(field interface{}, param string) bool {
func isEmail(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -85,7 +85,7 @@ func isEmail(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isHsla(field interface{}, param string) bool {
func isHsla(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -98,7 +98,7 @@ func isHsla(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isHsl(field interface{}, param string) bool {
func isHsl(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -111,7 +111,7 @@ func isHsl(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isRgba(field interface{}, param string) bool {
func isRgba(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -124,7 +124,7 @@ func isRgba(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isRgb(field interface{}, param string) bool {
func isRgb(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -137,7 +137,7 @@ func isRgb(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isHexcolor(field interface{}, param string) bool {
func isHexcolor(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -150,7 +150,7 @@ func isHexcolor(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isHexadecimal(field interface{}, param string) bool {
func isHexadecimal(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -163,7 +163,7 @@ func isHexadecimal(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isNumber(field interface{}, param string) bool {
func isNumber(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -176,7 +176,7 @@ func isNumber(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isNumeric(field interface{}, param string) bool {
func isNumeric(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -189,7 +189,7 @@ func isNumeric(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isAlphanum(field interface{}, param string) bool {
func isAlphanum(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -202,7 +202,7 @@ func isAlphanum(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isAlpha(field interface{}, param string) bool {
func isAlpha(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -215,7 +215,7 @@ func isAlpha(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func hasValue(field interface{}, param string) bool {
func hasValue(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -229,7 +229,7 @@ func hasValue(field interface{}, param string) bool {
}
}
func isGte(field interface{}, param string) bool {
func isGte(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -274,7 +274,7 @@ func isGte(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isGt(field interface{}, param string) bool {
func isGt(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -318,7 +318,7 @@ func isGt(field interface{}, param string) bool {
// 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(field interface{}, param string) bool {
func hasLengthOf(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -357,12 +357,12 @@ func hasLengthOf(field interface{}, param string) bool {
// 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(field interface{}, param string) bool {
func hasMinOf(val interface{}, field interface{}, param string) bool {
return isGte(field, param)
return isGte(val, field, param)
}
func isLte(field interface{}, param string) bool {
func isLte(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -407,7 +407,7 @@ func isLte(field interface{}, param string) bool {
panic(fmt.Sprintf("Bad field type %T", field))
}
func isLt(field interface{}, param string) bool {
func isLt(val interface{}, field interface{}, param string) bool {
st := reflect.ValueOf(field)
@ -453,9 +453,9 @@ func isLt(field interface{}, param string) bool {
// 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(field interface{}, param string) bool {
func hasMaxOf(val interface{}, field interface{}, param string) bool {
return isLte(field, param)
return isLte(val, field, param)
}
// asInt retuns the parameter as a int64

@ -82,7 +82,7 @@ Custom Functions
Custom functions can be added
//Structure
func customFunc(field interface{}, param string) bool {
func customFunc(val interface{}, field interface{}, param string) bool {
if whatever {
return false
@ -92,8 +92,34 @@ Custom functions can be added
}
validator.AddFunction("custom tag name", customFunc)
// NOTE: using the same tag name as an existing function
// will overwrite the existing one
// NOTES: using the same tag name as an existing function
// will overwrite the existing one
Cross Field 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
// into the function
// when calling validator.ValidateFieldByTagAndValue(val, field, tag) val will be
// whatever you pass, struct, field...
// when calling validator.ValidateFieldByTag(field, tag) val will be nil
//
// 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
// would be added, but you can add your own custom ones and keep all your validation logic
// in one place.
func isDateRangeValid(val interface{}, field interface{}, param string) bool {
myStruct := val.(myStructType)
if myStruct.Start.After(field.(time.Time)) {
return false
}
return true
}
Custom Tag Name

@ -96,8 +96,8 @@ func (e *StructValidationErrors) Flatten() map[string]*FieldValidationError {
return errs
}
// ValidationFunc that accepts the value of a field and parameter for use in validation (parameter not always used or needed)
type ValidationFunc func(v interface{}, param string) bool
// ValidationFunc that accepts a value(optional usage), a field and parameter(optional usage) for use in validation
type ValidationFunc func(val interface{}, v interface{}, param string) bool
// Validator implements the Validator Struct
// NOTE: Fields within are not thread safe and that is on purpose
@ -168,6 +168,18 @@ func ValidateStruct(s interface{}) *StructValidationErrors {
// ValidateStruct validates a struct and returns a struct containing the errors
func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
structValue := reflect.ValueOf(s)
if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
return v.ValidateStruct(structValue.Elem().Interface())
}
return v.validateStructRecursive(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
func (v *Validator) validateStructRecursive(top interface{}, s interface{}) *StructValidationErrors {
structValue := reflect.ValueOf(s)
structType := reflect.TypeOf(s)
structName := structType.Name()
@ -179,7 +191,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
}
if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
return v.ValidateStruct(structValue.Elem().Interface())
return v.validateStructRecursive(top, structValue.Elem().Interface())
}
if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
@ -218,7 +230,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
if valueField.Type() == reflect.TypeOf(time.Time{}) {
if fieldError := v.validateFieldByNameAndTag(valueField.Interface(), typeField.Name, tag); fieldError != nil {
if fieldError := v.validateFieldByNameAndTagAndValue(top, valueField.Interface(), typeField.Name, tag); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference
fieldError = nil
@ -235,7 +247,7 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
default:
if fieldError := v.validateFieldByNameAndTag(valueField.Interface(), typeField.Name, tag); fieldError != nil {
if fieldError := v.validateFieldByNameAndTagAndValue(top, valueField.Interface(), typeField.Name, tag); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference
fieldError = nil
@ -253,30 +265,42 @@ func (v *Validator) ValidateStruct(s interface{}) *StructValidationErrors {
// 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.validateFieldByNameAndTag(f, "", tag)
return internalValidator.ValidateFieldByTag(f, tag)
}
// 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 {
return v.validateFieldByNameAndTag(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
func (v *Validator) ValidateFieldByTagAndValue(val interface{}, f interface{}, tag string) *FieldValidationError {
return v.validateFieldByNameAndTagAndValue(val, f, "", tag)
}
func (v *Validator) validateFieldByNameAndTag(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
if tag == "-" {
return nil
}
if strings.Contains(tag, omitempty) && !hasValue(f, "") {
if strings.Contains(tag, omitempty) && !hasValue(val, f, "") {
return nil
}
valueField := reflect.ValueOf(f)
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
return v.ValidateFieldByTag(valueField.Elem().Interface(), tag)
return v.validateFieldByNameAndTagAndValue(val, valueField.Elem().Interface(), name, tag)
}
switch valueField.Kind() {
@ -302,7 +326,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
for _, val := range orVals {
valErr, err = v.validateFieldByNameAndSingleTag(f, name, val)
valErr, err = v.validateFieldByNameAndSingleTag(val, f, name, val)
if err == nil {
return nil
@ -320,7 +344,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
return valErr
}
if valErr, err = v.validateFieldByNameAndSingleTag(f, name, valTag); err != nil {
if valErr, err = v.validateFieldByNameAndSingleTag(val, f, name, valTag); err != nil {
valErr.Kind = valueField.Kind()
return valErr
@ -330,7 +354,7 @@ func (v *Validator) validateFieldByNameAndTag(f interface{}, name string, tag st
return nil
}
func (v *Validator) validateFieldByNameAndSingleTag(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, "=")
key := strings.Trim(vals[0], " ")
@ -361,7 +385,7 @@ func (v *Validator) validateFieldByNameAndSingleTag(f interface{}, name string,
param = strings.Trim(vals[1], " ")
}
if err := valFunc(f, param); !err {
if err := valFunc(val, f, param); !err {
valErr.Param = param
return valErr, errors.New(key)
}

@ -124,11 +124,35 @@ func AssertMapFieldError(s map[string]*validator.FieldValidationError, field str
c.Assert(val.ErrorTag, Equals, expectedTag)
}
func newValidatorFunc(field interface{}, param string) bool {
func newValidatorFunc(val interface{}, field interface{}, param string) bool {
return true
}
func isEqualFunc(val interface{}, field interface{}, param string) bool {
return val.(string) == field.(string)
}
func (ms *MySuite) TestValidateByTagAndValue(c *C) {
val := "test"
field := "test"
err := validator.ValidateFieldByTagAndValue(val, field, "required")
c.Assert(err, IsNil)
validator.AddFunction("isequaltestfunc", isEqualFunc)
err = validator.ValidateFieldByTagAndValue(val, field, "isequaltestfunc")
c.Assert(err, IsNil)
val = "unequal"
err = validator.ValidateFieldByTagAndValue(val, field, "isequaltestfunc")
c.Assert(err, NotNil)
c.Assert(err.ErrorTag, Equals, "isequaltestfunc")
}
func (ms *MySuite) TestAddFunctions(c *C) {
validate := validator.NewValidator("validateme", validator.BakedInValidators)

Loading…
Cancel
Save