@@ -16,7 +16,6 @@ import (
1616 "strings"
1717 "sync"
1818 "time"
19- "unicode"
2019)
2120
2221const (
@@ -196,7 +195,7 @@ func New(config *Config) *Validate {
196195 tagName : config .TagName ,
197196 fieldNameTag : config .FieldNameTag ,
198197 tagCache : & tagCacheMap {m : map [string ]* cachedTag {}},
199- structCache : & structCacheMap {m : map [string ]* cachedStruct {}},
198+ structCache : & structCacheMap {m : map [reflect. Type ]* cachedStruct {}},
200199 errsPool : & sync.Pool {New : func () interface {} {
201200 return ValidationErrors {}
202201 }}}
@@ -307,7 +306,7 @@ func (v *Validate) Field(field interface{}, tag string) error {
307306 errs := v .errsPool .Get ().(ValidationErrors )
308307 fieldVal := reflect .ValueOf (field )
309308
310- v .traverseField (fieldVal , fieldVal , fieldVal , blank , errs , false , tag , blank , blank , false , false , nil )
309+ v .traverseField (fieldVal , fieldVal , fieldVal , blank , errs , false , tag , blank , blank , false , false , nil , nil )
311310
312311 if len (errs ) == 0 {
313312 v .errsPool .Put (errs )
@@ -327,7 +326,7 @@ func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string
327326 errs := v .errsPool .Get ().(ValidationErrors )
328327 topVal := reflect .ValueOf (val )
329328
330- v .traverseField (topVal , topVal , reflect .ValueOf (field ), blank , errs , false , tag , blank , blank , false , false , nil )
329+ v .traverseField (topVal , topVal , reflect .ValueOf (field ), blank , errs , false , tag , blank , blank , false , false , nil , nil )
331330
332331 if len (errs ) == 0 {
333332 v .errsPool .Put (errs )
@@ -452,50 +451,80 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec
452451 panic ("value passed for validation is not a struct" )
453452 }
454453
455- var ok bool
454+ // var ok bool
456455 typ := current .Type ()
457456
457+ sName := typ .Name ()
458+
458459 if useStructName {
459- errPrefix += typ . Name () + "."
460+ errPrefix += sName + "."
460461 }
461462
462463 // structonly tag present don't tranverseFields
463464 // but must still check and run below struct level validation
464465 // if present
465466 if ! isStructOnly {
466- numFields := current .NumField ()
467467
468468 var fld reflect.StructField
469- var customName string
470469
471- for i := 0 ; i < numFields ; i ++ {
472- fld = typ .Field (i )
470+ // is anonymous struct, cannot parse or cache as
471+ // it has no name to index by
472+ if len (sName ) == 0 {
473473
474- if ! unicode . IsUpper ( rune ( fld . Name [ 0 ])) {
475- continue
476- }
474+ var customName string
475+ var ok bool
476+ numFields := current . NumField ()
477477
478- if partial {
478+ for i := 0 ; i < numFields ; i ++ {
479479
480- _ , ok = includeExclude [ errPrefix + fld . Name ]
480+ fld = typ . Field ( i )
481481
482- if ( ok && exclude ) || ( ! ok && ! exclude ) {
482+ if len ( fld . PkgPath ) != 0 {
483483 continue
484484 }
485- }
486485
487- customName = fld .Name
488- if v .fieldNameTag != "" {
486+ if partial {
489487
490- name := strings . SplitN ( fld .Tag . Get ( v . fieldNameTag ), "," , 2 )[ 0 ]
488+ _ , ok = includeExclude [ errPrefix + fld .Name ]
491489
492- // dash check is for json "-" means don't output in json
493- if name != "" && name != "-" {
494- customName = name
490+ if ( ok && exclude ) || ( ! ok && ! exclude ) {
491+ continue
492+ }
495493 }
494+
495+ customName = fld .Name
496+ if v .fieldNameTag != "" {
497+
498+ name := strings .SplitN (fld .Tag .Get (v .fieldNameTag ), "," , 2 )[0 ]
499+
500+ // dash check is for json "-" means don't output in json
501+ if name != "" && name != "-" {
502+ customName = name
503+ }
504+ }
505+
506+ v .traverseField (topStruct , currentStruct , current .Field (i ), errPrefix , errs , true , fld .Tag .Get (v .tagName ), fld .Name , customName , partial , exclude , includeExclude , nil )
496507 }
508+ } else {
509+ s , ok := v .structCache .Get (typ )
510+ if ! ok {
511+ s = v .parseStruct (current , sName )
512+ }
513+
514+ for i , f := range s .fields {
515+
516+ if partial {
517+
518+ _ , ok = includeExclude [errPrefix + f .Name ]
497519
498- v .traverseField (topStruct , currentStruct , current .Field (i ), errPrefix , errs , true , fld .Tag .Get (v .tagName ), fld .Name , customName , partial , exclude , includeExclude )
520+ if (ok && exclude ) || (! ok && ! exclude ) {
521+ continue
522+ }
523+ }
524+ fld = typ .Field (i )
525+
526+ v .traverseField (topStruct , currentStruct , current .Field (i ), errPrefix , errs , true , f .CachedTag .tag , fld .Name , f .AltName , partial , exclude , includeExclude , f .CachedTag )
527+ }
499528 }
500529 }
501530
@@ -508,16 +537,19 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec
508537}
509538
510539// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
511- func (v * Validate ) traverseField (topStruct reflect.Value , currentStruct reflect.Value , current reflect.Value , errPrefix string , errs ValidationErrors , isStructField bool , tag , name , customName string , partial bool , exclude bool , includeExclude map [string ]* struct {}) {
540+ func (v * Validate ) traverseField (topStruct reflect.Value , currentStruct reflect.Value , current reflect.Value , errPrefix string , errs ValidationErrors , isStructField bool , tag , name , customName string , partial bool , exclude bool , includeExclude map [string ]* struct {}, cTag * cachedTag ) {
512541
513542 if tag == skipValidationTag {
514543 return
515544 }
516545
517- cTag , isCached := v .tagCache .Get (tag )
546+ if cTag == nil {
547+ var isCached bool
548+ cTag , isCached = v .tagCache .Get (tag )
518549
519- if ! isCached {
520- cTag = v .parseTags (tag , name )
550+ if ! isCached {
551+ cTag = v .parseTags (tag , name )
552+ }
521553 }
522554
523555 current , kind := v .ExtractType (current )
@@ -615,9 +647,9 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
615647 // or panic ;)
616648 switch kind {
617649 case reflect .Slice , reflect .Array :
618- v .traverseSlice (topStruct , currentStruct , current , errPrefix , errs , diveSubTag , name , customName , partial , exclude , includeExclude )
650+ v .traverseSlice (topStruct , currentStruct , current , errPrefix , errs , diveSubTag , name , customName , partial , exclude , includeExclude , nil )
619651 case reflect .Map :
620- v .traverseMap (topStruct , currentStruct , current , errPrefix , errs , diveSubTag , name , customName , partial , exclude , includeExclude )
652+ v .traverseMap (topStruct , currentStruct , current , errPrefix , errs , diveSubTag , name , customName , partial , exclude , includeExclude , nil )
621653 default :
622654 // throw error, if not a slice or map then should not have gotten here
623655 // bad dive tag
@@ -627,18 +659,18 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
627659}
628660
629661// traverseSlice traverses a Slice or Array's elements and passes them to traverseField for validation
630- func (v * Validate ) traverseSlice (topStruct reflect.Value , currentStruct reflect.Value , current reflect.Value , errPrefix string , errs ValidationErrors , tag , name , customName string , partial bool , exclude bool , includeExclude map [string ]* struct {}) {
662+ func (v * Validate ) traverseSlice (topStruct reflect.Value , currentStruct reflect.Value , current reflect.Value , errPrefix string , errs ValidationErrors , tag , name , customName string , partial bool , exclude bool , includeExclude map [string ]* struct {}, cTag * cachedTag ) {
631663
632664 for i := 0 ; i < current .Len (); i ++ {
633- v .traverseField (topStruct , currentStruct , current .Index (i ), errPrefix , errs , false , tag , fmt .Sprintf (arrayIndexFieldName , name , i ), fmt .Sprintf (arrayIndexFieldName , customName , i ), partial , exclude , includeExclude )
665+ v .traverseField (topStruct , currentStruct , current .Index (i ), errPrefix , errs , false , tag , fmt .Sprintf (arrayIndexFieldName , name , i ), fmt .Sprintf (arrayIndexFieldName , customName , i ), partial , exclude , includeExclude , cTag )
634666 }
635667}
636668
637669// traverseMap traverses a map's elements and passes them to traverseField for validation
638- func (v * Validate ) traverseMap (topStruct reflect.Value , currentStruct reflect.Value , current reflect.Value , errPrefix string , errs ValidationErrors , tag , name , customName string , partial bool , exclude bool , includeExclude map [string ]* struct {}) {
670+ func (v * Validate ) traverseMap (topStruct reflect.Value , currentStruct reflect.Value , current reflect.Value , errPrefix string , errs ValidationErrors , tag , name , customName string , partial bool , exclude bool , includeExclude map [string ]* struct {}, cTag * cachedTag ) {
639671
640672 for _ , key := range current .MapKeys () {
641- v .traverseField (topStruct , currentStruct , current .MapIndex (key ), errPrefix , errs , false , tag , fmt .Sprintf (mapIndexFieldName , name , key .Interface ()), fmt .Sprintf (mapIndexFieldName , customName , key .Interface ()), partial , exclude , includeExclude )
673+ v .traverseField (topStruct , currentStruct , current .MapIndex (key ), errPrefix , errs , false , tag , fmt .Sprintf (mapIndexFieldName , name , key .Interface ()), fmt .Sprintf (mapIndexFieldName , customName , key .Interface ()), partial , exclude , includeExclude , cTag )
642674 }
643675}
644676
0 commit comments