From b43b1a1aab1b2565db7438faa374022dd80e466f Mon Sep 17 00:00:00 2001 From: Gustavo Date: Thu, 13 Feb 2025 00:16:08 -0300 Subject: [PATCH 1/2] feat: add option early return in case of error --- options.go | 7 +++++ validator.go | 4 ++- validator_instance.go | 1 + validator_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/options.go b/options.go index 86a0db218..7399471ec 100644 --- a/options.go +++ b/options.go @@ -24,3 +24,10 @@ func WithPrivateFieldValidation() Option { v.privateFieldValidation = true } } + +// WithEarlyExit +func WithEarlyExit() Option { + return func(v *Validate) { + v.earlyExit = true + } +} diff --git a/validator.go b/validator.go index 901e7b50a..c9b27fb84 100644 --- a/validator.go +++ b/validator.go @@ -54,7 +54,9 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur var f *cField for i := 0; i < len(cs.fields); i++ { - + if v.v.earlyExit && len(v.errs) > 0 { + return + } f = cs.fields[i] if v.isPartial { diff --git a/validator_instance.go b/validator_instance.go index d9f148dba..f548a7f6e 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -95,6 +95,7 @@ type Validate struct { hasTagNameFunc bool requiredStructEnabled bool privateFieldValidation bool + earlyExit bool } // New returns a new instance of 'validate' with sane defaults. diff --git a/validator_test.go b/validator_test.go index 5eadb2502..ca7625551 100644 --- a/validator_test.go +++ b/validator_test.go @@ -12080,7 +12080,7 @@ func TestExcludedIf(t *testing.T) { test11 := struct { Field1 bool - Field2 *string `validate:"excluded_if=Field1 false"` + Field2 *string `validate:"excluded_if=Field1 false"` }{ Field1: false, Field2: nil, @@ -14123,3 +14123,72 @@ func TestPrivateFieldsStruct(t *testing.T) { Equal(t, len(errs), tc.errorNum) } } + +func TestEarlyExitStruct(t *testing.T) { + type tc struct { + stct interface{} + errorNum int + } + + tcs := []tc{ + { + stct: &struct { + f1 int8 `validate:"gt=0"` + f2 int16 `validate:"gt=0"` + f3 int32 `validate:"gt=0"` + f4 int64 `validate:"gt=0"` + }{}, + errorNum: 1, + }, + { + stct: &struct { + f1 string `validate:"min=3"` + f2 string `validate:"min=3"` + }{}, + errorNum: 1, + }, + { + stct: &struct { + f1 struct { + f2 string `validate:"min=3"` + } + f3 int8 `validate:"gt=3"` + }{}, + errorNum: 1, + }, + { + stct: &struct { + f1 string `validate:"min=3"` + f2 struct { + f3 string `validate:"min=3"` + } + }{}, + errorNum: 1, + }, + { + stct: &struct { + f1 *int `validate:"gt=0"` + f2 struct { + f3 string `validate:"min=3"` + } + }{}, + errorNum: 1, + }, + { + stct: &struct { + f1 []string `validate:"required,dive"` + f2 []int8 `validate:"required,dive"` + }{}, + errorNum: 1, + }, + } + + validate := New(WithPrivateFieldValidation(), WithEarlyExit()) + + for _, tc := range tcs { + err := validate.Struct(tc.stct) + NotEqual(t, err, nil) + errs := err.(ValidationErrors) + Equal(t, len(errs), tc.errorNum) + } +} From 82b92900962c3b4f726f6cacb720b3c13656b1ea Mon Sep 17 00:00:00 2001 From: Gustavo Date: Thu, 13 Feb 2025 00:28:24 -0300 Subject: [PATCH 2/2] feat: add description in withearlyexit option --- options.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/options.go b/options.go index 7399471ec..aff31e51f 100644 --- a/options.go +++ b/options.go @@ -25,7 +25,10 @@ func WithPrivateFieldValidation() Option { } } -// WithEarlyExit +// WithEarlyExit configures the validator to immediately stop validation as soon as the first error is encountered +// +// This feature could be an opt-in behavior, allowing to opt into "early exit" validation, without breaking current workflows +// Early exit on the first failure would save time by avoiding unnecessary checks on the remaining fields func WithEarlyExit() Option { return func(v *Validate) { v.earlyExit = true