Skip to content

Commit 493dfb6

Browse files
Dean KarnDean Karn
authored andcommitted
correct required tag functionality for pointers, now works like old exists tag.
1 parent 506cc5d commit 493dfb6

File tree

5 files changed

+167
-63
lines changed

5 files changed

+167
-63
lines changed

README.md

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Package validator
22
================
33
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">
44
[![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)
5-
![Project status](https://img.shields.io/badge/version-9.2.0-green.svg)
5+
![Project status](https://img.shields.io/badge/version-9.2.1-green.svg)
66
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
77
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9)
88
[![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
6666

6767
Benchmarks
6868
------
69-
###### 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
69+
###### 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
7070
```go
71-
BenchmarkFieldSuccess-8 20000000 104 ns/op 0 B/op 0 allocs/op
72-
BenchmarkFieldSuccessParallel-8 50000000 34.5 ns/op 0 B/op 0 allocs/op
73-
BenchmarkFieldFailure-8 5000000 335 ns/op 208 B/op 4 allocs/op
74-
BenchmarkFieldFailureParallel-8 20000000 118 ns/op 208 B/op 4 allocs/op
75-
BenchmarkFieldDiveSuccess-8 2000000 718 ns/op 201 B/op 11 allocs/op
76-
BenchmarkFieldDiveSuccessParallel-8 10000000 234 ns/op 201 B/op 11 allocs/op
77-
BenchmarkFieldDiveFailure-8 2000000 971 ns/op 412 B/op 16 allocs/op
78-
BenchmarkFieldDiveFailureParallel-8 5000000 341 ns/op 413 B/op 16 allocs/op
79-
BenchmarkFieldCustomTypeSuccess-8 5000000 268 ns/op 32 B/op 2 allocs/op
80-
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.3 ns/op 32 B/op 2 allocs/op
81-
BenchmarkFieldCustomTypeFailure-8 5000000 331 ns/op 208 B/op 4 allocs/op
82-
BenchmarkFieldCustomTypeFailureParallel-8 20000000 116 ns/op 208 B/op 4 allocs/op
83-
BenchmarkFieldOrTagSuccess-8 2000000 872 ns/op 16 B/op 1 allocs/op
84-
BenchmarkFieldOrTagSuccessParallel-8 5000000 389 ns/op 16 B/op 1 allocs/op
85-
BenchmarkFieldOrTagFailure-8 3000000 569 ns/op 224 B/op 5 allocs/op
86-
BenchmarkFieldOrTagFailureParallel-8 5000000 397 ns/op 224 B/op 5 allocs/op
87-
BenchmarkStructLevelValidationSuccess-8 5000000 334 ns/op 32 B/op 2 allocs/op
88-
BenchmarkStructLevelValidationSuccessParallel-8 20000000 111 ns/op 32 B/op 2 allocs/op
89-
BenchmarkStructLevelValidationFailure-8 2000000 622 ns/op 304 B/op 8 allocs/op
90-
BenchmarkStructLevelValidationFailureParallel-8 10000000 274 ns/op 304 B/op 8 allocs/op
91-
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 525 ns/op 32 B/op 2 allocs/op
92-
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 165 ns/op 32 B/op 2 allocs/op
93-
BenchmarkStructSimpleCustomTypeFailure-8 2000000 826 ns/op 424 B/op 9 allocs/op
94-
BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 378 ns/op 440 B/op 10 allocs/op
95-
BenchmarkStructFilteredSuccess-8 2000000 734 ns/op 288 B/op 9 allocs/op
96-
BenchmarkStructFilteredSuccessParallel-8 5000000 313 ns/op 288 B/op 9 allocs/op
97-
BenchmarkStructFilteredFailure-8 2000000 592 ns/op 256 B/op 7 allocs/op
98-
BenchmarkStructFilteredFailureParallel-8 10000000 272 ns/op 256 B/op 7 allocs/op
99-
BenchmarkStructPartialSuccess-8 2000000 682 ns/op 256 B/op 6 allocs/op
100-
BenchmarkStructPartialSuccessParallel-8 10000000 279 ns/op 256 B/op 6 allocs/op
101-
BenchmarkStructPartialFailure-8 2000000 938 ns/op 480 B/op 11 allocs/op
102-
BenchmarkStructPartialFailureParallel-8 5000000 398 ns/op 480 B/op 11 allocs/op
103-
BenchmarkStructExceptSuccess-8 1000000 1088 ns/op 496 B/op 12 allocs/op
104-
BenchmarkStructExceptSuccessParallel-8 10000000 257 ns/op 240 B/op 5 allocs/op
105-
BenchmarkStructExceptFailure-8 2000000 897 ns/op 464 B/op 10 allocs/op
106-
BenchmarkStructExceptFailureParallel-8 5000000 394 ns/op 464 B/op 10 allocs/op
107-
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 535 ns/op 72 B/op 3 allocs/op
108-
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 184 ns/op 72 B/op 3 allocs/op
109-
BenchmarkStructSimpleCrossFieldFailure-8 2000000 789 ns/op 304 B/op 8 allocs/op
110-
BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 386 ns/op 304 B/op 8 allocs/op
111-
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 793 ns/op 80 B/op 4 allocs/op
112-
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 5000000 287 ns/op 80 B/op 4 allocs/op
113-
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1065 ns/op 320 B/op 9 allocs/op
114-
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 417 ns/op 320 B/op 9 allocs/op
115-
BenchmarkStructSimpleSuccess-8 5000000 364 ns/op 0 B/op 0 allocs/op
116-
BenchmarkStructSimpleSuccessParallel-8 20000000 112 ns/op 0 B/op 0 allocs/op
117-
BenchmarkStructSimpleFailure-8 2000000 785 ns/op 424 B/op 9 allocs/op
118-
BenchmarkStructSimpleFailureParallel-8 5000000 339 ns/op 424 B/op 9 allocs/op
119-
BenchmarkStructComplexSuccess-8 1000000 2136 ns/op 128 B/op 8 allocs/op
120-
BenchmarkStructComplexSuccessParallel-8 2000000 755 ns/op 128 B/op 8 allocs/op
121-
BenchmarkStructComplexFailure-8 300000 5248 ns/op 3041 B/op 53 allocs/op
122-
BenchmarkStructComplexFailureParallel-8 1000000 2363 ns/op 3041 B/op 53 allocs/op
71+
BenchmarkFieldSuccess-8 20000000 106 ns/op
72+
BenchmarkFieldSuccessParallel-8 50000000 33.7 ns/op
73+
BenchmarkFieldFailure-8 5000000 346 ns/op
74+
BenchmarkFieldFailureParallel-8 20000000 115 ns/op
75+
BenchmarkFieldDiveSuccess-8 2000000 739 ns/op
76+
BenchmarkFieldDiveSuccessParallel-8 10000000 246 ns/op
77+
BenchmarkFieldDiveFailure-8 1000000 1043 ns/op
78+
BenchmarkFieldDiveFailureParallel-8 5000000 381 ns/op
79+
BenchmarkFieldCustomTypeSuccess-8 5000000 270 ns/op
80+
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 92.5 ns/op
81+
BenchmarkFieldCustomTypeFailure-8 5000000 331 ns/op
82+
BenchmarkFieldCustomTypeFailureParallel-8 20000000 132 ns/op
83+
BenchmarkFieldOrTagSuccess-8 2000000 874 ns/op
84+
BenchmarkFieldOrTagSuccessParallel-8 5000000 368 ns/op
85+
BenchmarkFieldOrTagFailure-8 3000000 566 ns/op
86+
BenchmarkFieldOrTagFailureParallel-8 5000000 427 ns/op
87+
BenchmarkStructLevelValidationSuccess-8 5000000 335 ns/op
88+
BenchmarkStructLevelValidationSuccessParallel-8 20000000 124 ns/op
89+
BenchmarkStructLevelValidationFailure-8 2000000 630 ns/op
90+
BenchmarkStructLevelValidationFailureParallel-8 10000000 298 ns/op
91+
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 535 ns/op
92+
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 170 ns/op
93+
BenchmarkStructSimpleCustomTypeFailure-8 2000000 821 ns/op
94+
BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 379 ns/op
95+
BenchmarkStructFilteredSuccess-8 2000000 769 ns/op
96+
BenchmarkStructFilteredSuccessParallel-8 5000000 328 ns/op
97+
BenchmarkStructFilteredFailure-8 2000000 594 ns/op
98+
BenchmarkStructFilteredFailureParallel-8 10000000 244 ns/op
99+
BenchmarkStructPartialSuccess-8 2000000 682 ns/op
100+
BenchmarkStructPartialSuccessParallel-8 5000000 291 ns/op
101+
BenchmarkStructPartialFailure-8 1000000 1034 ns/op
102+
BenchmarkStructPartialFailureParallel-8 5000000 392 ns/op
103+
BenchmarkStructExceptSuccess-8 1000000 1014 ns/op
104+
BenchmarkStructExceptSuccessParallel-8 10000000 257 ns/op
105+
BenchmarkStructExceptFailure-8 2000000 875 ns/op
106+
BenchmarkStructExceptFailureParallel-8 5000000 405 ns/op
107+
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 545 ns/op
108+
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 177 ns/op
109+
BenchmarkStructSimpleCrossFieldFailure-8 2000000 787 ns/op
110+
BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 341 ns/op
111+
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 795 ns/op
112+
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 267 ns/op
113+
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 1119 ns/op
114+
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 437 ns/op
115+
BenchmarkStructSimpleSuccess-8 5000000 377 ns/op
116+
BenchmarkStructSimpleSuccessParallel-8 20000000 110 ns/op
117+
BenchmarkStructSimpleFailure-8 2000000 785 ns/op
118+
BenchmarkStructSimpleFailureParallel-8 5000000 302 ns/op
119+
BenchmarkStructComplexSuccess-8 1000000 2159 ns/op
120+
BenchmarkStructComplexSuccessParallel-8 2000000 723 ns/op
121+
BenchmarkStructComplexFailure-8 300000 5237 ns/op
122+
BenchmarkStructComplexFailureParallel-8 1000000 2378 ns/op
123123
```
124124

125125
Complimentary Software

baked_in.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ var (
2626
utf8HexComma: {},
2727
utf8Pipe: {},
2828
noStructLevelTag: {},
29+
requiredTag: {},
2930
}
3031

3132
// BakedInAliasValidators is a default mapping of a single validation tag that
@@ -887,6 +888,11 @@ func hasValue(fl FieldLevel) bool {
887888
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
888889
return !field.IsNil()
889890
default:
891+
892+
if fl.(*validate).fldIsPointer && field.Interface() != nil {
893+
return true
894+
}
895+
890896
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
891897
}
892898
}

validator.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ type validate struct {
2020
ffn FilterFunc
2121

2222
// StructLevel & FieldLevel fields
23-
slflParent reflect.Value
24-
slCurrent reflect.Value
25-
flField reflect.Value
26-
flParam string
23+
slflParent reflect.Value
24+
slCurrent reflect.Value
25+
flField reflect.Value
26+
flParam string
27+
fldIsPointer bool
2728

2829
// misc reusable values
2930
misc []byte
@@ -95,9 +96,8 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
9596

9697
var typ reflect.Type
9798
var kind reflect.Kind
98-
var nullable bool
9999

100-
current, kind, nullable = v.extractTypeInternal(current, nullable)
100+
current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
101101

102102
switch kind {
103103
case reflect.Ptr, reflect.Interface, reflect.Invalid:
@@ -207,7 +207,7 @@ OUTER:
207207
v.flField = current
208208
v.flParam = ""
209209

210-
if !nullable && !hasValue(v) {
210+
if !v.fldIsPointer && !hasValue(v) {
211211
return
212212
}
213213

validator_instance.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
omitempty = "omitempty"
2424
skipValidationTag = "-"
2525
diveTag = "dive"
26+
requiredTag = "required"
2627
namespaceSeparator = "."
2728
leftBracket = "["
2829
rightBracket = "]"
@@ -92,7 +93,7 @@ func New() *Validate {
9293
for k, val := range bakedInValidators {
9394

9495
// no need to error check here, baked in will alwaays be valid
95-
v.RegisterValidation(k, val)
96+
v.registerValidation(k, val, true)
9697
}
9798

9899
v.pool = &sync.Pool{
@@ -127,6 +128,10 @@ func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
127128
// - if the key already exists, the previous validation function will be replaced.
128129
// - this method is not thread-safe it is intended that these all be registered prior to any validation
129130
func (v *Validate) RegisterValidation(tag string, fn Func) error {
131+
return v.registerValidation(tag, fn, false)
132+
}
133+
134+
func (v *Validate) registerValidation(tag string, fn Func, bakedIn bool) error {
130135

131136
if len(tag) == 0 {
132137
return errors.New("Function Key cannot be empty")
@@ -138,7 +143,7 @@ func (v *Validate) RegisterValidation(tag string, fn Func) error {
138143

139144
_, ok := restrictedTags[tag]
140145

141-
if ok || strings.ContainsAny(tag, restrictedTagChars) {
146+
if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
142147
panic(fmt.Sprintf(restrictedTagErr, tag))
143148
}
144149

validator_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6731,3 +6731,96 @@ func TestStructFiltered(t *testing.T) {
67316731
NotEqual(t, err, nil)
67326732
Equal(t, err.Error(), "validator: (nil *time.Time)")
67336733
}
6734+
6735+
func TestRequiredPtr(t *testing.T) {
6736+
6737+
type Test struct {
6738+
Bool *bool `validate:"required"`
6739+
}
6740+
6741+
validate := New()
6742+
6743+
f := false
6744+
6745+
test := Test{
6746+
Bool: &f,
6747+
}
6748+
6749+
err := validate.Struct(test)
6750+
Equal(t, err, nil)
6751+
6752+
tr := true
6753+
6754+
test.Bool = &tr
6755+
6756+
err = validate.Struct(test)
6757+
Equal(t, err, nil)
6758+
6759+
test.Bool = nil
6760+
6761+
err = validate.Struct(test)
6762+
NotEqual(t, err, nil)
6763+
6764+
errs, ok := err.(ValidationErrors)
6765+
Equal(t, ok, true)
6766+
Equal(t, len(errs), 1)
6767+
AssertError(t, errs, "Test.Bool", "Test.Bool", "Bool", "Bool", "required")
6768+
6769+
type Test2 struct {
6770+
Bool bool `validate:"required"`
6771+
}
6772+
6773+
var test2 Test2
6774+
6775+
err = validate.Struct(test2)
6776+
NotEqual(t, err, nil)
6777+
6778+
errs, ok = err.(ValidationErrors)
6779+
Equal(t, ok, true)
6780+
Equal(t, len(errs), 1)
6781+
AssertError(t, errs, "Test2.Bool", "Test2.Bool", "Bool", "Bool", "required")
6782+
6783+
test2.Bool = true
6784+
6785+
err = validate.Struct(test2)
6786+
Equal(t, err, nil)
6787+
6788+
type Test3 struct {
6789+
Arr []string `validate:"required"`
6790+
}
6791+
6792+
var test3 Test3
6793+
6794+
err = validate.Struct(test3)
6795+
NotEqual(t, err, nil)
6796+
6797+
errs, ok = err.(ValidationErrors)
6798+
Equal(t, ok, true)
6799+
Equal(t, len(errs), 1)
6800+
AssertError(t, errs, "Test3.Arr", "Test3.Arr", "Arr", "Arr", "required")
6801+
6802+
test3.Arr = make([]string, 0)
6803+
6804+
err = validate.Struct(test3)
6805+
Equal(t, err, nil)
6806+
6807+
type Test4 struct {
6808+
Arr *[]string `validate:"required"` // I know I know pointer to array, just making sure validation works as expected...
6809+
}
6810+
6811+
var test4 Test4
6812+
6813+
err = validate.Struct(test4)
6814+
NotEqual(t, err, nil)
6815+
6816+
errs, ok = err.(ValidationErrors)
6817+
Equal(t, ok, true)
6818+
Equal(t, len(errs), 1)
6819+
AssertError(t, errs, "Test4.Arr", "Test4.Arr", "Arr", "Arr", "required")
6820+
6821+
arr := make([]string, 0)
6822+
test4.Arr = &arr
6823+
6824+
err = validate.Struct(test4)
6825+
Equal(t, err, nil)
6826+
}

0 commit comments

Comments
 (0)