correct required tag functionality for pointers, now works like old `exists` tag.

pull/262/head v9.2.1
Dean Karn 8 years ago
parent 506cc5da56
commit 493dfb6209
  1. 108
      README.md
  2. 6
      baked_in.go
  3. 6
      validator.go
  4. 9
      validator_instance.go
  5. 93
      validator_test.go

@ -2,7 +2,7 @@ Package validator
================ ================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png"> <img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">
[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-9.2.0-green.svg) ![Project status](https://img.shields.io/badge/version-9.2.1-green.svg)
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
@ -66,60 +66,60 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa
Benchmarks Benchmarks
------ ------
###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.7 darwin/amd64 ###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.7.1 darwin/amd64
```go ```go
BenchmarkFieldSuccess-8 20000000 104 ns/op 0 B/op 0 allocs/op BenchmarkFieldSuccess-8 20000000 106 ns/op
BenchmarkFieldSuccessParallel-8 50000000 34.5 ns/op 0 B/op 0 allocs/op BenchmarkFieldSuccessParallel-8 50000000 33.7 ns/op
BenchmarkFieldFailure-8 5000000 335 ns/op 208 B/op 4 allocs/op BenchmarkFieldFailure-8 5000000 346 ns/op
BenchmarkFieldFailureParallel-8 20000000 118 ns/op 208 B/op 4 allocs/op BenchmarkFieldFailureParallel-8 20000000 115 ns/op
BenchmarkFieldDiveSuccess-8 2000000 718 ns/op 201 B/op 11 allocs/op BenchmarkFieldDiveSuccess-8 2000000 739 ns/op
BenchmarkFieldDiveSuccessParallel-8 10000000 234 ns/op 201 B/op 11 allocs/op BenchmarkFieldDiveSuccessParallel-8 10000000 246 ns/op
BenchmarkFieldDiveFailure-8 2000000 971 ns/op 412 B/op 16 allocs/op BenchmarkFieldDiveFailure-8 1000000 1043 ns/op
BenchmarkFieldDiveFailureParallel-8 5000000 341 ns/op 413 B/op 16 allocs/op BenchmarkFieldDiveFailureParallel-8 5000000 381 ns/op
BenchmarkFieldCustomTypeSuccess-8 5000000 268 ns/op 32 B/op 2 allocs/op BenchmarkFieldCustomTypeSuccess-8 5000000 270 ns/op
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.3 ns/op 32 B/op 2 allocs/op BenchmarkFieldCustomTypeSuccessParallel-8 20000000 92.5 ns/op
BenchmarkFieldCustomTypeFailure-8 5000000 331 ns/op 208 B/op 4 allocs/op BenchmarkFieldCustomTypeFailure-8 5000000 331 ns/op
BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op BenchmarkFieldCustomTypeFailureParallel-8 20000000 132 ns/op
BenchmarkFieldOrTagSuccess-8 2000000 872 ns/op 16 B/op 1 allocs/op BenchmarkFieldOrTagSuccess-8 2000000 874 ns/op
BenchmarkFieldOrTagSuccessParallel-8 5000000 389 ns/op 16 B/op 1 allocs/op BenchmarkFieldOrTagSuccessParallel-8 5000000 368 ns/op
BenchmarkFieldOrTagFailure-8 3000000 569 ns/op 224 B/op 5 allocs/op BenchmarkFieldOrTagFailure-8 3000000 566 ns/op
BenchmarkFieldOrTagFailureParallel-8 5000000 397 ns/op 224 B/op 5 allocs/op BenchmarkFieldOrTagFailureParallel-8 5000000 427 ns/op
BenchmarkStructLevelValidationSuccess-8 5000000 334 ns/op 32 B/op 2 allocs/op BenchmarkStructLevelValidationSuccess-8 5000000 335 ns/op
BenchmarkStructLevelValidationSuccessParallel-8 20000000 111 ns/op 32 B/op 2 allocs/op BenchmarkStructLevelValidationSuccessParallel-8 20000000 124 ns/op
BenchmarkStructLevelValidationFailure-8 2000000 622 ns/op 304 B/op 8 allocs/op BenchmarkStructLevelValidationFailure-8 2000000 630 ns/op
BenchmarkStructLevelValidationFailureParallel-8 10000000 274 ns/op 304 B/op 8 allocs/op BenchmarkStructLevelValidationFailureParallel-8 10000000 298 ns/op
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 525 ns/op 32 B/op 2 allocs/op BenchmarkStructSimpleCustomTypeSuccess-8 3000000 535 ns/op
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 165 ns/op 32 B/op 2 allocs/op BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 170 ns/op
BenchmarkStructSimpleCustomTypeFailure-8 2000000 826 ns/op 424 B/op 9 allocs/op BenchmarkStructSimpleCustomTypeFailure-8 2000000 821 ns/op
BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 378 ns/op 440 B/op 10 allocs/op BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 379 ns/op
BenchmarkStructFilteredSuccess-8 2000000 734 ns/op 288 B/op 9 allocs/op BenchmarkStructFilteredSuccess-8 2000000 769 ns/op
BenchmarkStructFilteredSuccessParallel-8 5000000 313 ns/op 288 B/op 9 allocs/op BenchmarkStructFilteredSuccessParallel-8 5000000 328 ns/op
BenchmarkStructFilteredFailure-8 2000000 592 ns/op 256 B/op 7 allocs/op BenchmarkStructFilteredFailure-8 2000000 594 ns/op
BenchmarkStructFilteredFailureParallel-8 10000000 272 ns/op 256 B/op 7 allocs/op BenchmarkStructFilteredFailureParallel-8 10000000 244 ns/op
BenchmarkStructPartialSuccess-8 2000000 682 ns/op 256 B/op 6 allocs/op BenchmarkStructPartialSuccess-8 2000000 682 ns/op
BenchmarkStructPartialSuccessParallel-8 10000000 279 ns/op 256 B/op 6 allocs/op BenchmarkStructPartialSuccessParallel-8 5000000 291 ns/op
BenchmarkStructPartialFailure-8 2000000 938 ns/op 480 B/op 11 allocs/op BenchmarkStructPartialFailure-8 1000000 1034 ns/op
BenchmarkStructPartialFailureParallel-8 5000000 398 ns/op 480 B/op 11 allocs/op BenchmarkStructPartialFailureParallel-8 5000000 392 ns/op
BenchmarkStructExceptSuccess-8 1000000 1088 ns/op 496 B/op 12 allocs/op BenchmarkStructExceptSuccess-8 1000000 1014 ns/op
BenchmarkStructExceptSuccessParallel-8 10000000 257 ns/op 240 B/op 5 allocs/op BenchmarkStructExceptSuccessParallel-8 10000000 257 ns/op
BenchmarkStructExceptFailure-8 2000000 897 ns/op 464 B/op 10 allocs/op BenchmarkStructExceptFailure-8 2000000 875 ns/op
BenchmarkStructExceptFailureParallel-8 5000000 394 ns/op 464 B/op 10 allocs/op BenchmarkStructExceptFailureParallel-8 5000000 405 ns/op
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 535 ns/op 72 B/op 3 allocs/op BenchmarkStructSimpleCrossFieldSuccess-8 3000000 545 ns/op
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 184 ns/op 72 B/op 3 allocs/op BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 177 ns/op
BenchmarkStructSimpleCrossFieldFailure-8 2000000 789 ns/op 304 B/op 8 allocs/op BenchmarkStructSimpleCrossFieldFailure-8 2000000 787 ns/op
BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 386 ns/op 304 B/op 8 allocs/op BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 341 ns/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 793 ns/op 80 B/op 4 allocs/op BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 795 ns/op
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 5000000 287 ns/op 80 B/op 4 allocs/op BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 267 ns/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1065 ns/op 320 B/op 9 allocs/op BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1119 ns/op
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 417 ns/op 320 B/op 9 allocs/op BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 437 ns/op
BenchmarkStructSimpleSuccess-8 5000000 364 ns/op 0 B/op 0 allocs/op BenchmarkStructSimpleSuccess-8 5000000 377 ns/op
BenchmarkStructSimpleSuccessParallel-8 20000000 112 ns/op 0 B/op 0 allocs/op BenchmarkStructSimpleSuccessParallel-8 20000000 110 ns/op
BenchmarkStructSimpleFailure-8 2000000 785 ns/op 424 B/op 9 allocs/op BenchmarkStructSimpleFailure-8 2000000 785 ns/op
BenchmarkStructSimpleFailureParallel-8 5000000 339 ns/op 424 B/op 9 allocs/op BenchmarkStructSimpleFailureParallel-8 5000000 302 ns/op
BenchmarkStructComplexSuccess-8 1000000 2136 ns/op 128 B/op 8 allocs/op BenchmarkStructComplexSuccess-8 1000000 2159 ns/op
BenchmarkStructComplexSuccessParallel-8 2000000 755 ns/op 128 B/op 8 allocs/op BenchmarkStructComplexSuccessParallel-8 2000000 723 ns/op
BenchmarkStructComplexFailure-8 300000 5248 ns/op 3041 B/op 53 allocs/op BenchmarkStructComplexFailure-8 300000 5237 ns/op
BenchmarkStructComplexFailureParallel-8 1000000 2363 ns/op 3041 B/op 53 allocs/op BenchmarkStructComplexFailureParallel-8 1000000 2378 ns/op
``` ```
Complimentary Software Complimentary Software

@ -26,6 +26,7 @@ var (
utf8HexComma: {}, utf8HexComma: {},
utf8Pipe: {}, utf8Pipe: {},
noStructLevelTag: {}, noStructLevelTag: {},
requiredTag: {},
} }
// BakedInAliasValidators is a default mapping of a single validation tag that // BakedInAliasValidators is a default mapping of a single validation tag that
@ -887,6 +888,11 @@ func hasValue(fl FieldLevel) bool {
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil() return !field.IsNil()
default: default:
if fl.(*validate).fldIsPointer && field.Interface() != nil {
return true
}
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
} }
} }

