Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using generics for NotIn, updating docs #8

Merged
merged 1 commit into from
Jul 29, 2024
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -607,14 +607,14 @@ When performing context-aware validation, if a rule does not implement `validati

The following rules are provided in the `validation` package:

- `In(...interface{})`: checks if a value can be found in the given list of values.
- `NotIn(...interface{})`: checks if a value is NOT among the given list of values.
- `In[T any](values ...T)`: checks if a value can be found in the given list of values.
- `NotIn[T any](values ...T)`: checks if a value is NOT among the given list of values.
- `Length(min, max int)`: checks if the length of a value is within the specified range.
This rule should only be used for validating strings, slices, maps, and arrays.
- `RuneLength(min, max int)`: checks if the length of a string is within the specified range.
This rule is similar as `Length` except that when the value being validated is a string, it checks
its rune length instead of byte length.
- `Min(min interface{})` and `Max(max interface{})`: checks if a value is within the specified range.
- `Min(min any)` and `Max(max any)`: checks if a value is within the specified range.
These two rules should only be used for validating int, uint, float and time.Time types.
- `Match(*regexp.Regexp)`: checks if a value matches the specified regular expression.
This rule should only be used for strings and byte slices.
Expand Down
20 changes: 11 additions & 9 deletions not_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,50 @@

package validation

import "reflect"

// ErrNotInInvalid is the error that returns when a value is in a list.
var ErrNotInInvalid = NewError("validation_not_in_invalid", "must not be in list")

// NotIn returns a validation rule that checks if a value is absent from the given list of values.
// Note that the value being checked and the possible range of values must be of the same type.
// Like with In(), reflect.DeepEqual() will be used to determine if two values are equal.
// An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func NotIn(values ...interface{}) NotInRule {
return NotInRule{
func NotIn[T any](values ...T) NotInRule[T] {
return NotInRule[T]{
elements: values,
err: ErrNotInInvalid,
}
}

// NotInRule is a validation rule that checks if a value is absent from the given list of values.
type NotInRule struct {
elements []interface{}
type NotInRule[T any] struct {
elements []T
err Error
}

// Validate checks if the given value is valid or not.
func (r NotInRule) Validate(value interface{}) error {
func (r NotInRule[T]) Validate(value interface{}) error {
value, isNil := Indirect(value)
if isNil || IsEmpty(value) {
return nil
}

for _, e := range r.elements {
if e == value {
if reflect.DeepEqual(e, value) {
return r.err
}
}
return nil
}

// Error sets the error message for the rule.
func (r NotInRule) Error(message string) NotInRule {
func (r NotInRule[T]) Error(message string) NotInRule[T] {
r.err = r.err.SetMessage(message)
return r
}

// ErrorObject sets the error struct for the rule.
func (r NotInRule) ErrorObject(err Error) NotInRule {
func (r NotInRule[T]) ErrorObject(err Error) NotInRule[T] {
r.err = err
return r
}
26 changes: 26 additions & 0 deletions not_in_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ func TestNotIn(t *testing.T) {
}
}

func TestNotIn_Ints(t *testing.T) {
v := 1
var v2 *int
var tests = []struct {
tag string
values []int
value any
err string
}{
{"t0", []int{1, 2}, 0, ""},
{"t1", []int{1, 2}, 1, "must not be in list"},
{"t2", []int{1, 2}, 2, "must not be in list"},
{"t3", []int{1, 2}, 3, ""},
{"t4", []int{}, 3, ""},
{"t5", []int{1, 2}, "1", ""},
{"t6", []int{1, 2}, &v, "must not be in list"},
{"t7", []int{1, 2}, v2, ""},
}

for _, test := range tests {
r := NotIn(test.values...)
err := r.Validate(test.value)
assertError(t, test.err, err, test.tag)
}
}

func Test_NotInRule_Error(t *testing.T) {
r := NotIn(1, 2, 3)
assert.Equal(t, "must not be in list", r.Validate(1).Error())
Expand Down
Loading