continuing perf optimizations

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

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

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

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

@ -237,7 +237,7 @@ func StructValidationTestStructSuccess(sl StructLevel) {
st := sl.Current().Interface().(TestStruct) st := sl.Current().Interface().(TestStruct)
if st.String != "good value" { 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) st := sl.Current().Interface().(TestStruct)
if st.String != "bad value" { 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) st := sl.Current().Interface().(TestStruct)
if st.String != "bad value" { 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) st := sl.Current().Interface().(TestStruct)
if st.String != "bad value" { 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) s := sl.Current().Interface().(StructLevelInvalidErr)
if top.Value == s.Value { 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||len=13") }, "Invalid validation tag on field ''")
PanicMatches(t, func() { validate.Var(s, "rgb|rgbaa|len=13") }, "Undefined validation function 'rgbaa' 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) { func TestHsla(t *testing.T) {
@ -6364,3 +6389,27 @@ func TestPointerAndOmitEmpty(t *testing.T) {
errs = validate.Struct(ti3) errs = validate.Struct(ti3)
Equal(t, errs, nil) 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