Skip to content

Commit d627af8

Browse files
Dean KarnDean Karn
authored andcommitted
Merge pull request #87 from bluesuncorp/v5-development
Correct interface panic & add interface type handling
2 parents 740b7d0 + f6358a9 commit d627af8

File tree

2 files changed

+344
-16
lines changed

2 files changed

+344
-16
lines changed

validator.go

Lines changed: 124 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,6 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
353353
structName = structType.Name()
354354
numFields = structValue.NumField()
355355
cs = &cachedStruct{name: structName, children: numFields}
356-
structCache.Set(structType, cs)
357356
}
358357

359358
validationErrors := structPool.Borrow()
@@ -429,24 +428,62 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
429428
continue
430429
}
431430

432-
if valueField.Kind() == reflect.Ptr && valueField.IsNil() {
431+
if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {
433432

434433
if strings.Contains(cField.tag, omitempty) {
435-
continue
434+
goto CACHEFIELD
436435
}
437436

438-
if strings.Contains(cField.tag, required) {
437+
tags := strings.Split(cField.tag, tagSeparator)
438+
439+
if len(tags) > 0 {
440+
441+
var param string
442+
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
443+
444+
if len(vals) > 1 {
445+
param = vals[1]
446+
}
439447

440448
validationErrors.Errors[cField.name] = &FieldError{
441449
Field: cField.name,
442-
Tag: required,
450+
Tag: vals[0],
451+
Param: param,
443452
Value: valueField.Interface(),
453+
Kind: valueField.Kind(),
454+
Type: valueField.Type(),
444455
}
445456

446-
continue
457+
goto CACHEFIELD
458+
}
459+
}
460+
461+
// if we get here, the field is interface and could be a struct or a field
462+
// and we need to check the inner type and validate
463+
if cField.kind == reflect.Interface {
464+
465+
valueField = valueField.Elem()
466+
467+
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
468+
valueField = valueField.Elem()
469+
}
470+
471+
if valueField.Kind() == reflect.Struct {
472+
goto VALIDATESTRUCT
473+
}
474+
475+
// sending nil for cField as it was type interface and could be anything
476+
// each time and so must be calculated each time and can't be cached reliably
477+
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil {
478+
validationErrors.Errors[fieldError.Field] = fieldError
479+
// free up memory reference
480+
fieldError = nil
447481
}
482+
483+
goto CACHEFIELD
448484
}
449485

486+
VALIDATESTRUCT:
450487
if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
451488
validationErrors.StructErrors[cField.name] = structErrors
452489
// free up memory map no longer needed
@@ -486,11 +523,14 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
486523
}
487524
}
488525

526+
CACHEFIELD:
489527
if !isCached {
490528
cs.fields = append(cs.fields, cField)
491529
}
492530
}
493531

532+
structCache.Set(structType, cs)
533+
494534
if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
495535
structPool.Return(validationErrors)
496536
return nil
@@ -709,26 +749,60 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
709749
continue
710750
}
711751

712-
if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
752+
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
713753

714-
if strings.Contains(cField.tag, omitempty) {
754+
if strings.Contains(cField.diveTag, omitempty) {
715755
continue
716756
}
717757

718-
if strings.Contains(cField.tag, required) {
758+
tags := strings.Split(cField.diveTag, tagSeparator)
759+
760+
if len(tags) > 0 {
761+
762+
var param string
763+
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
764+
765+
if len(vals) > 1 {
766+
param = vals[1]
767+
}
719768

720769
errs[key.Interface()] = &FieldError{
721770
Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()),
722-
Tag: required,
771+
Tag: vals[0],
772+
Param: param,
723773
Value: idxField.Interface(),
724-
Kind: reflect.Ptr,
774+
Kind: idxField.Kind(),
725775
Type: cField.mapSubtype,
726776
}
727777
}
728778

729779
continue
730780
}
731781

782+
// if we get here, the field is interface and could be a struct or a field
783+
// and we need to check the inner type and validate
784+
if idxField.Kind() == reflect.Interface {
785+
786+
idxField = idxField.Elem()
787+
788+
if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
789+
idxField = idxField.Elem()
790+
}
791+
792+
if idxField.Kind() == reflect.Struct {
793+
goto VALIDATESTRUCT
794+
}
795+
796+
// sending nil for cField as it was type interface and could be anything
797+
// each time and so must be calculated each time and can't be cached reliably
798+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
799+
errs[key.Interface()] = fieldError
800+
}
801+
802+
continue
803+
}
804+
805+
VALIDATESTRUCT:
732806
if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
733807
errs[key.Interface()] = structErrors
734808
}
@@ -768,26 +842,60 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
768842
continue
769843
}
770844

771-
if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
845+
if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
772846

773-
if strings.Contains(cField.tag, omitempty) {
847+
if strings.Contains(cField.diveTag, omitempty) {
774848
continue
775849
}
776850

777-
if strings.Contains(cField.tag, required) {
851+
tags := strings.Split(cField.diveTag, tagSeparator)
852+
853+
if len(tags) > 0 {
854+
855+
var param string
856+
vals := strings.SplitN(tags[0], tagKeySeparator, 2)
857+
858+
if len(vals) > 1 {
859+
param = vals[1]
860+
}
778861

779862
errs[i] = &FieldError{
780863
Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i),
781-
Tag: required,
864+
Tag: vals[0],
865+
Param: param,
782866
Value: idxField.Interface(),
783-
Kind: reflect.Ptr,
867+
Kind: idxField.Kind(),
784868
Type: cField.sliceSubtype,
785869
}
786870
}
787871

788872
continue
789873
}
790874

875+
// if we get here, the field is interface and could be a struct or a field
876+
// and we need to check the inner type and validate
877+
if idxField.Kind() == reflect.Interface {
878+
879+
idxField = idxField.Elem()
880+
881+
if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
882+
idxField = idxField.Elem()
883+
}
884+
885+
if idxField.Kind() == reflect.Struct {
886+
goto VALIDATESTRUCT
887+
}
888+
889+
// sending nil for cField as it was type interface and could be anything
890+
// each time and so must be calculated each time and can't be cached reliably
891+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
892+
errs[i] = fieldError
893+
}
894+
895+
continue
896+
}
897+
898+
VALIDATESTRUCT:
791899
if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
792900
errs[i] = structErrors
793901
}

0 commit comments

Comments
 (0)