continuing perf optimizations

pull/256/head
joeybloggs 8 years ago
parent 27b607d6ad
commit 6765805f88
  1. 4
      benchmarks_test.go
  2. 32
      struct_level.go
  3. 50
      validator.go
  4. 59
      validator_test.go

@ -112,7 +112,7 @@ func BenchmarkStructLevelValidationSuccess(b *testing.B) {
validate := New()
validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{})
tst := &TestStruct{
tst := TestStruct{
String: "good value",
}
@ -127,7 +127,7 @@ func BenchmarkStructLevelValidationFailure(b *testing.B) {
validate := New()
validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{})
tst := &TestStruct{
tst := TestStruct{
String: "good value",
}

@ -40,7 +40,7 @@ type StructLevel interface {
//
// tag can be an existing validation tag or just something you make up
// and process on the flip side it's up to you.
ReportError(field interface{}, fieldName, altName, tag string)
ReportError(field interface{}, fieldName, altName, tag, param string)
// reports an error just by passing ValidationErrors
//
@ -97,7 +97,7 @@ func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind
}
// ReportError reports an error just by passing the field and tag information
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag string) {
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
@ -105,11 +105,13 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
structFieldName = fieldName
}
// v.slNs = append(v.slNs, fieldName...)
// v.slStructNs = append(v.slStructNs, structFieldName...)
v.str1 = string(append(v.ns, fieldName...))
v.ns = append(v.ns, fieldName...)
v.actualNs = append(v.actualNs, structFieldName...)
if v.v.hasTagNameFunc || fieldName != structFieldName {
v.str2 = string(append(v.actualNs, structFieldName...))
} else {
v.str2 = v.str1
}
switch kind {
case reflect.Invalid:
@ -118,12 +120,12 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
&fieldError{
tag: tag,
actualTag: tag,
ns: string(v.ns),
structNs: string(v.actualNs),
ns: v.str1,
structNs: v.str2,
field: fieldName,
structField: structFieldName,
// param: "",
kind: kind,
param: param,
kind: kind,
},
)
@ -133,14 +135,14 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
&fieldError{
tag: tag,
actualTag: tag,
ns: string(v.ns),
structNs: string(v.actualNs),
ns: v.str1,
structNs: v.str2,
field: fieldName,
structField: structFieldName,
value: fv.Interface(),
// param: "",
kind: kind,
typ: fv.Type(),
param: param,
kind: kind,
typ: fv.Type(),
},
)
}

