@@ -20,20 +20,19 @@ import (
2020)
2121
2222const (
23- utf8HexComma = "0x2C"
24- tagSeparator = ","
25- orSeparator = "|"
26- noValidationTag = "-"
27- tagKeySeparator = "="
28- structOnlyTag = "structonly"
29- omitempty = "omitempty"
30- required = "required"
31- fieldErrMsg = "Field validation for \" %s\" failed on the \" %s\" tag"
32- sliceErrMsg = "Field validation for \" %s\" failed at index \" %d\" with error(s): %s"
33- mapErrMsg = "Field validation for \" %s\" failed on key \" %v\" with error(s): %s"
34- structErrMsg = "Struct:%s\n "
35- diveTag = "dive"
36- // diveSplit = "," + diveTag
23+ utf8HexComma = "0x2C"
24+ tagSeparator = ","
25+ orSeparator = "|"
26+ noValidationTag = "-"
27+ tagKeySeparator = "="
28+ structOnlyTag = "structonly"
29+ omitempty = "omitempty"
30+ required = "required"
31+ fieldErrMsg = "Field validation for \" %s\" failed on the \" %s\" tag"
32+ sliceErrMsg = "Field validation for \" %s\" failed at index \" %d\" with error(s): %s"
33+ mapErrMsg = "Field validation for \" %s\" failed on key \" %v\" with error(s): %s"
34+ structErrMsg = "Struct:%s\n "
35+ diveTag = "dive"
3736 arrayIndexFieldName = "%s[%d]"
3837 mapIndexFieldName = "%s[%v]"
3938)
@@ -193,6 +192,82 @@ func (e *FieldError) Error() string {
193192 return fmt .Sprintf (fieldErrMsg , e .Field , e .Tag )
194193}
195194
195+ // Flatten flattens the FieldError hierarchical structure into a flat namespace style field name
196+ // for those that want/need it.
197+ // This is now needed because of the new dive functionality
198+ func (e * FieldError ) Flatten () map [string ]* FieldError {
199+
200+ errs := map [string ]* FieldError {}
201+
202+ if e .IsPlaceholderErr {
203+
204+ if e .IsSliceOrArray {
205+ for key , err := range e .SliceOrArrayErrs {
206+
207+ fe , ok := err .(* FieldError )
208+
209+ if ok {
210+
211+ if flat := fe .Flatten (); flat != nil && len (flat ) > 0 {
212+ for k , v := range flat {
213+ if fe .IsPlaceholderErr {
214+ errs [fmt .Sprintf ("[%#v]%s" , key , k )] = v
215+ } else {
216+ errs [fmt .Sprintf ("[%#v]" , key )] = v
217+ }
218+
219+ }
220+ }
221+ } else {
222+
223+ se := err .(* StructErrors )
224+
225+ if flat := se .Flatten (); flat != nil && len (flat ) > 0 {
226+ for k , v := range flat {
227+ errs [fmt .Sprintf ("[%#v].%s.%s" , key , se .Struct , k )] = v
228+ }
229+ }
230+ }
231+ }
232+ }
233+
234+ if e .IsMap {
235+ for key , err := range e .MapErrs {
236+
237+ fe , ok := err .(* FieldError )
238+
239+ if ok {
240+
241+ if flat := fe .Flatten (); flat != nil && len (flat ) > 0 {
242+ for k , v := range flat {
243+ if fe .IsPlaceholderErr {
244+ errs [fmt .Sprintf ("[%#v]%s" , key , k )] = v
245+ } else {
246+ errs [fmt .Sprintf ("[%#v]" , key )] = v
247+ }
248+ }
249+ }
250+ } else {
251+
252+ se := err .(* StructErrors )
253+
254+ if flat := se .Flatten (); flat != nil && len (flat ) > 0 {
255+ for k , v := range flat {
256+ errs [fmt .Sprintf ("[%#v].%s.%s" , key , se .Struct , k )] = v
257+ }
258+ }
259+ }
260+ }
261+ }
262+
263+ return errs
264+ }
265+
266+ errs [e .Field ] = e
267+
268+ return errs
269+ }
270+
196271// StructErrors is hierarchical list of field and struct validation errors
197272// for a non hierarchical representation please see the Flatten method for StructErrors
198273type StructErrors struct {
@@ -234,7 +309,17 @@ func (e *StructErrors) Flatten() map[string]*FieldError {
234309
235310 for _ , f := range e .Errors {
236311
237- errs [f .Field ] = f
312+ if flat := f .Flatten (); flat != nil && len (flat ) > 0 {
313+
314+ for k , fe := range flat {
315+
316+ if f .IsPlaceholderErr {
317+ errs [f .Field + k ] = fe
318+ } else {
319+ errs [k ] = fe
320+ }
321+ }
322+ }
238323 }
239324
240325 for key , val := range e .StructErrors {
@@ -353,7 +438,6 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
353438 structName = structType .Name ()
354439 numFields = structValue .NumField ()
355440 cs = & cachedStruct {name : structName , children : numFields }
356- structCache .Set (structType , cs )
357441 }
358442
359443 validationErrors := structPool .Borrow ()
@@ -429,24 +513,62 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
429513 continue
430514 }
431515
432- if valueField .Kind () == reflect .Ptr && valueField .IsNil () {
516+ if ( valueField .Kind () == reflect .Ptr || cField . kind == reflect . Interface ) && valueField .IsNil () {
433517
434518 if strings .Contains (cField .tag , omitempty ) {
435- continue
519+ goto CACHEFIELD
436520 }
437521
438- if strings .Contains (cField .tag , required ) {
522+ tags := strings .Split (cField .tag , tagSeparator )
523+
524+ if len (tags ) > 0 {
525+
526+ var param string
527+ vals := strings .SplitN (tags [0 ], tagKeySeparator , 2 )
528+
529+ if len (vals ) > 1 {
530+ param = vals [1 ]
531+ }
439532
440533 validationErrors .Errors [cField .name ] = & FieldError {
441534 Field : cField .name ,
442- Tag : required ,
535+ Tag : vals [0 ],
536+ Param : param ,
443537 Value : valueField .Interface (),
538+ Kind : valueField .Kind (),
539+ Type : valueField .Type (),
444540 }
445541
446- continue
542+ goto CACHEFIELD
543+ }
544+ }
545+
546+ // if we get here, the field is interface and could be a struct or a field
547+ // and we need to check the inner type and validate
548+ if cField .kind == reflect .Interface {
549+
550+ valueField = valueField .Elem ()
551+
552+ if valueField .Kind () == reflect .Ptr && ! valueField .IsNil () {
553+ valueField = valueField .Elem ()
447554 }
555+
556+ if valueField .Kind () == reflect .Struct {
557+ goto VALIDATESTRUCT
558+ }
559+
560+ // sending nil for cField as it was type interface and could be anything
561+ // each time and so must be calculated each time and can't be cached reliably
562+ if fieldError := v .fieldWithNameAndValue (top , current , valueField .Interface (), cField .tag , cField .name , false , nil ); fieldError != nil {
563+ validationErrors .Errors [fieldError .Field ] = fieldError
564+ // free up memory reference
565+ fieldError = nil
566+ }
567+
568+ goto CACHEFIELD
448569 }
449570
571+ VALIDATESTRUCT:
450572 if structErrors := v .structRecursive (top , valueField .Interface (), valueField .Interface ()); structErrors != nil {
451573 validationErrors .StructErrors [cField .name ] = structErrors
452574 // free up memory map no longer needed
@@ -486,11 +608,14 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
486608 }
487609 }
488610
611+ CACHEFIELD:
489612 if ! isCached {
490613 cs .fields = append (cs .fields , cField )
491614 }
492615 }
493616
617+ structCache .Set (structType , cs )
618+
494619 if len (validationErrors .Errors ) == 0 && len (validationErrors .StructErrors ) == 0 {
495620 structPool .Return (validationErrors )
496621 return nil
@@ -709,26 +834,60 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
709834 continue
710835 }
711836
712- if idxField .Kind () == reflect .Ptr && idxField .IsNil () {
837+ if ( idxField .Kind () == reflect .Ptr || idxField . Kind () == reflect . Interface ) && idxField .IsNil () {
713838
714- if strings .Contains (cField .tag , omitempty ) {
839+ if strings .Contains (cField .diveTag , omitempty ) {
715840 continue
716841 }
717842
718- if strings .Contains (cField .tag , required ) {
843+ tags := strings .Split (cField .diveTag , tagSeparator )
844+
845+ if len (tags ) > 0 {
846+
847+ var param string
848+ vals := strings .SplitN (tags [0 ], tagKeySeparator , 2 )
849+
850+ if len (vals ) > 1 {
851+ param = vals [1 ]
852+ }
719853
720854 errs [key .Interface ()] = & FieldError {
721855 Field : fmt .Sprintf (mapIndexFieldName , cField .name , key .Interface ()),
722- Tag : required ,
856+ Tag : vals [0 ],
857+ Param : param ,
723858 Value : idxField .Interface (),
724- Kind : reflect . Ptr ,
859+ Kind : idxField . Kind () ,
725860 Type : cField .mapSubtype ,
726861 }
727862 }
728863
729864 continue
730865 }
731866
867+ // if we get here, the field is interface and could be a struct or a field
868+ // and we need to check the inner type and validate
869+ if idxField .Kind () == reflect .Interface {
870+
871+ idxField = idxField .Elem ()
872+
873+ if idxField .Kind () == reflect .Ptr && ! idxField .IsNil () {
874+ idxField = idxField .Elem ()
875+ }
876+
877+ if idxField .Kind () == reflect .Struct {
878+ goto VALIDATESTRUCT
879+ }
880+
881+ // sending nil for cField as it was type interface and could be anything
882+ // each time and so must be calculated each time and can't be cached reliably
883+ if fieldError := v .fieldWithNameAndValue (val , current , idxField .Interface (), cField .diveTag , fmt .Sprintf (mapIndexFieldName , cField .name , key .Interface ()), false , nil ); fieldError != nil {
884+ errs [key .Interface ()] = fieldError
885+ }
886+
887+ continue
888+ }
889+
890+ VALIDATESTRUCT:
732891 if structErrors := v .structRecursive (val , current , idxField .Interface ()); structErrors != nil {
733892 errs [key .Interface ()] = structErrors
734893 }
@@ -768,26 +927,60 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
768927 continue
769928 }
770929
771- if idxField .Kind () == reflect .Ptr && idxField .IsNil () {
930+ if ( idxField .Kind () == reflect .Ptr || idxField . Kind () == reflect . Interface ) && idxField .IsNil () {
772931
773- if strings .Contains (cField .tag , omitempty ) {
932+ if strings .Contains (cField .diveTag , omitempty ) {
774933 continue
775934 }
776935
777- if strings .Contains (cField .tag , required ) {
936+ tags := strings .Split (cField .diveTag , tagSeparator )
937+
938+ if len (tags ) > 0 {
939+
940+ var param string
941+ vals := strings .SplitN (tags [0 ], tagKeySeparator , 2 )
942+
943+ if len (vals ) > 1 {
944+ param = vals [1 ]
945+ }
778946
779947 errs [i ] = & FieldError {
780948 Field : fmt .Sprintf (arrayIndexFieldName , cField .name , i ),
781- Tag : required ,
949+ Tag : vals [0 ],
950+ Param : param ,
782951 Value : idxField .Interface (),
783- Kind : reflect . Ptr ,
952+ Kind : idxField . Kind () ,
784953 Type : cField .sliceSubtype ,
785954 }
786955 }
787956
788957 continue
789958 }
790959
960+ // if we get here, the field is interface and could be a struct or a field
961+ // and we need to check the inner type and validate
962+ if idxField .Kind () == reflect .Interface {
963+
964+ idxField = idxField .Elem ()
965+
966+ if idxField .Kind () == reflect .Ptr && ! idxField .IsNil () {
967+ idxField = idxField .Elem ()
968+ }
969+
970+ if idxField .Kind () == reflect .Struct {
971+ goto VALIDATESTRUCT
972+ }
973+
974+ // sending nil for cField as it was type interface and could be anything
975+ // each time and so must be calculated each time and can't be cached reliably
976+ if fieldError := v .fieldWithNameAndValue (val , current , idxField .Interface (), cField .diveTag , fmt .Sprintf (arrayIndexFieldName , cField .name , i ), false , nil ); fieldError != nil {
977+ errs [i ] = fieldError
978+ }
979+
980+ continue
981+ }
982+
983+ VALIDATESTRUCT:
791984 if structErrors := v .structRecursive (val , current , idxField .Interface ()); structErrors != nil {
792985 errs [i ] = structErrors
793986 }
0 commit comments