diff --git a/.gitignore b/.gitignore index 6305e5290..c954d1744 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ _testmain.go cover.html README.html .idea +.vscode \ No newline at end of file diff --git a/errors.go b/errors.go index be2676e9e..c2a36a518 100644 --- a/errors.go +++ b/errors.go @@ -157,8 +157,16 @@ type FieldError interface { Error() string } +type TopFieldError interface { + FieldError + + // Top returns the actual field's parent value in case needed for creating custom the error + Top() reflect.Value +} + // compile time interface checks var _ FieldError = new(fieldError) +var _ TopFieldError = new(fieldError) var _ error = new(fieldError) // fieldError contains a single field's validation error along @@ -176,6 +184,7 @@ type fieldError struct { param string kind reflect.Kind typ reflect.Type + top reflect.Value } // Tag returns the validation tag that failed. @@ -274,3 +283,9 @@ func (fe *fieldError) Translate(ut ut.Translator) string { return fn(ut, fe) } + +// Top returns the actual field's parent value in case needed for creating custom the error +// message +func (fe *fieldError) Top() reflect.Value { + return fe.top +} diff --git a/struct_level.go b/struct_level.go index 271328f71..bb3c3b12c 100644 --- a/struct_level.go +++ b/struct_level.go @@ -135,6 +135,7 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta structfieldLen: uint8(len(structFieldName)), param: param, kind: kind, + top: v.top, }, ) return @@ -153,6 +154,7 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta param: param, kind: kind, typ: fv.Type(), + top: v.top, }, ) } diff --git a/validator.go b/validator.go index 901e7b50a..8f7415204 100644 --- a/validator.go +++ b/validator.go @@ -136,6 +136,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr structfieldLen: uint8(len(cf.name)), param: ct.param, kind: kind, + top: v.top, }, ) return @@ -161,6 +162,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr param: ct.param, kind: kind, typ: current.Type(), + top: v.top, }, ) return @@ -415,6 +417,7 @@ OUTER: param: ct.param, kind: kind, typ: typ, + top: v.top, }, ) @@ -435,6 +438,7 @@ OUTER: param: ct.param, kind: kind, typ: typ, + top: v.top, }, ) } @@ -475,6 +479,7 @@ OUTER: param: ct.param, kind: kind, typ: typ, + top: v.top, }, ) diff --git a/validator_test.go b/validator_test.go index 367a6b419..8cd683e26 100644 --- a/validator_test.go +++ b/validator_test.go @@ -355,7 +355,7 @@ func TestStructLevelInvalidError(t *testing.T) { validate.RegisterStructValidation(StructLevelInvalidError, StructLevelInvalidErr{}) var test StructLevelInvalidErr - + val := reflect.ValueOf(test) err := validate.Struct(test) NotEqual(t, err, nil) @@ -371,6 +371,10 @@ func TestStructLevelInvalidError(t *testing.T) { Equal(t, fe.ActualTag(), "required") Equal(t, fe.Kind(), reflect.Invalid) Equal(t, fe.Type(), reflect.TypeOf(nil)) + top, ok := fe.(TopFieldError) + Equal(t, ok, true) + Equal(t, top.Top().Kind(), val.Kind()) + Equal(t, top.Top().Type().Name(), val.Type().Name()) } func TestNameNamespace(t *testing.T) {