Skip to content

Commit 506cc5d

Browse files
Dean KarnDean Karn
authored andcommitted
Add Filter logic
1 parent 0c0ae40 commit 506cc5d

File tree

6 files changed

+391
-52
lines changed

6 files changed

+391
-52
lines changed

README.md

Lines changed: 53 additions & 49 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.1.3-green.svg)
5+
![Project status](https://img.shields.io/badge/version-9.2.0-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)
@@ -68,54 +68,58 @@ Benchmarks
6868
------
6969
###### 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
7070
```go
71-
BenchmarkFieldSuccess-8 20000000 105 ns/op 0 B/op 0 allocs/op
72-
BenchmarkFieldSuccessParallel-8 50000000 35.1 ns/op 0 B/op 0 allocs/op
73-
BenchmarkFieldFailure-8 5000000 337 ns/op 208 B/op 4 allocs/op
74-
BenchmarkFieldFailureParallel-8 20000000 120 ns/op 208 B/op 4 allocs/op
75-
BenchmarkFieldDiveSuccess-8 2000000 716 ns/op 201 B/op 11 allocs/op
76-
BenchmarkFieldDiveSuccessParallel-8 10000000 253 ns/op 201 B/op 11 allocs/op
77-
BenchmarkFieldDiveFailure-8 1000000 1060 ns/op 412 B/op 16 allocs/op
78-
BenchmarkFieldDiveFailureParallel-8 5000000 360 ns/op 413 B/op 16 allocs/op
79-
BenchmarkFieldCustomTypeSuccess-8 5000000 299 ns/op 32 B/op 2 allocs/op
80-
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 86.0 ns/op 32 B/op 2 allocs/op
81-
BenchmarkFieldCustomTypeFailure-8 5000000 341 ns/op 208 B/op 4 allocs/op
82-
BenchmarkFieldCustomTypeFailureParallel-8 20000000 140 ns/op 208 B/op 4 allocs/op
83-
BenchmarkFieldOrTagSuccess-8 2000000 893 ns/op 16 B/op 1 allocs/op
84-
BenchmarkFieldOrTagSuccessParallel-8 5000000 431 ns/op 16 B/op 1 allocs/op
85-
BenchmarkFieldOrTagFailure-8 2000000 563 ns/op 224 B/op 5 allocs/op
86-
BenchmarkFieldOrTagFailureParallel-8 5000000 417 ns/op 224 B/op 5 allocs/op
87-
BenchmarkStructLevelValidationSuccess-8 5000000 339 ns/op 32 B/op 2 allocs/op
88-
BenchmarkStructLevelValidationSuccessParallel-8 20000000 114 ns/op 32 B/op 2 allocs/op
89-
BenchmarkStructLevelValidationFailure-8 2000000 630 ns/op 304 B/op 8 allocs/op
90-
BenchmarkStructLevelValidationFailureParallel-8 5000000 291 ns/op 304 B/op 8 allocs/op
91-
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 540 ns/op 32 B/op 2 allocs/op
92-
BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 176 ns/op 32 B/op 2 allocs/op
93-
BenchmarkStructSimpleCustomTypeFailure-8 2000000 821 ns/op 424 B/op 9 allocs/op
94-
BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 336 ns/op 440 B/op 10 allocs/op
95-
BenchmarkStructPartialSuccess-8 2000000 686 ns/op 256 B/op 6 allocs/op
96-
BenchmarkStructPartialSuccessParallel-8 5000000 282 ns/op 256 B/op 6 allocs/op
97-
BenchmarkStructPartialFailure-8 2000000 931 ns/op 480 B/op 11 allocs/op
98-
BenchmarkStructPartialFailureParallel-8 5000000 394 ns/op 480 B/op 11 allocs/op
99-
BenchmarkStructExceptSuccess-8 1000000 1017 ns/op 496 B/op 12 allocs/op
100-
BenchmarkStructExceptSuccessParallel-8 10000000 233 ns/op 240 B/op 5 allocs/op
101-
BenchmarkStructExceptFailure-8 2000000 864 ns/op 464 B/op 10 allocs/op
102-
BenchmarkStructExceptFailureParallel-8 5000000 393 ns/op 464 B/op 10 allocs/op
103-
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 552 ns/op 72 B/op 3 allocs/op
104-
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 202 ns/op 72 B/op 3 allocs/op
105-
BenchmarkStructSimpleCrossFieldFailure-8 2000000 798 ns/op 304 B/op 8 allocs/op
106-
BenchmarkStructSimpleCrossFieldFailureParallel-8 5000000 356 ns/op 304 B/op 8 allocs/op
107-
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 825 ns/op 80 B/op 4 allocs/op
108-
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 5000000 300 ns/op 80 B/op 4 allocs/op
109-
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 1103 ns/op 320 B/op 9 allocs/op
110-
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 3000000 433 ns/op 320 B/op 9 allocs/op
111-
BenchmarkStructSimpleSuccess-8 5000000 360 ns/op 0 B/op 0 allocs/op
112-
BenchmarkStructSimpleSuccessParallel-8 20000000 110 ns/op 0 B/op 0 allocs/op
113-
BenchmarkStructSimpleFailure-8 2000000 783 ns/op 424 B/op 9 allocs/op
114-
BenchmarkStructSimpleFailureParallel-8 5000000 358 ns/op 424 B/op 9 allocs/op
115-
BenchmarkStructComplexSuccess-8 1000000 2120 ns/op 128 B/op 8 allocs/op
116-
BenchmarkStructComplexSuccessParallel-8 2000000 659 ns/op 128 B/op 8 allocs/op
117-
BenchmarkStructComplexFailure-8 300000 5126 ns/op 3041 B/op 53 allocs/op
118-
BenchmarkStructComplexFailureParallel-8 1000000 2261 ns/op 3041 B/op 53 allocs/op
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
119123
```
120124

121125
Complimentary Software

benchmarks_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package validator
22

33
import (
4+
"bytes"
45
sql "database/sql/driver"
56
"testing"
67
"time"
@@ -375,6 +376,110 @@ func BenchmarkStructSimpleCustomTypeFailureParallel(b *testing.B) {
375376
})
376377
}
377378

