diff --git a/benchmarks_test.go b/benchmarks_test.go index eb52745..0858548 100644 --- a/benchmarks_test.go +++ b/benchmarks_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", } diff --git a/struct_level.go b/struct_level.go index 4e294e7..bef03b9 100644 --- a/struct_level.go +++ b/struct_level.go @@ -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(), }, ) } diff --git a/validator.go b/validator.go index ba4a8b6..f1cde17 100644 --- a/validator.go +++ b/validator.go @@ -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(), diff --git a/validator_test.go b/validator_test.go index b6fb08c..07fce3f 100644 --- a/validator_test.go +++ b/validator_test.go @@ -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") +}