Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ How 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.

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
v1 and v1-development branch however, there will also be a v2-development brach even though v2 doesn't exist yet.
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
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
help make this package even better.
Expand Down
44 changes: 29 additions & 15 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,22 @@ import (
)

const (
utf8HexComma = "0x2C"
utf8Pipe = "0x7C"
tagSeparator = ","
orSeparator = "|"
tagKeySeparator = "="
structOnlyTag = "structonly"
omitempty = "omitempty"
skipValidationTag = "-"
diveTag = "dive"
existsTag = "exists"
fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag"
arrayIndexFieldName = "%s" + leftBracket + "%d" + rightBracket
mapIndexFieldName = "%s" + leftBracket + "%v" + rightBracket
invalidValidation = "Invalid validation tag on field %s"
undefinedValidation = "Undefined validation function on field %s"
utf8HexComma = "0x2C"
utf8Pipe = "0x7C"
tagSeparator = ","
orSeparator = "|"
tagKeySeparator = "="
structOnlyTag = "structonly"
omitempty = "omitempty"
skipValidationTag = "-"
diveTag = "dive"
existsTag = "exists"
fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag"
arrayIndexFieldName = "%s" + leftBracket + "%d" + rightBracket
mapIndexFieldName = "%s" + leftBracket + "%v" + rightBracket
invalidValidation = "Invalid validation tag on field %s"
undefinedValidation = "Undefined validation function on field %s"
validatorNotInitialized = "Validator instance not initialized"
)

var (
Expand Down Expand Up @@ -78,6 +79,12 @@ type Validate struct {
config Config
}

func (v *Validate) initCheck() {
if v == nil {
panic(validatorNotInitialized)
}
}

// Config contains the options that a Validator instance will use.
// It is passed to the New() function
type Config struct {
Expand Down Expand Up @@ -146,6 +153,7 @@ func New(config Config) *Validate {
// NOTE: if the key already exists, the previous validation function will be replaced.
// NOTE: this method is not thread-safe
func (v *Validate) RegisterValidation(key string, f Func) error {
v.initCheck()

if len(key) == 0 {
return errors.New("Function Key cannot be empty")
Expand All @@ -162,6 +170,7 @@ func (v *Validate) RegisterValidation(key string, f Func) error {

// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
v.initCheck()

if v.config.CustomTypeFuncs == nil {
v.config.CustomTypeFuncs = map[reflect.Type]CustomTypeFunc{}
Expand All @@ -178,6 +187,7 @@ func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{
// 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
func (v *Validate) Field(field interface{}, tag string) ValidationErrors {
v.initCheck()

errs := errsPool.Get().(ValidationErrors)
fieldVal := reflect.ValueOf(field)
Expand All @@ -196,6 +206,7 @@ func (v *Validate) Field(field interface{}, tag string) ValidationErrors {
// 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
func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors {
v.initCheck()

errs := errsPool.Get().(ValidationErrors)
topVal := reflect.ValueOf(val)
Expand All @@ -216,6 +227,7 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string
// NOTE: This is normally not needed, however in some specific cases such as: tied to a
// legacy data structure, it will be useful
func (v *Validate) StructPartial(current interface{}, fields ...string) ValidationErrors {
v.initCheck()

sv, _ := v.extractType(reflect.ValueOf(current))
name := sv.Type().Name()
Expand Down Expand Up @@ -274,6 +286,7 @@ func (v *Validate) StructPartial(current interface{}, fields ...string) Validati
// NOTE: This is normally not needed, however in some specific cases such as: tied to a
// legacy data structure, it will be useful
func (v *Validate) StructExcept(current interface{}, fields ...string) ValidationErrors {
v.initCheck()

sv, _ := v.extractType(reflect.ValueOf(current))
name := sv.Type().Name()
Expand All @@ -297,6 +310,7 @@ func (v *Validate) StructExcept(current interface{}, fields ...string) Validatio

// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
func (v *Validate) Struct(current interface{}) ValidationErrors {
v.initCheck()

errs := errsPool.Get().(ValidationErrors)
sv := reflect.ValueOf(current)
Expand Down
25 changes: 25 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,31 @@ type TestPartial struct {
}
}

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) {

p1 := []string{
Expand Down