@ -24,6 +24,7 @@ type validate struct {
slCurrent reflect.Value slCurrent reflect.Value
flField reflect.Value flField reflect.Value
flParam string flParam string
fldIsPointer bool
// misc reusable values // misc reusable values
misc []byte misc []byte
@ -95,9 +96,8 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
var typ reflect.Type var typ reflect.Type
var kind reflect.Kind var kind reflect.Kind
var nullable bool
current, kind, nullable = v.extractTypeInternal(current, nullable) current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
switch kind { switch kind {
case reflect.Ptr, reflect.Interface, reflect.Invalid: case reflect.Ptr, reflect.Interface, reflect.Invalid:
@ -207,7 +207,7 @@ OUTER:
v.flField = current v.flField = current
v.flParam = "" v.flParam = ""
if !nullable && !hasValue(v) { if !v.fldIsPointer && !hasValue(v) {
return return
} }

@ -23,6 +23,7 @@ const (
omitempty = "omitempty" omitempty = "omitempty"
skipValidationTag = "-" skipValidationTag = "-"
diveTag = "dive" diveTag = "dive"
requiredTag = "required"
namespaceSeparator = "." namespaceSeparator = "."
leftBracket = "[" leftBracket = "["
rightBracket = "]" rightBracket = "]"
@ -92,7 +93,7 @@ func New() *Validate {
for k, val := range bakedInValidators { for k, val := range bakedInValidators {
// no need to error check here, baked in will alwaays be valid // no need to error check here, baked in will alwaays be valid
v.RegisterValidation(k, val) v.registerValidation(k, val, true)
} }
v.pool = &sync.Pool{ v.pool = &sync.Pool{
@ -127,6 +128,10 @@ func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
// - if the key already exists, the previous validation function will be replaced. // - if the key already exists, the previous validation function will be replaced.
// - this method is not thread-safe it is intended that these all be registered prior to any validation // - this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterValidation(tag string, fn Func) error { func (v *Validate) RegisterValidation(tag string, fn Func) error {
return v.registerValidation(tag, fn, false)
}
func (v *Validate) registerValidation(tag string, fn Func, bakedIn bool) error {
if len(tag) == 0 { if len(tag) == 0 {
return errors.New("Function Key cannot be empty") return errors.New("Function Key cannot be empty")
@ -138,7 +143,7 @@ func (v *Validate) RegisterValidation(tag string, fn Func) error {
_, ok := restrictedTags[tag] _, ok := restrictedTags[tag]
if ok || strings.ContainsAny(tag, restrictedTagChars) { if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
panic(fmt.Sprintf(restrictedTagErr, tag)) panic(fmt.Sprintf(restrictedTagErr, tag))
} }

@ -6731,3 +6731,96 @@ func TestStructFiltered(t *testing.T) {
NotEqual(t, err, nil) NotEqual(t, err, nil)
Equal(t, err.Error(), "validator: (nil *time.Time)") Equal(t, err.Error(), "validator: (nil *time.Time)")
} }
func TestRequiredPtr(t *testing.T) {
type Test struct {
Bool *bool `validate:"required"`
}
validate := New()
f := false
test := Test{
Bool: &f,
}
err := validate.Struct(test)
Equal(t, err, nil)
tr := true
test.Bool = &tr
err = validate.Struct(test)
Equal(t, err, nil)
test.Bool = nil
err = validate.Struct(test)
NotEqual(t, err, nil)
errs, ok := err.(ValidationErrors)
Equal(t, ok, true)
Equal(t, len(errs), 1)
AssertError(t, errs, "Test.Bool", "Test.Bool", "Bool", "Bool", "required")
type Test2 struct {
Bool bool `validate:"required"`
}
var test2 Test2
err = validate.Struct(test2)
NotEqual(t, err, nil)
errs, ok = err.(ValidationErrors)
Equal(t, ok, true)
Equal(t, len(errs), 1)
AssertError(t, errs, "Test2.Bool", "Test2.Bool", "Bool", "Bool", "required")
test2.Bool = true
err = validate.Struct(test2)
Equal(t, err, nil)
type Test3 struct {
Arr []string `validate:"required"`
}
var test3 Test3
err = validate.Struct(test3)
NotEqual(t, err, nil)
errs, ok = err.(ValidationErrors)
Equal(t, ok, true)
Equal(t, len(errs), 1)
AssertError(t, errs, "Test3.Arr", "Test3.Arr", "Arr", "Arr", "required")
test3.Arr = make([]string, 0)
err = validate.Struct(test3)
Equal(t, err, nil)
type Test4 struct {
Arr *[]string `validate:"required"` // I know I know pointer to array, just making sure validation works as expected...
}
var test4 Test4
err = validate.Struct(test4)
NotEqual(t, err, nil)
errs, ok = err.(ValidationErrors)
Equal(t, ok, true)
Equal(t, len(errs), 1)
AssertError(t, errs, "Test4.Arr", "Test4.Arr", "Arr", "Arr", "required")
arr := make([]string, 0)
test4.Arr = &arr
err = validate.Struct(test4)
Equal(t, err, nil)
}

Loading…
Cancel
Save