379+
func BenchmarkStructFilteredSuccess(b *testing.B) {
380+
381+
validate := New()
382+
383+
type Test struct {
384+
Name string `validate:"required"`
385+
NickName string `validate:"required"`
386+
}
387+
388+
test := &Test{
389+
Name: "Joey Bloggs",
390+
}
391+
392+
byts := []byte("Name")
393+
394+
fn := func(ns []byte) bool {
395+
return !bytes.HasSuffix(ns, byts)
396+
}
397+
398+
b.ResetTimer()
399+
for n := 0; n < b.N; n++ {
400+
validate.StructFiltered(test, fn)
401+
}
402+
}
403+
404+
func BenchmarkStructFilteredSuccessParallel(b *testing.B) {
405+
406+
validate := New()
407+
408+
type Test struct {
409+
Name string `validate:"required"`
410+
NickName string `validate:"required"`
411+
}
412+
413+
test := &Test{
414+
Name: "Joey Bloggs",
415+
}
416+
417+
byts := []byte("Name")
418+
419+
fn := func(ns []byte) bool {
420+
return !bytes.HasSuffix(ns, byts)
421+
}
422+
423+
b.ResetTimer()
424+
b.RunParallel(func(pb *testing.PB) {
425+
for pb.Next() {
426+
validate.StructFiltered(test, fn)
427+
}
428+
})
429+
}
430+
431+
func BenchmarkStructFilteredFailure(b *testing.B) {
432+
433+
validate := New()
434+
435+
type Test struct {
436+
Name string `validate:"required"`
437+
NickName string `validate:"required"`
438+
}
439+
440+
test := &Test{
441+
Name: "Joey Bloggs",
442+
}
443+
444+
byts := []byte("NickName")
445+
446+
fn := func(ns []byte) bool {
447+
return !bytes.HasSuffix(ns, byts)
448+
}
449+
450+
b.ResetTimer()
451+
for n := 0; n < b.N; n++ {
452+
validate.StructFiltered(test, fn)
453+
}
454+
}
455+
456+
func BenchmarkStructFilteredFailureParallel(b *testing.B) {
457+
458+
validate := New()
459+
460+
type Test struct {
461+
Name string `validate:"required"`
462+
NickName string `validate:"required"`
463+
}
464+
465+
test := &Test{
466+
Name: "Joey Bloggs",
467+
}
468+
469+
byts := []byte("NickName")
470+
471+
fn := func(ns []byte) bool {
472+
return !bytes.HasSuffix(ns, byts)
473+
}
474+
475+
b.ResetTimer()
476+
b.RunParallel(func(pb *testing.PB) {
477+
for pb.Next() {
478+
validate.StructFiltered(test, fn)
479+
}
480+
})
481+
}
482+
378483
func BenchmarkStructPartialSuccess(b *testing.B) {
379484

380485
validate := New()

errors.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const (
1313
fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag"
1414
)
1515

16+
// ValidationErrorsTranslations is the translation return type
1617
type ValidationErrorsTranslations map[string]string
1718

1819
// InvalidValidationError describes an invalid argument passed to
@@ -55,6 +56,7 @@ func (ve ValidationErrors) Error() string {
5556
return strings.TrimSpace(buff.String())
5657
}
5758

59+
// Translate translates all of the ValidationErrors
5860
func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
5961

6062
trans := make(ValidationErrorsTranslations)

validator.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ type validate struct {
1717
hasExcludes bool
1818
includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
1919

20+
ffn FilterFunc
21+
2022
// StructLevel & FieldLevel fields
2123
slflParent reflect.Value
2224
slCurrent reflect.Value
@@ -54,10 +56,19 @@ func (v *validate) validateStruct(parent reflect.Value, current reflect.Value, t
5456

5557
if v.isPartial {
5658

57-
_, ok = v.includeExclude[string(append(structNs, f.name...))]
59+
if v.ffn != nil {
60+
// used with StructFiltered
61+
if v.ffn(append(structNs, f.name...)) {
62+
continue
63+
}
5864

59-
if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
60-
continue
65+
} else {
66+
// used with StructPartial & StructExcept
67+
_, ok = v.includeExclude[string(append(structNs, f.name...))]
68+
69+
if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
70+
continue
71+
}
6172
}
6273
}
6374

validator_instance.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ var (
3636
defaultCField = &cField{namesEqual: true}
3737
)
3838

39+
// FilterFunc is the type used to filter fields using
40+
// StructFiltered(...) function.
41+
// returning true results in the field being filtered/skiped from
42+
// validation
43+
type FilterFunc func(ns []byte) bool
44+
3945
// CustomTypeFunc allows for overriding or adding custom field type handler functions
4046
// field = field value of the type to return a value to be validated
4147
// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
@@ -192,6 +198,7 @@ func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{
192198
v.hasCustomFuncs = true
193199
}
194200

201+
// RegisterTranslation registers translations against the provided tag.
195202
func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
196203

197204
if v.transTagFunc == nil {
@@ -248,6 +255,43 @@ func (v *Validate) Struct(s interface{}) (err error) {
248255
return
249256
}
250257

258+
// StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates
259+
// nested structs, unless otherwise specified.
260+
//
261+
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
262+
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
263+
func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) (err error) {
264+
265+
val := reflect.ValueOf(s)
266+
top := val
267+
268+
if val.Kind() == reflect.Ptr && !val.IsNil() {
269+
val = val.Elem()
270+
}
271+
272+
if val.Kind() != reflect.Struct || val.Type() == timeType {
273+
return &InvalidValidationError{Type: reflect.TypeOf(s)}
274+
}
275+
276+
// good to validate
277+
vd := v.pool.Get().(*validate)
278+
vd.top = top
279+
vd.isPartial = true
280+
vd.ffn = fn
281+
// vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
282+
283+
vd.validateStruct(top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
284+
285+
if len(vd.errs) > 0 {
286+
err = vd.errs
287+
vd.errs = nil
288+
}
289+
290+
v.pool.Put(vd)
291+
292+
return
293+
}
294+
251295
// StructPartial validates the fields passed in only, ignoring all others.
252296
// Fields may be provided in a namespaced fashion relative to the struct provided
253297
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
@@ -271,6 +315,7 @@ func (v *Validate) StructPartial(s interface{}, fields ...string) (err error) {
271315
vd := v.pool.Get().(*validate)
272316
vd.top = top
273317
vd.isPartial = true
318+
vd.ffn = nil
274319
vd.hasExcludes = false
275320
vd.includeExclude = make(map[string]struct{})
276321

@@ -349,6 +394,7 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) (err error) {
349394
vd := v.pool.Get().(*validate)
350395
vd.top = top
351396
vd.isPartial = true
397+
vd.ffn = nil
352398
vd.hasExcludes = true
353399
vd.includeExclude = make(map[string]struct{})
354400

0 commit comments

Comments
 (0)