correct map references pointing to slice after copy/paste

for#78
pull/82/head
joeybloggs 10 years ago
parent 14f176e8ac
commit 8bf793acde
  1. 34
      validator.go
  2. 67
      validator_test.go

@ -33,7 +33,7 @@ const (
mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s" mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
structErrMsg = "Struct:%s\n" structErrMsg = "Struct:%s\n"
diveTag = "dive" diveTag = "dive"
diveSplit = "," + diveTag // diveSplit = "," + diveTag
arrayIndexFieldName = "%s[%d]" arrayIndexFieldName = "%s[%d]"
mapIndexFieldName = "%s[%v]" mapIndexFieldName = "%s[%v]"
) )
@ -457,7 +457,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
cField.isSliceOrArray = true cField.isSliceOrArray = true
cField.sliceSubtype = cField.typ.Elem() 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() cField.sliceSubKind = cField.sliceSubtype.Kind()
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { 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: case reflect.Map:
cField.isMap = true cField.isMap = true
cField.mapSubtype = cField.typ.Elem() 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() cField.mapSubKind = cField.mapSubtype.Kind()
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil { 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 { switch cField.kind {
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
isSingleField = false // cached tags mean nothing because it will be split up while diving
cField.isSliceOrArray = true cField.isSliceOrArray = true
cField.sliceSubtype = cField.typ.Elem() cField.sliceSubtype = cField.typ.Elem()
cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || 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() cField.sliceSubKind = cField.sliceSubtype.Kind()
case reflect.Map: case reflect.Map:
isSingleField = false // cached tags mean nothing because it will be split up while diving
cField.isMap = true cField.isMap = true
cField.mapSubtype = cField.typ.Elem() cField.mapSubtype = cField.typ.Elem()
cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{})) 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: case reflect.Struct, reflect.Interface, reflect.Invalid:
if cField.typ != reflect.TypeOf(time.Time{}) { 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 { if !isCached {
for k, t := range strings.Split(tag, tagSeparator) { for _, t := range strings.Split(tag, tagSeparator) {
if t == diveTag { if t == diveTag {
cField.dive = true cField.dive = true
cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",")
if k == 0 {
if len(tag) == 4 {
cField.diveTag = ""
} else {
cField.diveTag = tag[5:]
}
} else {
cField.diveTag = strings.SplitN(tag, diveSplit, 2)[1][1:]
}
break break
} }
@ -700,12 +692,14 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
idxField := valueField.MapIndex(key) idxField := valueField.MapIndex(key)
if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() { if cField.mapSubKind == reflect.Ptr && !idxField.IsNil() {
idxField = idxField.Elem() 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: case reflect.Struct, reflect.Interface:
if cField.isTimeSubtype { if cField.isTimeSubtype {
@ -730,7 +724,7 @@ func (v *Validate) traverseMap(val interface{}, current interface{}, valueField
Tag: required, Tag: required,
Value: idxField.Interface(), Value: idxField.Interface(),
Kind: reflect.Ptr, Kind: reflect.Ptr,
Type: cField.sliceSubtype, Type: cField.mapSubtype,
} }
} }

@ -228,21 +228,76 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e
func TestMapDiveValidation(t *testing.T) { func TestMapDiveValidation(t *testing.T) {
type Test struct { m := map[int]string{0: "ok", 3: "", 4: "ok"}
Errs map[int]string `validate:"gt=0,dive,required"`
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{ type TestMapStruct struct {
Errs: map[int]string{0: "ok", 1: "", 4: "ok"}, 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) 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) { 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 { type Test struct {
Errs []string `validate:"gt=0,dive,required"` Errs []string `validate:"gt=0,dive,required"`
} }
@ -3204,7 +3259,7 @@ func TestInvalidField(t *testing.T) {
Test: "1", 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) { func TestInvalidTagField(t *testing.T) {

Loading…
Cancel
Save