initial validation logic reworked

initial function layouts and validation completed, still need to rework
all of the baked in functions, add map and array traversal and add back original
test cases.

NOTE: a far more pragmatic validation tests will be added, but not until it has
been proven stable with at least the old tests.
pull/114/head
joeybloggs 9 years ago
parent 953cc993e6
commit 7af3fb7c1f
  1. 1186
      baked_in.go
  2. 154
      validator.go
  3. 15
      validator_test.go

File diff suppressed because it is too large Load Diff

@ -18,12 +18,14 @@ import (
const ( const (
utf8HexComma = "0x2C" utf8HexComma = "0x2C"
utf8Pipe = "0x7C"
tagSeparator = "," tagSeparator = ","
orSeparator = "|" orSeparator = "|"
tagKeySeparator = "=" tagKeySeparator = "="
structOnlyTag = "structonly" structOnlyTag = "structonly"
omitempty = "omitempty" omitempty = "omitempty"
skipValidationTag = "-" skipValidationTag = "-"
diveTag = "dive"
fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag" fieldErrMsg = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag"
invaldField = "Invalid field passed to traverseField" invaldField = "Invalid field passed to traverseField"
) )
@ -49,11 +51,11 @@ type Config struct {
} }
// Func accepts all values needed for file and cross field validation // Func accepts all values needed for file and cross field validation
// top = top level struct when validating by struct otherwise nil // topStruct = top level struct when validating by struct otherwise nil
// current = current level struct when validating by struct otherwise optional comparison value // currentStruct = current level struct when validating by struct otherwise optional comparison value
// f = field value for validation // field = field value for validation
// param = parameter used in validation i.e. gt=0 param would be 0 // param = parameter used in validation i.e. gt=0 param would be 0
type Func func(top interface{}, current interface{}, f interface{}, param string) bool type Func func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool
// ValidationErrors is a type of map[string]*FieldError // ValidationErrors is a type of map[string]*FieldError
// it exists to allow for multiple errors passed from this library // it exists to allow for multiple errors passed from this library
@ -131,12 +133,15 @@ func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflec
errPrefix += typ.Name() + "." errPrefix += typ.Name() + "."
numFields := current.NumField() numFields := current.NumField()
var fld reflect.StructField
for i := 0; i < numFields; i++ { for i := 0; i < numFields; i++ {
v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, typ.Field(i).Tag.Get(v.config.TagName)) fld = typ.Field(i)
v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.config.TagName), fld.Name)
} }
} }
func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string) { func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) {
if tag == skipValidationTag { if tag == skipValidationTag {
return return
@ -171,8 +176,8 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
param = vals[1] param = vals[1]
} }
errs[errPrefix+typ.Name()] = &FieldError{ errs[errPrefix+name] = &FieldError{
Field: typ.Name(), Field: name,
Tag: vals[0], Tag: vals[0],
Param: param, Param: param,
Value: current.Interface(), Value: current.Interface(),
@ -200,6 +205,9 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
kind = current.Kind() kind = current.Kind()
} }
// changed current, so have to get inner type again
typ = current.Type()
if kind != reflect.Struct { if kind != reflect.Struct {
goto FALLTHROUGH goto FALLTHROUGH
} }
@ -229,34 +237,122 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
} }
} }
// for _, t := range strings.Split(tag, tagSeparator) { var dive bool
// var diveSubTag string
for _, t := range strings.Split(tag, tagSeparator) {
if t == diveTag {
// if t == diveTag { dive = true
// diveSubTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",")
break
}
// no use in checking tags if it's empty and is ok to be
// omitempty needs to be the first tag if you wish to use it
if t == omitempty && !hasValue(topStruct, currentStruct, current, typ, kind, "") {
return
}
// cField.dive = true // if strings.Contains(tag, omitempty) && !hasValue(topStruct, currentStruct, current, "") {
// cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",") // return
// break
// } // }
// orVals := strings.Split(t, orSeparator) var key string
// cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} var param string
// cField.tags = append(cField.tags, cTag)
// for i, val := range orVals { // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
// vals := strings.SplitN(val, tagKeySeparator, 2) if strings.Index(t, orSeparator) == -1 {
vals := strings.SplitN(t, tagKeySeparator, 2)
key = vals[0]
// key := strings.TrimSpace(vals[0]) if len(key) == 0 {
panic(fmt.Sprintf("Invalid validation tag on field %s", name))
}
// if len(key) == 0 { if len(vals) > 1 {
// panic(fmt.Sprintf("Invalid validation tag on field %s", name)) param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
// } }
} else {
key = t
}
// param := "" if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, key, param, name) {
// if len(vals) > 1 { return
// param = strings.Replace(vals[1], utf8HexComma, ",", -1) }
// } }
// cTag.keyVals[i] = []string{key, param} if dive {
// } // traverse slice or map here
// } // or panic ;)
}
}
// validateField validates a field based on the provided key tag and param and return true if there is an error false if all ok
func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool {
// check if key is orVals, it could be!
orVals := strings.Split(key, orSeparator)
if len(orVals) > 1 {
var errTag string
for _, val := range orVals {
vals := strings.SplitN(val, tagKeySeparator, 2)
if len(vals[0]) == 0 {
panic(fmt.Sprintf("Invalid validation tag on field %s", name))
}
param := ""
if len(vals) > 1 {
param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
}
// validate and keep track!
valFunc, ok := v.config.ValidationFuncs[vals[0]]
if !ok {
panic(fmt.Sprintf("Undefined validation function on field %s", name))
}
if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) {
return false
}
errTag += orSeparator + vals[0]
}
errs[errPrefix+name] = &FieldError{
Field: name,
Tag: errTag[1:],
Value: current.Interface(),
Param: param,
Type: currentType,
Kind: currentKind,
}
return true
}
valFunc, ok := v.config.ValidationFuncs[key]
if !ok {
panic(fmt.Sprintf("Undefined validation function on field %s", name))
}
if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) {
return false
}
errs[errPrefix+name] = &FieldError{
Field: name,
Tag: key,
Value: current.Interface(),
Param: param,
Type: currentType,
Kind: currentKind,
}
return true
} }

@ -209,16 +209,17 @@ func TestValidation(t *testing.T) {
} }
type Test struct { type Test struct {
// Name string `validate:"required"` Name string `validate:"required"`
Inner *Inner `validate:"required"` Arr []string `validate:"required"`
// Inner *Inner `validate:"required"`
} }
inner := &Inner{ // inner := &Inner{
Name: "", // Name: "",
} // }
// tst := &Test{Name: "Dean"} tst := &Test{Name: "Dean"}
tst := &Test{Inner: inner} // tst := &Test{Inner: inner}
errs := validate.Struct(tst) errs := validate.Struct(tst)

Loading…
Cancel
Save