From 8bf793acde6d57df6e685975933c9761632b38fa Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Sat, 27 Jun 2015 08:22:37 -0400 Subject: [PATCH] correct map references pointing to slice after copy/paste for#78 --- validator.go | 60 +++++++++++++++++++----------------------- validator_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 39 deletions(-) diff --git a/validator.go b/validator.go index a77cbb5..b083a4d 100644 --- a/validator.go +++ b/validator.go @@ -20,20 +20,20 @@ import ( ) const ( - utf8HexComma = "0x2C" - tagSeparator = "," - orSeparator = "|" - noValidationTag = "-" - tagKeySeparator = "=" - structOnlyTag = "structonly" - omitempty = "omitempty" - required = "required" - fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag" - sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s" - mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s" - structErrMsg = "Struct:%s\n" - diveTag = "dive" - diveSplit = "," + diveTag + utf8HexComma = "0x2C" + tagSeparator = "," + orSeparator = "|" + noValidationTag = "-" + tagKeySeparator = "=" + structOnlyTag = "structonly" + omitempty = "omitempty" + required = "required" + fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag" + sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s" + mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s" + structErrMsg = "Struct:%s\n" + diveTag = "dive" + // diveSplit = "," + diveTag arrayIndexFieldName = "%s[%d]" mapIndexFieldName = "%s[%v]" ) @@ -457,7 +457,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter case reflect.Slice, reflect.Array: cField.isSliceOrArray = true cField.sliceSubtype = cField.typ.Elem() - cField.isTimeSubtype = cField.sliceSubtype == reflect.TypeOf(time.Time{}) + cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) cField.sliceSubKind = cField.sliceSubtype.Kind() if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { @@ -469,7 +469,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter case reflect.Map: cField.isMap = true cField.mapSubtype = cField.typ.Elem() - cField.isTimeSubtype = cField.mapSubtype == reflect.TypeOf(time.Time{}) + cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) cField.mapSubKind = cField.mapSubtype.Kind() if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { @@ -537,11 +537,13 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f switch cField.kind { case reflect.Slice, reflect.Array: + isSingleField = false // cached tags mean nothing because it will be split up while diving cField.isSliceOrArray = true cField.sliceSubtype = cField.typ.Elem() cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{})) cField.sliceSubKind = cField.sliceSubtype.Kind() case reflect.Map: + isSingleField = false // cached tags mean nothing because it will be split up while diving cField.isMap = true cField.mapSubtype = cField.typ.Elem() cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) @@ -556,7 +558,7 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f case reflect.Struct, reflect.Interface, reflect.Invalid: if cField.typ != reflect.TypeOf(time.Time{}) { - panic("Invalid field passed to ValidateFieldWithTag") + panic("Invalid field passed to fieldWithNameAndValue") } } @@ -568,22 +570,12 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f if !isCached { - for k, t := range strings.Split(tag, tagSeparator) { + for _, t := range strings.Split(tag, tagSeparator) { if t == diveTag { cField.dive = true - - if k == 0 { - if len(tag) == 4 { - cField.diveTag = "" - } else { - cField.diveTag = tag[5:] - } - } else { - cField.diveTag = strings.SplitN(tag, diveSplit, 2)[1][1:] - } - + cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") break } @@ -700,12 +692,14 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField idxField := valueField.MapIndex(key) - if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() { + if cField.mapSubKind == reflect.Ptr && !idxField.IsNil() { idxField = idxField.Elem() - cField.sliceSubKind = idxField.Kind() + cField.mapSubKind = idxField.Kind() } - switch cField.sliceSubKind { + // fmt.Println(cField.sliceSubKind) + + switch cField.mapSubKind { case reflect.Struct, reflect.Interface: if cField.isTimeSubtype { @@ -730,7 +724,7 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField Tag: required, Value: idxField.Interface(), Kind: reflect.Ptr, - Type: cField.sliceSubtype, + Type: cField.mapSubtype, } } diff --git a/validator_test.go b/validator_test.go index 207c948..90ee5ee 100644 --- a/validator_test.go +++ b/validator_test.go @@ -228,21 +228,76 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e func TestMapDiveValidation(t *testing.T) { - type Test struct { - Errs map[int]string `validate:"gt=0,dive,required"` + m := map[int]string{0: "ok", 3: "", 4: "ok"} + + err := validate.Field(m, "len=3,dive,required") + NotEqual(t, err, nil) + Equal(t, err.IsPlaceholderErr, true) + Equal(t, err.IsMap, true) + Equal(t, len(err.MapErrs), 1) + + err = validate.Field(m, "len=2,dive,required") + NotEqual(t, err, nil) + Equal(t, err.IsPlaceholderErr, false) + Equal(t, err.IsMap, false) + Equal(t, len(err.MapErrs), 0) + + type Inner struct { + Name string `validate:"required"` } - test := &Test{ - Errs: map[int]string{0: "ok", 1: "", 4: "ok"}, + type TestMapStruct struct { + Errs map[int]Inner `validate:"gt=0,dive"` } - errs := validate.Struct(test) + mi := map[int]Inner{0: Inner{"ok"}, 3: Inner{""}, 4: Inner{"ok"}} + + ms := &TestMapStruct{ + Errs: mi, + } + + errs := validate.Struct(ms) fmt.Println(errs) + + // type Test struct { + // Errs map[int]string `validate:"gt=0,dive,required"` + // } + + // test := &Test{ + // Errs: map[int]string{0: "ok", 1: "", 4: "ok"}, + // } + + // errs := validate.Struct(test) + // NotEqual(t, errs, nil) } func TestArrayDiveValidation(t *testing.T) { + arr := []string{"ok", "", "ok"} + + err := validate.Field(arr, "len=3,dive,required") + NotEqual(t, err, nil) + Equal(t, err.IsPlaceholderErr, true) + Equal(t, err.IsSliceOrArray, true) + Equal(t, len(err.SliceOrArrayErrs), 1) + + err = validate.Field(arr, "len=2,dive,required") + NotEqual(t, err, nil) + Equal(t, err.IsPlaceholderErr, false) + Equal(t, err.IsSliceOrArray, false) + Equal(t, len(err.SliceOrArrayErrs), 0) + + type BadDive struct { + Name string `validate:"dive"` + } + + bd := &BadDive{ + Name: "TEST", + } + + PanicMatches(t, func() { validate.Struct(bd) }, "dive error! can't dive on a non slice or map") + type Test struct { Errs []string `validate:"gt=0,dive,required"` } @@ -3204,7 +3259,7 @@ func TestInvalidField(t *testing.T) { Test: "1", } - PanicMatches(t, func() { validate.Field(s, "required") }, "Invalid field passed to ValidateFieldWithTag") + PanicMatches(t, func() { validate.Field(s, "required") }, "Invalid field passed to fieldWithNameAndValue") } func TestInvalidTagField(t *testing.T) {