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
56 changes: 28 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,34 +310,34 @@ Benchmarks
```go
$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4 5000000 305 ns/op 16 B/op 1 allocs/op
BenchmarkFieldFailure-4 5000000 301 ns/op 16 B/op 1 allocs/op
BenchmarkFieldDiveSuccess-4 500000 3544 ns/op 528 B/op 28 allocs/op
BenchmarkFieldDiveFailure-4 300000 4120 ns/op 928 B/op 32 allocs/op
BenchmarkFieldCustomTypeSuccess-4 3000000 465 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-4 2000000 769 ns/op 400 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-4 1000000 1372 ns/op 32 B/op 2 allocs/op
BenchmarkFieldOrTagFailure-4 1000000 1218 ns/op 432 B/op 6 allocs/op
BenchmarkStructLevelValidationSuccess-4 2000000 840 ns/op 160 B/op 6 allocs/op
BenchmarkStructLevelValidationFailure-4 1000000 1443 ns/op 592 B/op 11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1262 ns/op 80 B/op 5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1812 ns/op 624 B/op 11 allocs/op
BenchmarkStructPartialSuccess-4 1000000 1419 ns/op 400 B/op 11 allocs/op
BenchmarkStructPartialFailure-4 1000000 1967 ns/op 816 B/op 16 allocs/op
BenchmarkStructExceptSuccess-4 2000000 954 ns/op 368 B/op 9 allocs/op
BenchmarkStructExceptFailure-4 1000000 1422 ns/op 400 B/op 11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1286 ns/op 128 B/op 6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1885 ns/op 560 B/op 11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1948 ns/op 176 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 500000 2491 ns/op 608 B/op 14 allocs/op
BenchmarkStructSimpleSuccess-4 1000000 1239 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailure-4 1000000 1891 ns/op 624 B/op 11 allocs/op
BenchmarkStructSimpleSuccessParallel-4 5000000 386 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailureParallel-4 2000000 842 ns/op 624 B/op 11 allocs/op
BenchmarkStructComplexSuccess-4 200000 8604 ns/op 512 B/op 30 allocs/op
BenchmarkStructComplexFailure-4 100000 13332 ns/op 3416 B/op 72 allocs/op
BenchmarkStructComplexSuccessParallel-4 1000000 2929 ns/op 512 B/op 30 allocs/op
BenchmarkStructComplexFailureParallel-4 300000 5220 ns/op 3416 B/op 72 allocs/op
BenchmarkFieldSuccess-4 5000000 288 ns/op 16 B/op 1 allocs/op
BenchmarkFieldFailure-4 5000000 292 ns/op 16 B/op 1 allocs/op
BenchmarkFieldDiveSuccess-4 500000 3464 ns/op 528 B/op 28 allocs/op
BenchmarkFieldDiveFailure-4 500000 4031 ns/op 928 B/op 32 allocs/op
BenchmarkFieldCustomTypeSuccess-4 3000000 446 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-4 2000000 755 ns/op 400 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-4 1000000 1356 ns/op 32 B/op 2 allocs/op
BenchmarkFieldOrTagFailure-4 1000000 1177 ns/op 432 B/op 6 allocs/op
BenchmarkStructLevelValidationSuccess-4 2000000 810 ns/op 160 B/op 6 allocs/op
BenchmarkStructLevelValidationFailure-4 1000000 1424 ns/op 592 B/op 11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1231 ns/op 80 B/op 5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1779 ns/op 624 B/op 11 allocs/op
BenchmarkStructPartialSuccess-4 1000000 1396 ns/op 400 B/op 11 allocs/op
BenchmarkStructPartialFailure-4 1000000 1928 ns/op 816 B/op 16 allocs/op
BenchmarkStructExceptSuccess-4 2000000 946 ns/op 368 B/op 9 allocs/op
BenchmarkStructExceptFailure-4 1000000 1396 ns/op 400 B/op 11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1241 ns/op 128 B/op 6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1837 ns/op 560 B/op 11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1867 ns/op 176 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 500000 2495 ns/op 608 B/op 14 allocs/op
BenchmarkStructSimpleSuccess-4 1000000 1253 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailure-4 1000000 1852 ns/op 624 B/op 11 allocs/op
BenchmarkStructSimpleSuccessParallel-4 5000000 372 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailureParallel-4 2000000 784 ns/op 624 B/op 11 allocs/op
BenchmarkStructComplexSuccess-4 200000 8080 ns/op 512 B/op 30 allocs/op
BenchmarkStructComplexFailure-4 100000 12643 ns/op 3416 B/op 72 allocs/op
BenchmarkStructComplexSuccessParallel-4 1000000 2688 ns/op 512 B/op 30 allocs/op
BenchmarkStructComplexFailureParallel-4 300000 4727 ns/op 3416 B/op 72 allocs/op
```

