Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions baked_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,15 @@ func requiredIf(fl FieldLevel) bool {
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName()))
}

seen := make(map[string]struct{})
for i := 0; i < len(params); i += 2 {
if _, ok := seen[params[i]]; ok {
panic(fmt.Sprintf("Duplicate param %s for required_if %s", params[i], fl.FieldName()))
}
seen[params[i]] = struct{}{}
}

for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return true
Expand Down
1 change: 1 addition & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ The field under validation must be present and not empty only if all
the other specified fields are equal to the value following the specified
field. For strings ensures value is not "". For slices, maps, pointers,
interfaces, channels and functions ensures the value is not nil. For structs ensures value is not the zero value.
Using the same field name multiple times in the parameters will result in a panic at runtime.

Usage: required_if

Expand Down
84 changes: 74 additions & 10 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11099,6 +11099,49 @@ func TestRequiredIf(t *testing.T) {
_ = validate.Struct(test3)
}

func TestRequiredIfDuplicateParams(t *testing.T) {
validate := New()

PanicMatches(t, func() {
type TestStruct struct {
Field1 string `validate:"required_if=Field2 value1 Field2 value2"`
Field2 string
}
test := TestStruct{
Field1: "",
Field2: "value1",
}
_ = validate.Struct(test)
}, "Duplicate param Field2 for required_if Field1")

PanicMatches(t, func() {
type TestStruct struct {
Field1 string `validate:"required_if=Field2 val1 Field3 val2 Field2 val3"`
Field2 string
Field3 string
}
test := TestStruct{
Field1: "",
Field2: "val1",
Field3: "val2",
}
_ = validate.Struct(test)
}, "Duplicate param Field2 for required_if Field1")

type TestStruct struct {
Field1 string `validate:"required_if=Field2 val1 Field3 val2"`
Field2 string
Field3 string
}
test := TestStruct{
Field1: "",
Field2: "val1",
Field3: "val2",
}
errs := validate.Struct(test)
NotEqual(t, errs, nil)
}

func TestRequiredUnless(t *testing.T) {
type Inner struct {
Field *string
Expand Down Expand Up @@ -11169,19 +11212,40 @@ func TestRequiredUnless(t *testing.T) {
AssertError(t, errs, "Field10", "Field10", "Field10", "Field10", "required_unless")
AssertError(t, errs, "Field11", "Field11", "Field11", "Field11", "required_unless")

defer func() {
if r := recover(); r == nil {
t.Errorf("test3 should have panicked!")
PanicMatches(t, func() {
test3 := struct {
Inner *Inner
Field1 string `validate:"required_unless=Inner.Field" json:"field_1"`
}{
Inner: &Inner{Field: &fieldVal},
}
}()
_ = validate.Struct(test3)
}, "Bad param number for required_unless Field1")

test3 := struct {
Inner *Inner
Field1 string `validate:"required_unless=Inner.Field" json:"field_1"`
}{
Inner: &Inner{Field: &fieldVal},
type DuplicateStruct struct {
Field1 string `validate:"required_unless=Field2 value1 Field2 value2"`
Field2 string
}
_ = validate.Struct(test3)
test4 := DuplicateStruct{
Field1: "",
Field2: "value1",
}
errs = validate.Struct(test4)
Equal(t, errs, nil)

test5 := DuplicateStruct{
Field1: "",
Field2: "value2",
}
errs = validate.Struct(test5)
Equal(t, errs, nil)

test6 := DuplicateStruct{
Field1: "",
Field2: "value3",
}
errs = validate.Struct(test6)
NotEqual(t, errs, nil)
}

func TestSkipUnless(t *testing.T) {
Expand Down
Loading