Passed
Pull Request — main (#37)
by Rushan
02:17
created

validation.ConditionalConstraint.Else   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
nop 1
1
package validation
2
3
import (
4
	"github.com/muonsoft/validation/generic"
5
6
	"time"
7
)
8
9
// Constraint is the base interface to build validation constraints.
10
type Constraint interface {
11
	Option
12
	// Name is a constraint name that can be used in internal errors.
13
	Name() string
14
}
15
16
// NilConstraint is used for constraints that need to check value for nil. In common case
17
// you do not need to implement it in your constraints because nil values should be ignored.
18
type NilConstraint interface {
19
	Constraint
20
	ValidateNil(scope Scope) error
21
}
22
23
// BoolConstraint is used to build constraints for boolean values validation.
24
type BoolConstraint interface {
25
	Constraint
26
	ValidateBool(value *bool, scope Scope) error
27
}
28
29
// NumberConstraint is used to build constraints for numeric values validation.
30
//
31
// At this moment working with numbers is based on reflection.
32
// Be aware. This constraint is subject to be changed after generics implementation in Go.
33
type NumberConstraint interface {
34
	Constraint
35
	ValidateNumber(value generic.Number, scope Scope) error
36
}
37
38
// StringConstraint is used to build constraints for string values validation.
39
type StringConstraint interface {
40
	Constraint
41
	ValidateString(value *string, scope Scope) error
42
}
43
44
// IterableConstraint is used to build constraints for validation of iterables (arrays, slices, or maps).
45
//
46
// At this moment working with numbers is based on reflection.
47
// Be aware. This constraint is subject to be changed after generics implementation in Go.
48
type IterableConstraint interface {
49
	Constraint
50
	ValidateIterable(value generic.Iterable, scope Scope) error
51
}
52
53
// CountableConstraint is used to build constraints for simpler validation of iterable elements count.
54
type CountableConstraint interface {
55
	Constraint
56
	ValidateCountable(count int, scope Scope) error
57
}
58
59
// TimeConstraint is used to build constraints for date/time validation.
60
type TimeConstraint interface {
61
	Constraint
62
	ValidateTime(value *time.Time, scope Scope) error
63
}
64
65
// ConditionalConstraint is used to construct constraints using a conditional construct.
66
type ConditionalConstraint struct {
67
	condition       bool
68
	thenConstraints []Constraint
69
	elseConstraints []Constraint
70
}
71
72
// When creates a ConditionalConstraint that set condition.
73
//
74
// Example
75
//  v := "name"
76
//	err := validator.ValidateString(
77
//		&value,
78
//		validation.When(utf8.RuneCountInString(value) <= 3).
79
//		Then(
80
//			it.Matches(regexp.MustCompile(`^\\w$`)),
81
//		),
82
//	)
83
func When(condition bool) ConditionalConstraint {
84 1
	return ConditionalConstraint{
85
		condition: condition,
86
	}
87
}
88
89
// Then creates a ConditionalConstraint that set a list of then branch constraints.
90
//
91
// Example
92
//  v := "name"
93
//	err := validator.ValidateString(
94
//		&value,
95
//		validation.When(utf8.RuneCountInString(value) <= 3).
96
//		Then(
97
//			it.Matches(regexp.MustCompile(`^\\w$`)),
98
//		),
99
//	)
100
func (c ConditionalConstraint) Then(constraints ...Constraint) ConditionalConstraint {
101 1
	c.thenConstraints = constraints
102 1
	return c
103
}
104
105
// Else creates a ConditionalConstraint that set a list of else branch constraints.
106
//
107
// Example
108
//  v := "name"
109
//	err := validator.ValidateString(
110
//		&value,
111
//		validation.When(utf8.RuneCountInString(value) <= 3).
112
//		Then(
113
//			it.Matches(regexp.MustCompile(`^\\w$`)),
114
//		).
115
//		Else(
116
//			it.Matches(regexp.MustCompile(`^\\d$`)),
117
//		),
118
//	)
119
func (c ConditionalConstraint) Else(constraints ...Constraint) ConditionalConstraint {
120 1
	c.elseConstraints = constraints
121 1
	return c
122
}
123
124
// Name is the constraint name.
125
func (c ConditionalConstraint) Name() string {
126
	return "ConditionalConstraint"
127
}
128
129
// SetUp will return an error if the list of then branch constraints is empty.
130
func (c ConditionalConstraint) SetUp() error {
131 1
	if len(c.thenConstraints) == 0 {
132
		return errThenBranchNotSet
133
	}
134
135 1
	return nil
136
}
137
138
func (c ConditionalConstraint) validate(
139
	scope Scope,
140
	violations *ViolationList,
141
	validate ValidateByConstraintFunc,
142
) error {
143 1
	var constraints []Constraint
144 1
	if c.condition {
145 1
		constraints = c.thenConstraints
146
	} else {
147 1
		constraints = c.elseConstraints
148
	}
149
150 1
	for _, constraint := range constraints {
151 1
		err := violations.AppendFromError(validate(constraint, scope))
152 1
		if err != nil {
153
			return err
154
		}
155
	}
156
157 1
	return nil
158
}
159
160
// SequentiallyConstraint is used to build constraints allowing to interrupt the validation once
161
// the first violation is raised.
162
type SequentiallyConstraint struct {
163
	constraints []Constraint
164
}
165
166
// Sequentially creates a SequentiallyConstraint that set of rules that should be validated step-by-step.
167
//
168
// Example
169
//  v := "name"
170
//	err := validator.ValidateString(
171
//		&value,
172
//		validation.Sequentially(
173
//			it.IsNotBlank(),
174
//			it.Matches(regexp.MustCompile(`^\\w$`)),
175
//		),
176
//	)
177
func Sequentially(constraints ...Constraint) SequentiallyConstraint {
178 1
	return SequentiallyConstraint{
179
		constraints: constraints,
180
	}
181
}
182
183
// Name is the constraint name.
184
func (c SequentiallyConstraint) Name() string {
185
	return "SequentiallyConstraint"
186
}
187
188
// SetUp will return an error if the list of constraints is empty.
189
func (c SequentiallyConstraint) SetUp() error {
190 1
	if len(c.constraints) == 0 {
191
		return errSequentiallyConstraintsNotSet
192
	}
193 1
	return nil
194
}
195
196
func (c SequentiallyConstraint) validate(
197
	scope Scope,
198
	violations *ViolationList,
199
	validate ValidateByConstraintFunc,
200
) error {
201 1
	var err error
202 1
	for _, constraint := range c.constraints {
203 1
		err = validate(constraint, scope)
204 1
		if err != nil {
205 1
			break
206
		}
207
	}
208
209 1
	err = violations.AppendFromError(err)
210 1
	if err != nil {
211
		return err
212
	}
213
214 1
	return nil
215
}
216
217
type notFoundConstraint struct {
218
	key string
219
}
220
221
func (c notFoundConstraint) SetUp() error {
222 1
	return ConstraintNotFoundError{Key: c.key}
223
}
224
225
func (c notFoundConstraint) Name() string {
226 1
	return "notFoundConstraint"
227
}
228