Passed
Pull Request — main (#37)
by Rushan
01:59
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
type ConditionalConstraint struct {
66
	condition       bool
67
	thenConstraints []Constraint
68
	elseConstraints []Constraint
69
}
70
71
func When(condition bool) ConditionalConstraint {
72 1
	return ConditionalConstraint{
73
		condition: condition,
74
	}
75
}
76
77
func (c ConditionalConstraint) Then(constraints ...Constraint) ConditionalConstraint {
78 1
	c.thenConstraints = constraints
79 1
	return c
80
}
81
82
func (c ConditionalConstraint) Else(constraints ...Constraint) ConditionalConstraint {
83 1
	c.elseConstraints = constraints
84 1
	return c
85
}
86
87
func (c ConditionalConstraint) SetUp() error {
88 1
	if len(c.thenConstraints) == 0 {
89
		return errThenBranchNotSet
90
	}
91
92 1
	return nil
93
}
94
95
func (c *ConditionalConstraint) validateConditionConstraints(
96
	scope Scope,
97
	violations *ViolationList,
98
	validate ValidateByConstraintFunc,
99
) error {
100 1
	var constraints []Constraint
101 1
	if c.condition {
102 1
		constraints = c.thenConstraints
103
	} else {
104 1
		constraints = c.elseConstraints
105
	}
106
107 1
	for _, constraint := range constraints {
108 1
		err := violations.AppendFromError(validate(constraint, scope))
109 1
		if err != nil {
110
			return err
111
		}
112
	}
113
114 1
	return nil
115
}
116
117
func (c ConditionalConstraint) Name() string {
118
	return "ConditionalConstraint"
119
}
120
121
type notFoundConstraint struct {
122
	key string
123
}
124
125
func (c notFoundConstraint) SetUp() error {
126 1
	return ConstraintNotFoundError{Key: c.key}
127
}
128
129
func (c notFoundConstraint) Name() string {
130 1
	return "notFoundConstraint"
131
}
132
133
type SequentiallyConstraint struct {
134
	constraints []Constraint
135
}
136
137
func Sequentially(constraints ...Constraint) SequentiallyConstraint {
138 1
	return SequentiallyConstraint{
139
		constraints: constraints,
140
	}
141
}
142
143
// Name is the constraint name.
144
func (c SequentiallyConstraint) Name() string {
145
	return "SequentiallyConstraint"
146
}
147
148
// SetUp will return an error if the list of constraints is empty.
149
func (c SequentiallyConstraint) SetUp() error {
150 1
	if len(c.constraints) == 0 {
151
		return errSequentiallyConstraintsNotSet
152
	}
153 1
	return nil
154
}
155
156
func (c *SequentiallyConstraint) validateSequentiallyConstraints(
157
	scope Scope,
158
	violations *ViolationList,
159
	validate ValidateByConstraintFunc,
160
) error {
161 1
	var isViolation bool
162 1
	for _, constraint := range c.constraints {
163 1
		err := validate(constraint, scope)
164 1
		if err != nil {
165 1
			isViolation = true
166
		}
167 1
		err = violations.AppendFromError(err)
168 1
		if err != nil {
169
			return err
170
		}
171
172 1
		if isViolation {
173 1
			return nil
174
		}
175
	}
176
177
	return nil
178
}
179