mirror of
https://github.com/Fishwaldo/validator.git
synced 2025-03-15 19:51:38 +00:00
parent
d627af88ac
commit
7d55bfddde
2 changed files with 209 additions and 20 deletions
158
validator.go
158
validator.go
|
@ -20,20 +20,19 @@ 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"
|
||||
arrayIndexFieldName = "%s[%d]"
|
||||
mapIndexFieldName = "%s[%v]"
|
||||
)
|
||||
|
@ -193,6 +192,112 @@ func (e *FieldError) Error() string {
|
|||
return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag)
|
||||
}
|
||||
|
||||
// func (e *FieldError) flatten(isFromStruct bool) map[string]*FieldError {
|
||||
|
||||
// errs := map[string]*FieldError{}
|
||||
|
||||
// if e.IsPlaceholderErr {
|
||||
|
||||
// if e.IsSliceOrArray {
|
||||
// for key, err := range e.SliceOrArrayErrs {
|
||||
|
||||
// fe, ok := err.(*FieldError)
|
||||
|
||||
// if ok {
|
||||
|
||||
// if flat := fe.flatten(isFromStruct); flat != nil && len(flat) > 0 {
|
||||
// for k, v := range flat {
|
||||
// errs[fmt.Sprintf("[%#v]%s", key, k)] = v
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
|
||||
// se := err.(*StructErrors)
|
||||
|
||||
// if flat := se.flatten(isFromStruct); flat != nil && len(flat) > 0 {
|
||||
// for k, v := range flat {
|
||||
// errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// if e.IsMap {
|
||||
// // for _, err := range e.MapErrs {
|
||||
|
||||
// // if flat := err.Flatten(); flat != nil && len(flat) > 0 {
|
||||
// // for k, v := range flat {
|
||||
// // errs[k] = v
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
|
||||
// return errs
|
||||
// }
|
||||
|
||||
// errs[e.Field] = e
|
||||
|
||||
// return errs
|
||||
// }
|
||||
|
||||
// Flatten flattens the FieldError hierarchical structure into a flat namespace style field name
|
||||
// for those that want/need it.
|
||||
// This is now needed because of the new dive functionality
|
||||
func (e *FieldError) Flatten() map[string]*FieldError {
|
||||
|
||||
// return e.flatten(false)
|
||||
errs := map[string]*FieldError{}
|
||||
|
||||
if e.IsPlaceholderErr {
|
||||
|
||||
if e.IsSliceOrArray {
|
||||
for key, err := range e.SliceOrArrayErrs {
|
||||
|
||||
fe, ok := err.(*FieldError)
|
||||
|
||||
if ok {
|
||||
|
||||
if flat := fe.Flatten(); flat != nil && len(flat) > 0 {
|
||||
for k, v := range flat {
|
||||
errs[fmt.Sprintf("[%#v]%s", key, k)] = v
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
se := err.(*StructErrors)
|
||||
|
||||
if flat := se.flatten(false); flat != nil && len(flat) > 0 {
|
||||
for k, v := range flat {
|
||||
errs[fmt.Sprintf("[%#v].%s.%s", key, se.Struct, k)] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if e.IsMap {
|
||||
// for _, err := range e.MapErrs {
|
||||
|
||||
// if flat := err.Flatten(); flat != nil && len(flat) > 0 {
|
||||
// for k, v := range flat {
|
||||
// errs[k] = v
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
errs[e.Field] = e
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// StructErrors is hierarchical list of field and struct validation errors
|
||||
// for a non hierarchical representation please see the Flatten method for StructErrors
|
||||
type StructErrors struct {
|
||||
|
@ -222,10 +327,7 @@ func (e *StructErrors) Error() string {
|
|||
return strings.TrimSpace(buff.String())
|
||||
}
|
||||
|
||||
// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name
|
||||
// for those that want/need it
|
||||
func (e *StructErrors) Flatten() map[string]*FieldError {
|
||||
|
||||
func (e *StructErrors) flatten(isFromStruct bool) map[string]*FieldError {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -234,12 +336,22 @@ func (e *StructErrors) Flatten() map[string]*FieldError {
|
|||
|
||||
for _, f := range e.Errors {
|
||||
|
||||
errs[f.Field] = f
|
||||
if flat := f.Flatten(); flat != nil && len(flat) > 0 {
|
||||
|
||||
for k, fe := range flat {
|
||||
|
||||
if isFromStruct && f.Field[0:1] == "[" {
|
||||
errs[f.Field+k] = fe
|
||||
} else {
|
||||
errs[k] = fe
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, val := range e.StructErrors {
|
||||
|
||||
otherErrs := val.Flatten()
|
||||
otherErrs := val.flatten(isFromStruct)
|
||||
|
||||
for _, f2 := range otherErrs {
|
||||
|
||||
|
@ -251,6 +363,12 @@ func (e *StructErrors) Flatten() map[string]*FieldError {
|
|||
return errs
|
||||
}
|
||||
|
||||
// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name
|
||||
// for those that want/need it
|
||||
func (e *StructErrors) Flatten() map[string]*FieldError {
|
||||
return e.flatten(true)
|
||||
}
|
||||
|
||||
// Func accepts all values needed for file and cross field validation
|
||||
// top = top level struct when validating by struct otherwise nil
|
||||
// current = current level struct when validating by struct otherwise optional comparison value
|
||||
|
|
|
@ -226,6 +226,51 @@ func AssertMapFieldError(t *testing.T, s map[string]*FieldError, field string, e
|
|||
EqualSkip(t, 2, val.Tag, expectedTag)
|
||||
}
|
||||
|
||||
func TestFlattenValidation(t *testing.T) {
|
||||
|
||||
type Inner struct {
|
||||
Name string `validate:"required"`
|
||||
}
|
||||
|
||||
type TestMultiDimensionalStructsPtr struct {
|
||||
Errs [][]*Inner `validate:"gt=0,dive,dive"`
|
||||
}
|
||||
|
||||
var errStructPtrArray [][]*Inner
|
||||
|
||||
errStructPtrArray = append(errStructPtrArray, []*Inner{&Inner{"ok"}, &Inner{""}, &Inner{"ok"}})
|
||||
|
||||
tmsp := &TestMultiDimensionalStructsPtr{
|
||||
Errs: errStructPtrArray,
|
||||
}
|
||||
|
||||
errs := validate.Struct(tmsp)
|
||||
NotEqual(t, errs, nil)
|
||||
Equal(t, len(errs.Errors), 1)
|
||||
// for full test coverage
|
||||
fmt.Sprint(errs.Error())
|
||||
|
||||
fieldErr := errs.Errors["Errs"]
|
||||
Equal(t, fieldErr.IsPlaceholderErr, true)
|
||||
Equal(t, fieldErr.IsSliceOrArray, true)
|
||||
Equal(t, fieldErr.Field, "Errs")
|
||||
Equal(t, len(fieldErr.SliceOrArrayErrs), 1)
|
||||
|
||||
innerSlice1, ok := fieldErr.SliceOrArrayErrs[0].(*FieldError)
|
||||
Equal(t, ok, true)
|
||||
Equal(t, innerSlice1.IsPlaceholderErr, true)
|
||||
Equal(t, innerSlice1.Field, "Errs[0]")
|
||||
|
||||
flatFieldErr, ok := fieldErr.Flatten()["[0][1].Inner.Name"]
|
||||
Equal(t, ok, true)
|
||||
Equal(t, flatFieldErr.Field, "Name")
|
||||
Equal(t, flatFieldErr.Tag, "required")
|
||||
|
||||
// expect Errs[0][1].Inner.Name = error
|
||||
// fmt.Println((fieldErr.SliceOrArrayErrs[0].(*FieldError)).Field)
|
||||
// fmt.Println((fieldErr.SliceOrArrayErrs[0].(*FieldError)).IsPlaceholderErr)
|
||||
}
|
||||
|
||||
func TestInterfaceErrValidation(t *testing.T) {
|
||||
|
||||
var v1 interface{}
|
||||
|
@ -578,6 +623,11 @@ func TestArrayDiveValidation(t *testing.T) {
|
|||
Equal(t, err.IsSliceOrArray, true)
|
||||
Equal(t, len(err.SliceOrArrayErrs), 1)
|
||||
|
||||
// flat := err.Flatten()
|
||||
// fe, ok := flat["[1]"]
|
||||
// Equal(t, ok, true)
|
||||
// Equal(t, fe.Tag, "required")
|
||||
|
||||
err = validate.Field(arr, "len=2,dive,required")
|
||||
NotEqual(t, err, nil)
|
||||
Equal(t, err.IsPlaceholderErr, false)
|
||||
|
@ -606,6 +656,12 @@ func TestArrayDiveValidation(t *testing.T) {
|
|||
NotEqual(t, errs, nil)
|
||||
Equal(t, len(errs.Errors), 1)
|
||||
|
||||
// flat = errs.Flatten()
|
||||
// me, ok := flat["Errs[1]"]
|
||||
// Equal(t, ok, true)
|
||||
// Equal(t, me.Field, "Errs[1]")
|
||||
// Equal(t, me.Tag, "required")
|
||||
|
||||
fieldErr, ok := errs.Errors["Errs"]
|
||||
Equal(t, ok, true)
|
||||
Equal(t, fieldErr.IsPlaceholderErr, true)
|
||||
|
@ -666,6 +722,7 @@ func TestArrayDiveValidation(t *testing.T) {
|
|||
Equal(t, sliceError1.IsPlaceholderErr, true)
|
||||
Equal(t, sliceError1.IsSliceOrArray, true)
|
||||
Equal(t, len(sliceError1.SliceOrArrayErrs), 2)
|
||||
Equal(t, sliceError1.Field, "Errs[0]")
|
||||
|
||||
innerSliceError1, ok := sliceError1.SliceOrArrayErrs[1].(*FieldError)
|
||||
Equal(t, ok, true)
|
||||
|
@ -673,6 +730,7 @@ func TestArrayDiveValidation(t *testing.T) {
|
|||
Equal(t, innerSliceError1.Tag, required)
|
||||
Equal(t, innerSliceError1.IsSliceOrArray, false)
|
||||
Equal(t, len(innerSliceError1.SliceOrArrayErrs), 0)
|
||||
Equal(t, innerSliceError1.Field, "Errs[0][1]")
|
||||
|
||||
type Inner struct {
|
||||
Name string `validate:"required"`
|
||||
|
@ -736,12 +794,25 @@ func TestArrayDiveValidation(t *testing.T) {
|
|||
// for full test coverage
|
||||
fmt.Sprint(errs.Error())
|
||||
|
||||
// flat := errs.Flatten()
|
||||
// // fmt.Println(errs)
|
||||
// fmt.Println(flat)
|
||||
// expect Errs[0][1].Inner.Name
|
||||
// me, ok := flat["Errs[1]"]
|
||||
// Equal(t, ok, true)
|
||||
// Equal(t, me.Field, "Errs[1]")
|
||||
// Equal(t, me.Tag, "required")
|
||||
|
||||
fieldErr, ok = errs.Errors["Errs"]
|
||||
Equal(t, ok, true)
|
||||
Equal(t, fieldErr.IsPlaceholderErr, true)
|
||||
Equal(t, fieldErr.IsSliceOrArray, true)
|
||||
Equal(t, len(fieldErr.SliceOrArrayErrs), 3)
|
||||
|
||||
// flat := fieldErr.Flatten()
|
||||
// fmt.Println(errs)
|
||||
// fmt.Println(flat)
|
||||
|
||||
sliceError1, ok = fieldErr.SliceOrArrayErrs[0].(*FieldError)
|
||||
Equal(t, ok, true)
|
||||
Equal(t, sliceError1.IsPlaceholderErr, true)
|
||||
|
|
Loading…
Add table
Reference in a new issue