Passed
Pull Request — main (#68)
by Igor
02:34
created

validation.notFoundConstraint.SetUp   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
ccs 1
cts 1
cp 1
crap 1
nop 0
1
package validation
2
3
import (
4
	"time"
5
6
	"github.com/muonsoft/validation/code"
7
	"github.com/muonsoft/validation/generic"
8
	"github.com/muonsoft/validation/message"
9
)
10
11
// Constraint is the base interface to build validation constraints.
12
type Constraint interface {
13
	Option
14
	// Name is a constraint name that can be used in internal errors.
15
	Name() string
16
}
17
18
// NilConstraint is used for constraints that need to check value for nil. In common case
19
// you do not need to implement it in your constraints because nil values should be ignored.
20
type NilConstraint interface {
21
	Constraint
22
	ValidateNil(scope Scope) error
23
}
24
25
// BoolConstraint is used to build constraints for boolean values validation.
26
type BoolConstraint interface {
27
	Constraint
28
	ValidateBool(value *bool, scope Scope) error
29
}
30
31
// NumberConstraint is used to build constraints for numeric values validation.
32
//
33
// At this moment working with numbers is based on reflection.
34
// Be aware. This constraint is subject to be changed after generics implementation in Go.
35
type NumberConstraint interface {
36
	Constraint
37
	ValidateNumber(value generic.Number, scope Scope) error
38
}
39
40
// StringConstraint is used to build constraints for string values validation.
41
type StringConstraint interface {
42
	Constraint
43
	ValidateString(value *string, scope Scope) error
44
}
45
46
// StringsConstraint is used to build constraints to validate an array or a slice of strings.
47
type StringsConstraint interface {
48
	Constraint
49
	ValidateStrings(values []string, scope Scope) error
50
}
51
52
// IterableConstraint is used to build constraints for validation of iterables (arrays, slices, or maps).
53
//
54
// At this moment working with numbers is based on reflection.
55
// Be aware. This constraint is subject to be changed after generics implementation in Go.
56
type IterableConstraint interface {
57
	Constraint
58
	ValidateIterable(value generic.Iterable, scope Scope) error
59
}
60
61
// CountableConstraint is used to build constraints for simpler validation of iterable elements count.
62
type CountableConstraint interface {
63
	Constraint
64
	ValidateCountable(count int, scope Scope) error
65
}
66
67
// TimeConstraint is used to build constraints for date/time validation.
68
type TimeConstraint interface {
69
	Constraint
70
	ValidateTime(value *time.Time, scope Scope) error
71
}
72
73
type controlConstraint interface {
74
	validate(scope Scope, validate ValidateByConstraintFunc) (*ViolationList, error)
75
}
76
77
// CustomStringConstraint can be used to create custom constraints for validating string values
78
// based on function with signature func(string) bool.
79
type CustomStringConstraint struct {
80
	isIgnored         bool
81
	isValid           func(string) bool
82
	groups            []string
83
	name              string
84
	code              string
85
	messageTemplate   string
86
	messageParameters TemplateParameterList
87
}
88
89
// NewCustomStringConstraint creates a new string constraint from a function with signature func(string) bool.
90
// Optional parameters can be used to set up constraint name (first parameter), violation code (second),
91
// message template (third). All other parameters are ignored.
92
func NewCustomStringConstraint(isValid func(string) bool, parameters ...string) CustomStringConstraint {
93 1
	constraint := CustomStringConstraint{
94
		isValid:         isValid,
95
		name:            "CustomStringConstraint",
96
		code:            code.NotValid,
97
		messageTemplate: message.Templates[code.NotValid],
98
	}
99
100 1
	if len(parameters) > 0 {
101 1
		constraint.name = parameters[0]
102
	}
103 1
	if len(parameters) > 1 {
104 1
		constraint.code = parameters[1]
105
	}
106 1
	if len(parameters) > 2 {
107 1
		constraint.messageTemplate = parameters[2]
108
	}
109
110 1
	return constraint
111
}
112
113
// SetUp always returns no error.
114
func (c CustomStringConstraint) SetUp() error {
115 1
	return nil
116
}
117
118
// Name is the constraint name. It can be set via first parameter of function NewCustomStringConstraint.
119
func (c CustomStringConstraint) Name() string {
120 1
	return c.name
121
}
122
123
// Code overrides default code for produced violation.
124
func (c CustomStringConstraint) Code(code string) CustomStringConstraint {
125 1
	c.code = code
126 1
	return c
127
}
128
129
// Message sets the violation message template. You can set custom template parameters
130
// for injecting its values into the final message. Also, you can use default parameters:
131
//
132
//	{{ value }} - the current (invalid) value.
133
func (c CustomStringConstraint) Message(template string, parameters ...TemplateParameter) CustomStringConstraint {
134 1
	c.messageTemplate = template
135 1
	c.messageParameters = parameters
136 1
	return c
137
}
138
139
// When enables conditional validation of this constraint. If the expression evaluates to false,
140
// then the constraint will be ignored.
141
func (c CustomStringConstraint) When(condition bool) CustomStringConstraint {
142 1
	c.isIgnored = !condition
143 1
	return c
144
}
145
146
// WhenGroups enables conditional validation of the constraint by using the validation groups.
147
func (c CustomStringConstraint) WhenGroups(groups ...string) CustomStringConstraint {
148 1
	c.groups = groups
149 1
	return c
150
}
151
152
func (c CustomStringConstraint) ValidateString(value *string, scope Scope) error {
153 1
	if c.isIgnored || scope.IsIgnored(c.groups...) || value == nil || *value == "" || c.isValid(*value) {
154 1
		return nil
155
	}
156
157 1
	return scope.BuildViolation(c.code, c.messageTemplate).
158
		SetParameters(
159
			c.messageParameters.Prepend(
160
				TemplateParameter{Key: "{{ value }}", Value: *value},
161
			)...,
162
		).
163
		AddParameter("{{ value }}", *value).
164
		CreateViolation()
165
}
166
167
// ConditionalConstraint is used for conditional validation.
168
// Use the When function to initiate a conditional check.
169
// If the condition is true, then the constraints passed through the Then function will be applied.
170
// Otherwise, the constraints passed through the Else function will apply.
171
type ConditionalConstraint struct {
172
	condition       bool
173
	thenConstraints []Constraint
174
	elseConstraints []Constraint
175
}
176
177
// When function is used to initiate conditional validation.
178
// If the condition is true, then the constraints passed through the Then function will be applied.
179
// Otherwise, the constraints passed through the Else function will apply.
180
func When(condition bool) ConditionalConstraint {
181 1
	return ConditionalConstraint{condition: condition}
182
}
183
184
// Then function is used to set a sequence of constraints to be applied if the condition is true.
185
// If the list is empty error will be returned.
186
func (c ConditionalConstraint) Then(constraints ...Constraint) ConditionalConstraint {
187 1
	c.thenConstraints = constraints
188 1
	return c
189
}
190
191
// Else function is used to set a sequence of constraints to be applied if a condition is false.
192
func (c ConditionalConstraint) Else(constraints ...Constraint) ConditionalConstraint {
193 1
	c.elseConstraints = constraints
194 1
	return c
195
}
196
197
// SetUp will return an error if Then function did not set any constraints.
198
func (c ConditionalConstraint) SetUp() error {
199 1
	if len(c.thenConstraints) == 0 {
200 1
		return errThenBranchNotSet
201
	}
202
203 1
	return nil
204
}
205
206
// Name is the constraint name.
207
func (c ConditionalConstraint) Name() string {
208 1
	return "ConditionalConstraint"
209
}
210
211
func (c ConditionalConstraint) validate(
212
	scope Scope,
213
	validate ValidateByConstraintFunc,
214
) (*ViolationList, error) {
215 1
	violations := &ViolationList{}
216 1
	var constraints []Constraint
217
218 1
	if c.condition {
219 1
		constraints = c.thenConstraints
220
	} else {
221 1
		constraints = c.elseConstraints
222
	}
223
224 1
	for _, constraint := range constraints {
225 1
		err := violations.AppendFromError(validate(constraint, scope))
226 1
		if err != nil {
227
			return nil, err
228
		}
229
	}
230
231 1
	return violations, nil
232
}
233
234
// SequentialConstraint is used to set constraints allowing to interrupt the validation once
235
// the first violation is raised.
236
type SequentialConstraint struct {
237
	isIgnored   bool
238
	groups      []string
239
	constraints []Constraint
240
}
241
242
// Sequentially function used to set of constraints that should be validated step-by-step.
243
// If the list is empty error will be returned.
244
func Sequentially(constraints ...Constraint) SequentialConstraint {
245 1
	return SequentialConstraint{constraints: constraints}
246
}
247
248
// SetUp will return an error if the list of constraints is empty.
249
func (c SequentialConstraint) SetUp() error {
250 1
	if len(c.constraints) == 0 {
251 1
		return errSequentiallyConstraintsNotSet
252
	}
253 1
	return nil
254
}
255
256
// Name is the constraint name.
257
func (c SequentialConstraint) Name() string {
258 1
	return "SequentialConstraint"
259
}
260
261
// When enables conditional validation of this constraint. If the expression evaluates to false,
262
// then the constraint will be ignored.
263
func (c SequentialConstraint) When(condition bool) SequentialConstraint {
264 1
	c.isIgnored = !condition
265 1
	return c
266
}
267
268
// WhenGroups enables conditional validation of the constraint by using the validation groups.
269
func (c SequentialConstraint) WhenGroups(groups ...string) SequentialConstraint {
270 1
	c.groups = groups
271 1
	return c
272
}
273
274
func (c SequentialConstraint) validate(
275
	scope Scope,
276
	validate ValidateByConstraintFunc,
277
) (*ViolationList, error) {
278 1
	if c.isIgnored || scope.IsIgnored(c.groups...) {
279 1
		return nil, nil
280
	}
281
282 1
	violations := &ViolationList{}
283
284 1
	for _, constraint := range c.constraints {
285 1
		err := violations.AppendFromError(validate(constraint, scope))
286 1
		if err != nil {
287
			return nil, err
288 1
		} else if violations.len > 0 {
289 1
			return violations, nil
290
		}
291
	}
292
293
	return violations, nil
294
}
295
296
// AtLeastOneOfConstraint is used to set constraints allowing checks that the value satisfies
297
// at least one of the given constraints.
298
// The validation stops as soon as one constraint is satisfied.
299
type AtLeastOneOfConstraint struct {
300
	isIgnored   bool
301
	groups      []string
302
	constraints []Constraint
303
}
304
305
// AtLeastOneOf function used to set of constraints that the value satisfies at least one of the given constraints.
306
// If the list is empty error will be returned.
307
func AtLeastOneOf(constraints ...Constraint) AtLeastOneOfConstraint {
308 1
	return AtLeastOneOfConstraint{constraints: constraints}
309
}
310
311
// SetUp will return an error if the list of constraints is empty.
312
func (c AtLeastOneOfConstraint) SetUp() error {
313 1
	if len(c.constraints) == 0 {
314 1
		return errAtLeastOneOfConstraintsNotSet
315
	}
316 1
	return nil
317
}
318
319
// Name is the constraint name.
320
func (c AtLeastOneOfConstraint) Name() string {
321 1
	return "AtLeastOneOfConstraint"
322
}
323
324
// When enables conditional validation of this constraint. If the expression evaluates to false,
325
// then the constraint will be ignored.
326
func (c AtLeastOneOfConstraint) When(condition bool) AtLeastOneOfConstraint {
327 1
	c.isIgnored = !condition
328 1
	return c
329
}
330
331
// WhenGroups enables conditional validation of the constraint by using the validation groups.
332
func (c AtLeastOneOfConstraint) WhenGroups(groups ...string) AtLeastOneOfConstraint {
333 1
	c.groups = groups
334 1
	return c
335
}
336
337
func (c AtLeastOneOfConstraint) validate(
338
	scope Scope,
339
	validate ValidateByConstraintFunc,
340
) (*ViolationList, error) {
341 1
	if c.isIgnored || scope.IsIgnored(c.groups...) {
342 1
		return nil, nil
343
	}
344
345 1
	violations := &ViolationList{}
346
347 1
	for _, constraint := range c.constraints {
348 1
		violation := validate(constraint, scope)
349 1
		if violation == nil {
350 1
			return nil, nil
351
		}
352
353 1
		err := violations.AppendFromError(violation)
354 1
		if err != nil {
355
			return nil, err
356
		}
357
	}
358
359 1
	return violations, nil
360
}
361
362
// CompoundConstraint is used to create your own set of reusable constraints, representing rules to use consistently.
363
type CompoundConstraint struct {
364
	isIgnored   bool
365
	groups      []string
366
	constraints []Constraint
367
}
368
369
// Compound function used to create set of reusable constraints.
370
// If the list is empty error will be returned.
371
func Compound(constraints ...Constraint) CompoundConstraint {
372 1
	return CompoundConstraint{constraints: constraints}
373
}
374
375
// SetUp will return an error if the list of constraints is empty.
376
func (c CompoundConstraint) SetUp() error {
377 1
	if len(c.constraints) == 0 {
378 1
		return errCompoundConstraintsNotSet
379
	}
380 1
	return nil
381
}
382
383
// Name is the constraint name.
384
func (c CompoundConstraint) Name() string {
385 1
	return "CompoundConstraint"
386
}
387
388
// When enables conditional validation of this constraint. If the expression evaluates to false,
389
// then the constraint will be ignored.
390
func (c CompoundConstraint) When(condition bool) CompoundConstraint {
391 1
	c.isIgnored = !condition
392 1
	return c
393
}
394
395
// WhenGroups enables conditional validation of the constraint by using the validation groups.
396
func (c CompoundConstraint) WhenGroups(groups ...string) CompoundConstraint {
397 1
	c.groups = groups
398 1
	return c
399
}
400
401
func (c CompoundConstraint) validate(
402
	scope Scope,
403
	validate ValidateByConstraintFunc,
404
) (*ViolationList, error) {
405 1
	if c.isIgnored || scope.IsIgnored(c.groups...) {
406 1
		return nil, nil
407
	}
408
409 1
	violations := &ViolationList{}
410
411 1
	for _, constraint := range c.constraints {
412 1
		err := violations.AppendFromError(validate(constraint, scope))
413 1
		if err != nil {
414
			return nil, err
415
		}
416
	}
417
418 1
	return violations, nil
419
}
420
421
type notFoundConstraint struct {
422
	key string
423
}
424
425
func (c notFoundConstraint) SetUp() error {
426 1
	return ConstraintNotFoundError{Key: c.key}
427
}
428
429
func (c notFoundConstraint) Name() string {
430 1
	return "notFoundConstraint"
431
}
432