@ -16,13 +16,17 @@ type validate struct {
isPartial bool
hasExcludes bool
includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
misc []byte
// StructLevel & FieldLevel fields
slflParent reflect.Value
slCurrent reflect.Value
flField reflect.Value
flParam string
// misc reusable values
misc []byte
str1 string
str2 string
}
// parent and current will be the same the first run of validateStruct
@ -97,14 +101,22 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
if ct.hasTag {
v.str1 = string(append(ns, cf.altName...))
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
} else {
v.str2 = v.str1
}
if kind == reflect.Invalid {
v.errs = append(v.errs,
&fieldError{
tag: ct.aliasTag,
actualTag: ct.tag,
ns: string(append(ns, cf.altName...)),
structNs: string(append(structNs, cf.name...)),
ns: v.str1,
structNs: v.str2,
field: cf.altName,
structField: cf.name,
param: ct.param,
@ -119,8 +131,8 @@ func (v *validate) traverseField(parent reflect.Value, current reflect.Value, ns
&fieldError{
tag: ct.aliasTag,
actualTag: ct.tag,
ns: string(append(ns, cf.altName...)),
structNs: string(append(structNs, cf.name...)),
ns: v.str1,
structNs: v.str2,
field: cf.altName,
structField: cf.name,
value: current.Interface(),
@ -299,14 +311,22 @@ OUTER:
if ct.next == nil {
// if we get here, no valid 'or' value and no more tags
v.str1 = string(append(ns, cf.altName...))
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
} else {
v.str2 = v.str1
}
if ct.hasAlias {
v.errs = append(v.errs,
&fieldError{
tag: ct.aliasTag,
actualTag: ct.actualAliasTag,
ns: string(append(ns, cf.altName...)),
structNs: string(append(structNs, cf.name...)),
ns: v.str1,
structNs: v.str2,
field: cf.altName,
structField: cf.name,
value: current.Interface(),
@ -324,8 +344,8 @@ OUTER:
&fieldError{
tag: tVal,
actualTag: tVal,
ns: string(append(ns, cf.altName...)),
structNs: string(append(structNs, cf.name...)),
ns: v.str1,
structNs: v.str2,
field: cf.altName,
structField: cf.name,
value: current.Interface(),
@ -351,12 +371,20 @@ OUTER:
if !ct.fn(v) {
v.str1 = string(append(ns, cf.altName...))
if v.v.hasTagNameFunc {
v.str2 = string(append(structNs, cf.name...))
} else {
v.str2 = v.str1
}
v.errs = append(v.errs,
&fieldError{
tag: ct.aliasTag,
actualTag: ct.tag,
ns: string(append(ns, cf.altName...)),
structNs: string(append(structNs, cf.name...)),
ns: v.str1,
structNs: v.str2,
field: cf.altName,
structField: cf.name,
value: current.Interface(),

@ -237,7 +237,7 @@ func StructValidationTestStructSuccess(sl StructLevel) {
st := sl.Current().Interface().(TestStruct)
if st.String != "good value" {
sl.ReportError(st.String, "StringVal", "String", "badvalueteststruct")
sl.ReportError(st.String, "StringVal", "String", "badvalueteststruct", "good value")
}
}
@ -246,7 +246,7 @@ func StructValidationTestStruct(sl StructLevel) {
st := sl.Current().Interface().(TestStruct)
if st.String != "bad value" {
sl.ReportError(st.String, "StringVal", "String", "badvalueteststruct")
sl.ReportError(st.String, "StringVal", "String", "badvalueteststruct", "bad value")
}
}
@ -255,7 +255,7 @@ func StructValidationNoTestStructCustomName(sl StructLevel) {
st := sl.Current().Interface().(TestStruct)
if st.String != "bad value" {
sl.ReportError(st.String, "String", "", "badvalueteststruct")
sl.ReportError(st.String, "String", "", "badvalueteststruct", "bad value")
}
}
@ -264,7 +264,7 @@ func StructValidationTestStructInvalid(sl StructLevel) {
st := sl.Current().Interface().(TestStruct)
if st.String != "bad value" {
sl.ReportError(nil, "StringVal", "String", "badvalueteststruct")
sl.ReportError(nil, "StringVal", "String", "badvalueteststruct", "bad value")
}
}
@ -314,7 +314,7 @@ func StructLevelInvalidError(sl StructLevel) {
s := sl.Current().Interface().(StructLevelInvalidErr)
if top.Value == s.Value {
sl.ReportError(nil, "Value", "Value", "required")
sl.ReportError(nil, "Value", "Value", "required", "")
}
}
@ -5513,6 +5513,31 @@ func TestOrTag(t *testing.T) {
PanicMatches(t, func() { validate.Var(s, "rgb||len=13") }, "Invalid validation tag on field ''")
PanicMatches(t, func() { validate.Var(s, "rgb|rgbaa|len=13") }, "Undefined validation function 'rgbaa' on field ''")
v2 := New()
v2.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
type Colors struct {
Fav string `validate:"rgb|rgba" json:"fc"`
}
c := Colors{Fav: "this ain't right"}
err := v2.Struct(c)
NotEqual(t, err, nil)
errs = err.(ValidationErrors)
fe := getError(errs, "Colors.fc", "Colors.Fav")
NotEqual(t, fe, nil)
}
func TestHsla(t *testing.T) {
@ -6364,3 +6389,27 @@ func TestPointerAndOmitEmpty(t *testing.T) {
errs = validate.Struct(ti3)
Equal(t, errs, nil)
}
func TestRequired(t *testing.T) {
validate := New()
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
type Test struct {
Value interface{} `validate:"required"`
}
var test Test
err := validate.Struct(test)
NotEqual(t, err, nil)
AssertError(t, err.(ValidationErrors), "Test.Value", "Test.Value", "Value", "Value", "required")
}

Loading…
Cancel
Save