Skip to content

Commit 14f176e

Browse files
joeybloggsjoeybloggs
authored andcommitted
add traverseMap
for #78
1 parent 98f4165 commit 14f176e

File tree

2 files changed

+103
-20
lines changed

2 files changed

+103
-20
lines changed

validator.go

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,22 @@ import (
2020
)
2121

2222
const (
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\" failed with error(s): %s"
33-
mapErrMsg = "Field validation for \"%s\" failed with key \"%v\" failed with error(s): %s"
34-
structErrMsg = "Struct:%s\n"
35-
diveTag = "dive"
36-
diveSplit = "," + diveTag
37-
indexFieldName = "%s[%d]"
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
37+
arrayIndexFieldName = "%s[%d]"
38+
mapIndexFieldName = "%s[%v]"
3839
)
3940

4041
var structPool *pool
@@ -670,7 +671,18 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
670671
}
671672

672673
} else if cField.isMap {
673-
// return if error here
674+
if errs := v.traverseMap(val, current, valueField, cField); errs != nil && len(errs) > 0 {
675+
676+
return &FieldError{
677+
Field: cField.name,
678+
Kind: cField.kind,
679+
Type: cField.typ,
680+
Value: f,
681+
IsPlaceholderErr: true,
682+
IsMap: true,
683+
MapErrs: errs,
684+
}
685+
}
674686
} else {
675687
// throw error, if not a slice or map then should not have gotten here
676688
panic("dive error! can't dive on a non slice or map")
@@ -680,6 +692,65 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
680692
return nil
681693
}
682694

695+
func (v *Validate) traverseMap(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[interface{}]error {
696+
697+
errs := map[interface{}]error{}
698+
699+
for _, key := range valueField.MapKeys() {
700+
701+
idxField := valueField.MapIndex(key)
702+
703+
if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() {
704+
idxField = idxField.Elem()
705+
cField.sliceSubKind = idxField.Kind()
706+
}
707+
708+
switch cField.sliceSubKind {
709+
case reflect.Struct, reflect.Interface:
710+
711+
if cField.isTimeSubtype {
712+
713+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
714+
errs[key.Interface()] = fieldError
715+
}
716+
717+
continue
718+
}
719+
720+
if idxField.Kind() == reflect.Ptr && idxField.IsNil() {
721+
722+
if strings.Contains(cField.tag, omitempty) {
723+
continue
724+
}
725+
726+
if strings.Contains(cField.tag, required) {
727+
728+
errs[key.Interface()] = &FieldError{
729+
Field: cField.name,
730+
Tag: required,
731+
Value: idxField.Interface(),
732+
Kind: reflect.Ptr,
733+
Type: cField.sliceSubtype,
734+
}
735+
}
736+
737+
continue
738+
}
739+
740+
if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
741+
errs[key.Interface()] = structErrors
742+
}
743+
744+
default:
745+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
746+
errs[key.Interface()] = fieldError
747+
}
748+
}
749+
}
750+
751+
return errs
752+
}
753+
683754
func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error {
684755

685756
errs := map[int]error{}
@@ -698,7 +769,7 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
698769

699770
if cField.isTimeSubtype {
700771

701-
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, cField.name, false, nil); fieldError != nil {
772+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
702773
errs[i] = fieldError
703774
}
704775

@@ -730,7 +801,7 @@ func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, va
730801
}
731802

732803
default:
733-
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(indexFieldName, cField.name, i), false, nil); fieldError != nil {
804+
if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
734805
errs[i] = fieldError
735806
}
736807
}

validator_test.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e
227227
}
228228

229229
func TestMapDiveValidation(t *testing.T) {
230+
231+
type Test struct {
232+
Errs map[int]string `validate:"gt=0,dive,required"`
233+
}
234+
235+
test := &Test{
236+
Errs: map[int]string{0: "ok", 1: "", 4: "ok"},
237+
}
238+
239+
errs := validate.Struct(test)
240+
241+
fmt.Println(errs)
230242
}
231243

232244
func TestArrayDiveValidation(t *testing.T) {
@@ -509,7 +521,7 @@ func TestArrayDiveValidation(t *testing.T) {
509521
Equal(t, innerSliceError1.IsPlaceholderErr, false)
510522
Equal(t, innerSliceError1.IsSliceOrArray, false)
511523
Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0)
512-
Equal(t, innerSliceError1.Field, "Errs[2]")
524+
Equal(t, innerSliceError1.Field, "Errs[2][1]")
513525
Equal(t, innerSliceError1.Tag, required)
514526

515527
type TestMultiDimensionalTimeTime2 struct {
@@ -551,7 +563,7 @@ func TestArrayDiveValidation(t *testing.T) {
551563
Equal(t, innerSliceError1.IsPlaceholderErr, false)
552564
Equal(t, innerSliceError1.IsSliceOrArray, false)
553565
Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0)
554-
Equal(t, innerSliceError1.Field, "Errs[2]")
566+
Equal(t, innerSliceError1.Field, "Errs[2][1]")
555567
Equal(t, innerSliceError1.Tag, required)
556568
}
557569

0 commit comments

Comments
 (0)