Skip to content

Commit

Permalink
feat(conditional): adding rules for "else" case (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
samber authored Apr 26, 2020
1 parent c098b9a commit a80f7c5
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 24 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,23 +416,23 @@ fmt.Println(err)
Sometimes, we may want to validate a value only when certain condition is met. For example, we want to ensure the
`unit` struct field is not empty only when the `quantity` field is not empty; or we may want to ensure either `email`
or `phone` is provided. The so-called conditional validation can be achieved with the help of `validation.When`.
The following code implements the aforementioned two examples:
The following code implements the aforementioned examples:

```go
result := validation.ValidateStruct(&a,
validation.Field(&a.Unit, validation.When(a.Quantity != "", validation.Required)),
validation.Field(&a.Unit, validation.When(a.Quantity != "", validation.Required).Else(validation.Nil)),
validation.Field(&a.Phone, validation.When(a.Email == "", validation.Required.Error('Either phone or Email is required.')),
validation.Field(&a.Email, validation.When(a.Phone == "", validation.Required.Error('Either phone or Email is required.')),
validation.Field(&a.Email, validation.When(a.Phone == "", validation.Required.Error('Either phone or Email is required.')),
)
```

Note that `validation.When` can take a list of validation rules. These rules will be executed only when the condition is true.
Note that `validation.When` and `validation.When.Else` can take a list of validation rules. These rules will be executed only when the condition is true (When) or false (Else).

The above code can also be simplified using the shortcut `validation.Required.When`:

```go
result := validation.ValidateStruct(&a,
validation.Field(&a.Unit, validation.Required.When(a.Quantity != "")),
validation.Field(&a.Unit, validation.Required.When(a.Quantity != ""), validation.Nil.When(a.Quantity == "")),
validation.Field(&a.Phone, validation.Required.When(a.Email == "").Error('Either phone or Email is required.')),
validation.Field(&a.Email, validation.Required.When(a.Phone == "").Error('Either phone or Email is required.')),
)
Expand Down Expand Up @@ -463,6 +463,7 @@ The following rules are provided in the `validation` package:
* `MultipleOf`: checks if the value is a multiple of the specified range.
* `Each(rules ...Rule)`: checks the elements within an iterable (map/slice/array) with other rules.
* `When(condition, rules ...Rule)`: validates with the specified rules only when the condition is true.
* `Else(rules ...Rule)`: must be used with `When(condition, rules ...Rule)`, validates with the specified rules only when the condition is false.

The `is` sub-package provides a list of commonly used string validation rules that can be used to check if the format
of a value satisfies certain requirements. Note that these rules only handle strings and byte slices and if a string
Expand Down
10 changes: 9 additions & 1 deletion when.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ func When(condition bool, rules ...Rule) WhenRule {
return WhenRule{
condition: condition,
rules: rules,
elseRules: []Rule{},
}
}

// WhenRule is a validation rule that executes the given list of rules when the condition is true.
type WhenRule struct {
condition bool
rules []Rule
elseRules []Rule
}

// Validate checks if the condition is true and if so, it validates the value using the specified rules.
Expand All @@ -20,5 +22,11 @@ func (r WhenRule) Validate(value interface{}) error {
return Validate(value, r.rules...)
}

return nil
return Validate(value, r.elseRules...)
}

// When returns a validation rule that executes the given list of rules when the condition is false.
func (r WhenRule) Else(rules ...Rule) WhenRule {
r.elseRules = rules
return r
}
39 changes: 21 additions & 18 deletions when_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,35 @@ func TestWhen(t *testing.T) {
condition bool
value interface{}
rules []Rule
elseRules []Rule
err string
}{
// True condition
{"t1.1", true, nil, []Rule{}, ""},
{"t1.2", true, "", []Rule{}, ""},
{"t1.3", true, "", []Rule{abcRule}, ""},
{"t1.4", true, 12, []Rule{Required}, ""},
{"t1.5", true, nil, []Rule{Required}, "cannot be blank"},
{"t1.6", true, "123", []Rule{abcRule}, "wrong_abc"},
{"t1.7", true, "abc", []Rule{abcRule}, ""},
{"t1.8", true, "abc", []Rule{abcRule, abcRule}, ""},
{"t1.9", true, "abc", []Rule{abcRule, validateMeRule}, "wrong_me"},
{"t1.10", true, "me", []Rule{abcRule, validateMeRule}, "wrong_abc"},
{"t1.1", true, nil, []Rule{}, []Rule{}, ""},
{"t1.2", true, "", []Rule{}, []Rule{}, ""},
{"t1.3", true, "", []Rule{abcRule}, []Rule{}, ""},
{"t1.4", true, 12, []Rule{Required}, []Rule{}, ""},
{"t1.5", true, nil, []Rule{Required}, []Rule{}, "cannot be blank"},
{"t1.6", true, "123", []Rule{abcRule}, []Rule{}, "wrong_abc"},
{"t1.7", true, "abc", []Rule{abcRule}, []Rule{}, ""},
{"t1.8", true, "abc", []Rule{abcRule, abcRule}, []Rule{}, ""},
{"t1.9", true, "abc", []Rule{abcRule, validateMeRule}, []Rule{}, "wrong_me"},
{"t1.10", true, "me", []Rule{abcRule, validateMeRule}, []Rule{}, "wrong_abc"},
{"t1.11", true, "me", []Rule{}, []Rule{abcRule}, ""},

// False condition
{"t2.1", false, "", []Rule{}, ""},
{"t2.2", false, "", []Rule{abcRule}, ""},
{"t2.3", false, "abc", []Rule{abcRule}, ""},
{"t2.4", false, "abc", []Rule{abcRule, abcRule}, ""},
{"t2.5", false, "abc", []Rule{abcRule, validateMeRule}, ""},
{"t2.6", false, "me", []Rule{abcRule, validateMeRule}, ""},
{"t2.7", false, "", []Rule{abcRule, validateMeRule}, ""},
{"t2.1", false, "", []Rule{}, []Rule{}, ""},
{"t2.2", false, "", []Rule{abcRule}, []Rule{}, ""},
{"t2.3", false, "abc", []Rule{abcRule}, []Rule{}, ""},
{"t2.4", false, "abc", []Rule{abcRule, abcRule}, []Rule{}, ""},
{"t2.5", false, "abc", []Rule{abcRule, validateMeRule}, []Rule{}, ""},
{"t2.6", false, "me", []Rule{abcRule, validateMeRule}, []Rule{}, ""},
{"t2.7", false, "", []Rule{abcRule, validateMeRule}, []Rule{}, ""},
{"t2.8", false, "me", []Rule{}, []Rule{abcRule, validateMeRule}, "wrong_abc"},
}

for _, test := range tests {
err := Validate(test.value, When(test.condition, test.rules...))
err := Validate(test.value, When(test.condition, test.rules...).Else(test.elseRules...))
assertError(t, test.err, err, test.tag)
}
}

0 comments on commit a80f7c5

Please sign in to comment.