How to Contribute
Expand Down
64 changes: 31 additions & 33 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ func (v *Validate) StructPartial(current interface{}, fields ...string) error {

errs := v.errsPool.Get().(ValidationErrors)

v.tranverseStruct(sv, sv, sv, blank, errs, true, len(m) != 0, false, m)
v.tranverseStruct(sv, sv, sv, blank, errs, true, len(m) != 0, false, m, false)

if len(errs) == 0 {
v.errsPool.Put(errs)
Expand All @@ -440,7 +440,7 @@ func (v *Validate) StructExcept(current interface{}, fields ...string) error {

errs := v.errsPool.Get().(ValidationErrors)

v.tranverseStruct(sv, sv, sv, blank, errs, true, len(m) != 0, true, m)
v.tranverseStruct(sv, sv, sv, blank, errs, true, len(m) != 0, true, m, false)

if len(errs) == 0 {
v.errsPool.Put(errs)
Expand All @@ -459,7 +459,7 @@ func (v *Validate) Struct(current interface{}) error {
errs := v.errsPool.Get().(ValidationErrors)
sv := reflect.ValueOf(current)

v.tranverseStruct(sv, sv, sv, blank, errs, true, false, false, nil)
v.tranverseStruct(sv, sv, sv, blank, errs, true, false, false, nil, false)

if len(errs) == 0 {
v.errsPool.Put(errs)
Expand All @@ -470,7 +470,7 @@ func (v *Validate) Struct(current interface{}) error {
}

// tranverseStruct traverses a structs fields and then passes them to be validated by traverseField
func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}) {
func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}, isStructOnly bool) {

if current.Kind() == reflect.Ptr && !current.IsNil() {
current = current.Elem()
Expand All @@ -487,39 +487,44 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec
errPrefix += typ.Name() + "."
}

numFields := current.NumField()
// structonly tag present don't tranverseFields
// but must still check and run below struct level validation
// if present
if !isStructOnly {
numFields := current.NumField()

var fld reflect.StructField
var customName string
var fld reflect.StructField
var customName string

for i := 0; i < numFields; i++ {
fld = typ.Field(i)
for i := 0; i < numFields; i++ {
fld = typ.Field(i)

if !unicode.IsUpper(rune(fld.Name[0])) {
continue
}
if !unicode.IsUpper(rune(fld.Name[0])) {
continue
}

if partial {
if partial {

_, ok = includeExclude[errPrefix+fld.Name]
_, ok = includeExclude[errPrefix+fld.Name]

if (ok && exclude) || (!ok && !exclude) {
continue
if (ok && exclude) || (!ok && !exclude) {
continue
}
}
}

customName = fld.Name
if v.fieldNameTag != "" {
customName = fld.Name
if v.fieldNameTag != "" {

name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]

// dash check is for json "-" means don't output in json
if name != "" && name != "-" {
customName = name
// dash check is for json "-" means don't output in json
if name != "" && name != "-" {
customName = name
}
}
}

v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.tagName), fld.Name, customName, partial, exclude, includeExclude)
v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.tagName), fld.Name, customName, partial, exclude, includeExclude)
}
}

// check if any struct level validations, after all field validations already checked.
Expand Down Expand Up @@ -590,14 +595,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
typ = current.Type()

if typ != timeType {

// required passed validation above so stop here
// if only validating the structs existance.
if strings.Contains(tag, structOnlyTag) {
return
}

v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false, partial, exclude, includeExclude)
v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false, partial, exclude, includeExclude, strings.Contains(tag, structOnlyTag))
return
}
}
Expand Down