From c098b9ac30947466598dee5bab7422d0b0cddeb4 Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Mon, 27 Apr 2020 00:28:07 +0200 Subject: [PATCH] Adds support for 'Nil' and 'Empty' rules (#106) --- README.md | 2 + absent.go | 60 ++++++++++++++++++++++++++++ absent_test.go | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 absent.go create mode 100644 absent_test.go diff --git a/README.md b/README.md index 89b5b51..692050c 100644 --- a/README.md +++ b/README.md @@ -457,6 +457,8 @@ The following rules are provided in the `validation` package: * `Required`: checks if a value is not empty (neither nil nor zero). * `NotNil`: checks if a pointer value is not nil. Non-pointer values are considered valid. * `NilOrNotEmpty`: checks if a value is a nil pointer or a non-empty value. This differs from `Required` in that it treats a nil pointer as valid. +* `Nil`: checks if a value is a nil pointer. +* `Empty`: checks if a value is empty. nil pointers are considered valid. * `Skip`: this is a special rule used to indicate that all rules following it should be skipped (including the nested ones). * `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. diff --git a/absent.go b/absent.go new file mode 100644 index 0000000..5074251 --- /dev/null +++ b/absent.go @@ -0,0 +1,60 @@ +// Copyright 2016 Qiang Xue. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package validation + +var ( + // ErrNil is the error that returns when a value is not nil. + ErrNil = NewError("validation_nil", "must be blank") + // ErrEmpty is the error that returns when a not nil value is not empty. + ErrEmpty = NewError("validation_empty", "must be blank") +) + +// Nil is a validation rule that checks if a value is nil. +// It is the opposite of NotNil rule +var Nil = absentRule{err: ErrNil, condition: true, skipNil: false} + +// Empty checks if a not nil value is empty. +var Empty = absentRule{err: ErrEmpty, condition: true, skipNil: true} + +type absentRule struct { + condition bool + err Error + skipNil bool +} + +// Validate checks if the given value is valid or not. +func (r absentRule) Validate(value interface{}) error { + if r.condition { + value, isNil := Indirect(value) + if r.skipNil == false && isNil == false { + return r.err + } + if isNil { + return nil + } + if !IsEmpty(value) { + return r.err + } + } + return nil +} + +// When sets the condition that determines if the validation should be performed. +func (r absentRule) When(condition bool) absentRule { + r.condition = condition + return r +} + +// Error sets the error message for the rule. +func (r absentRule) Error(message string) absentRule { + r.err = r.err.SetMessage(message) + return r +} + +// ErrorObject sets the error struct for the rule. +func (r absentRule) ErrorObject(err Error) absentRule { + r.err = err + return r +} diff --git a/absent_test.go b/absent_test.go new file mode 100644 index 0000000..3e5d8c8 --- /dev/null +++ b/absent_test.go @@ -0,0 +1,104 @@ +// Copyright 2016 Qiang Xue. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package validation + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestNil(t *testing.T) { + s1 := "123" + s2 := "" + var time1 time.Time + tests := []struct { + tag string + value interface{} + err string + }{ + {"t1", 123, "must be blank"}, + {"t2", "", "must be blank"}, + {"t3", &s1, "must be blank"}, + {"t4", &s2, "must be blank"}, + {"t5", nil, ""}, + {"t6", time1, "must be blank"}, + } + + for _, test := range tests { + r := Nil + err := r.Validate(test.value) + assertError(t, test.err, err, test.tag) + } +} + +func TestEmpty(t *testing.T) { + s1 := "123" + s2 := "" + time1 := time.Now() + var time2 time.Time + tests := []struct { + tag string + value interface{} + err string + }{ + {"t1", 123, "must be blank"}, + {"t2", "", ""}, + {"t3", &s1, "must be blank"}, + {"t4", &s2, ""}, + {"t5", nil, ""}, + {"t6", time1, "must be blank"}, + {"t7", time2, ""}, + } + + for _, test := range tests { + r := Empty + err := r.Validate(test.value) + assertError(t, test.err, err, test.tag) + } +} + +func TestAbsentRule_When(t *testing.T) { + r := Nil.When(false) + err := Validate(42, r) + assert.Nil(t, err) + + r = Nil.When(true) + err = Validate(42, r) + assert.Equal(t, ErrNil, err) +} + +func Test_absentRule_Error(t *testing.T) { + r := Nil + assert.Equal(t, "must be blank", r.Validate("42").Error()) + assert.False(t, r.skipNil) + r2 := r.Error("123") + assert.Equal(t, "must be blank", r.Validate("42").Error()) + assert.False(t, r.skipNil) + assert.Equal(t, "123", r2.err.Message()) + assert.False(t, r2.skipNil) + + r = Empty + assert.Equal(t, "must be blank", r.Validate("42").Error()) + assert.True(t, r.skipNil) + r2 = r.Error("123") + assert.Equal(t, "must be blank", r.Validate("42").Error()) + assert.True(t, r.skipNil) + assert.Equal(t, "123", r2.err.Message()) + assert.True(t, r2.skipNil) +} + +func TestAbsentRule_Error(t *testing.T) { + r := Nil + + err := NewError("code", "abc") + r = r.ErrorObject(err) + + assert.Equal(t, err, r.err) + assert.Equal(t, err.Code(), r.err.Code()) + assert.Equal(t, err.Message(), r.err.Message()) + assert.NotEqual(t, err, Nil.err) +}