Merge branch 'v7-development' into merge

pull/169/head
joeybloggs 9 years ago
commit e8133a0b33
  1. 6
      README.md
  2. 45
      validator.go
  3. 24
      validator_test.go

@ -239,9 +239,9 @@ How to Contribute
There will always be a development branch for each version i.e. `v1-development`. In order to contribute, There will always be a development branch for each version i.e. `v1-development`. In order to contribute,
please make your pull requests against those branches. please make your pull requests against those branches.
If the changes being proposed or requested are breaking changes, please create an issue, for discussion If the changes being proposed or requested are breaking changes, please create an issue, for discussion
or create a pull request against the highest development branch for example this package has a or create a pull request against the highest development branch for example this package has a
v1 and v1-development branch however, there will also be a v2-development brach even though v2 doesn't exist yet. v1 and v1-development branch however, there will also be a v2-development branch even though v2 doesn't exist yet.
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.

@ -20,21 +20,22 @@ import (
) )
const ( const (
utf8HexComma = "0x2C" utf8HexComma = "0x2C"
utf8Pipe = "0x7C" utf8Pipe = "0x7C"
tagSeparator = "," tagSeparator = ","
orSeparator = "|" orSeparator = "|"
tagKeySeparator = "=" tagKeySeparator = "="
structOnlyTag = "structonly" structOnlyTag = "structonly"
omitempty = "omitempty" omitempty = "omitempty"
skipValidationTag = "-" skipValidationTag = "-"
diveTag = "dive" diveTag = "dive"
existsTag = "exists" existsTag = "exists"
fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag"
arrayIndexFieldName = "%s" + leftBracket + "%d" + rightBracket arrayIndexFieldName = "%s" + leftBracket + "%d" + rightBracket
mapIndexFieldName = "%s" + leftBracket + "%v" + rightBracket mapIndexFieldName = "%s" + leftBracket + "%v" + rightBracket
invalidValidation = "Invalid validation tag on field %s" invalidValidation = "Invalid validation tag on field %s"
undefinedValidation = "Undefined validation function on field %s" undefinedValidation = "Undefined validation function on field %s"
validatorNotInitialized = "Validator instance not initialized"
) )
var ( var (
@ -86,6 +87,12 @@ type Validate struct {
errsPool *sync.Pool errsPool *sync.Pool
} }
func (v *Validate) initCheck() {
if v == nil {
panic(validatorNotInitialized)
}
}
// Config contains the options that a Validator instance will use. // Config contains the options that a Validator instance will use.
// It is passed to the New() function // It is passed to the New() function
type Config struct { type Config struct {
@ -171,6 +178,7 @@ func New(config *Config) *Validate {
// NOTE: if the key already exists, the previous validation function will be replaced. // NOTE: if the key already exists, the previous validation function will be replaced.
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterValidation(key string, f Func) error { func (v *Validate) RegisterValidation(key string, f Func) error {
v.initCheck()
if len(key) == 0 { if len(key) == 0 {
return errors.New("Function Key cannot be empty") return errors.New("Function Key cannot be empty")
@ -194,6 +202,7 @@ func (v *Validate) RegisterValidation(key string, f Func) error {
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types // RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
v.initCheck()
if v.customTypeFuncs == nil { if v.customTypeFuncs == nil {
v.customTypeFuncs = map[reflect.Type]CustomTypeFunc{} v.customTypeFuncs = map[reflect.Type]CustomTypeFunc{}
@ -214,6 +223,7 @@ func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{
// will be the actual tag within the alias that failed. // will be the actual tag within the alias that failed.
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterAliasValidation(alias, tags string) { func (v *Validate) RegisterAliasValidation(alias, tags string) {
v.initCheck()
_, ok := restrictedTags[alias] _, ok := restrictedTags[alias]
@ -230,6 +240,7 @@ func (v *Validate) RegisterAliasValidation(alias, tags string) {
// NOTE: it returns ValidationErrors instead of a single FieldError because this can also // NOTE: it returns ValidationErrors instead of a single FieldError because this can also
// validate Array, Slice and maps fields which may contain more than one error // validate Array, Slice and maps fields which may contain more than one error
func (v *Validate) Field(field interface{}, tag string) error { func (v *Validate) Field(field interface{}, tag string) error {
v.initCheck()
errs := v.errsPool.Get().(ValidationErrors) errs := v.errsPool.Get().(ValidationErrors)
fieldVal := reflect.ValueOf(field) fieldVal := reflect.ValueOf(field)
@ -249,6 +260,7 @@ func (v *Validate) Field(field interface{}, tag string) error {
// NOTE: it returns ValidationErrors instead of a single FieldError because this can also // NOTE: it returns ValidationErrors instead of a single FieldError because this can also
// validate Array, Slice and maps fields which may contain more than one error // validate Array, Slice and maps fields which may contain more than one error
func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) error { func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) error {
v.initCheck()
errs := v.errsPool.Get().(ValidationErrors) errs := v.errsPool.Get().(ValidationErrors)
topVal := reflect.ValueOf(val) topVal := reflect.ValueOf(val)
@ -268,6 +280,7 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name and returns nil or ValidationErrors as error // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name and returns nil or ValidationErrors as error
// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors.
func (v *Validate) StructPartial(current interface{}, fields ...string) error { func (v *Validate) StructPartial(current interface{}, fields ...string) error {
v.initCheck()
sv, _ := v.extractType(reflect.ValueOf(current)) sv, _ := v.extractType(reflect.ValueOf(current))
name := sv.Type().Name() name := sv.Type().Name()
@ -325,6 +338,7 @@ func (v *Validate) StructPartial(current interface{}, fields ...string) error {
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name and returns nil or ValidationErrors as error // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name and returns nil or ValidationErrors as error
// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors.
func (v *Validate) StructExcept(current interface{}, fields ...string) error { func (v *Validate) StructExcept(current interface{}, fields ...string) error {
v.initCheck()
sv, _ := v.extractType(reflect.ValueOf(current)) sv, _ := v.extractType(reflect.ValueOf(current))
name := sv.Type().Name() name := sv.Type().Name()
@ -350,6 +364,7 @@ func (v *Validate) StructExcept(current interface{}, fields ...string) error {
// it returns nil or ValidationErrors as error. // it returns nil or ValidationErrors as error.
// You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors.
func (v *Validate) Struct(current interface{}) error { func (v *Validate) Struct(current interface{}) error {
v.initCheck()
errs := v.errsPool.Get().(ValidationErrors) errs := v.errsPool.Get().(ValidationErrors)
sv := reflect.ValueOf(current) sv := reflect.ValueOf(current)

@ -255,6 +255,30 @@ func TestAliasTags(t *testing.T) {
PanicMatches(t, func() { validate.RegisterAliasValidation("exists", "gt=5,lt=10") }, "Alias \"exists\" either contains restricted characters or is the same as a restricted tag needed for normal operation") PanicMatches(t, func() { validate.RegisterAliasValidation("exists", "gt=5,lt=10") }, "Alias \"exists\" either contains restricted characters or is the same as a restricted tag needed for normal operation")
} }
func TestNilValidator(t *testing.T) {
type TestStruct struct {
Test string `validate:"required"`
}
ts := TestStruct{}
var val *Validate
fn := func(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return current.String() == field.String()
}
PanicMatches(t, func() { val.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) }, validatorNotInitialized)
PanicMatches(t, func() { val.RegisterValidation("something", fn) }, validatorNotInitialized)
PanicMatches(t, func() { val.Field(ts.Test, "required") }, validatorNotInitialized)
PanicMatches(t, func() { val.FieldWithValue("test", ts.Test, "required") }, validatorNotInitialized)
PanicMatches(t, func() { val.Struct(ts) }, validatorNotInitialized)
PanicMatches(t, func() { val.StructExcept(ts, "Test") }, validatorNotInitialized)
PanicMatches(t, func() { val.StructPartial(ts, "Test") }, validatorNotInitialized)
}
func TestStructPartial(t *testing.T) { func TestStructPartial(t *testing.T) {
p1 := []string{ p1 := []string{

Loading…
Cancel
Save