correct map references pointing to slice after copy/paste

for#78
pull/82/head
joeybloggs 9 years ago
parent 14f176e8ac
commit 8bf793acde
  1. 60
      validator.go
  2. 67
      validator_test.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,
}
}

@ -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) {

Loading…
Cancel
Save