Test Failed
Push — main ( 7dfe2c...03ceef )
by Igor
01:04 queued 12s
created

validation.SequentialArgument.When   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
package validation
2
3
import (
4
	"context"
5
	"sync"
6
)
7
8
// WhenArgument is used to build conditional validation. Use the When function to initiate a conditional check.
9
// If the condition is true, then the arguments passed through the Then function will be processed.
10
// Otherwise, the arguments passed through the Else function will be processed.
11
type WhenArgument struct {
12
	isTrue        bool
13
	path          []PropertyPathElement
14
	thenArguments []Argument
15
	elseArguments []Argument
16
}
17
18
// When function is used to initiate conditional validation.
19
// If the condition is true, then the arguments passed through the Then function will be processed.
20
// Otherwise, the arguments passed through the Else function will be processed.
21
func When(isTrue bool) WhenArgument {
22
	return WhenArgument{isTrue: isTrue}
23
}
24
25
// Then function is used to set a sequence of arguments to be processed if the condition is true.
26
func (arg WhenArgument) Then(arguments ...Argument) WhenArgument {
27
	arg.thenArguments = arguments
28
	return arg
29
}
30
31
// Else function is used to set a sequence of arguments to be processed if a condition is false.
32
func (arg WhenArgument) Else(arguments ...Argument) WhenArgument {
33
	arg.elseArguments = arguments
34
	return arg
35
}
36
37
// At returns a copy of WhenArgument with appended property path suffix.
38
func (arg WhenArgument) At(path ...PropertyPathElement) WhenArgument {
39
	arg.path = append(arg.path, path...)
40
	return arg
41
}
42
43
func (arg WhenArgument) setUp(ctx *executionContext) {
44
	ctx.addValidation(arg.validate, arg.path...)
45
}
46
47
func (arg WhenArgument) validate(ctx context.Context, validator *Validator) (*ViolationList, error) {
48
	var err error
49
	if arg.isTrue {
50
		err = validator.Validate(ctx, arg.thenArguments...)
51
	} else {
52
		err = validator.Validate(ctx, arg.elseArguments...)
53
	}
54
55
	return unwrapViolationList(err)
56
}
57
58
// WhenGroupsArgument is used to build conditional validation based on groups. Use the WhenGroups function
59
// to initiate a conditional check. If validation group matches to the validator one,
60
// then the arguments passed through the Then function will be processed.
61
// Otherwise, the arguments passed through the Else function will be processed.
62
type WhenGroupsArgument struct {
63
	groups        []string
64
	path          []PropertyPathElement
65
	thenArguments []Argument
66
	elseArguments []Argument
67
}
68
69
// WhenGroups is used to build conditional validation based on groups. If validation group matches to
70
// the validator one, then the arguments passed through the Then function will be processed.
71
// Otherwise, the arguments passed through the Else function will be processed.
72
func WhenGroups(groups ...string) WhenGroupsArgument {
73
	return WhenGroupsArgument{groups: groups}
74
}
75
76
// Then function is used to set a sequence of arguments to be processed if the validation group is active.
77
func (arg WhenGroupsArgument) Then(arguments ...Argument) WhenGroupsArgument {
78
	arg.thenArguments = arguments
79
	return arg
80
}
81
82
// Else function is used to set a sequence of arguments to be processed if the validation group is active.
83
func (arg WhenGroupsArgument) Else(arguments ...Argument) WhenGroupsArgument {
84
	arg.elseArguments = arguments
85
	return arg
86
}
87
88
// At returns a copy of WhenGroupsArgument with appended property path suffix.
89
func (arg WhenGroupsArgument) At(path ...PropertyPathElement) WhenGroupsArgument {
90
	arg.path = append(arg.path, path...)
91
	return arg
92
}
93
94
func (arg WhenGroupsArgument) setUp(ctx *executionContext) {
95
	ctx.addValidation(arg.validate, arg.path...)
96
}
97
98
func (arg WhenGroupsArgument) validate(ctx context.Context, validator *Validator) (*ViolationList, error) {
99
	var err error
100
	if validator.IsIgnoredForGroups(arg.groups...) {
101
		err = validator.Validate(ctx, arg.elseArguments...)
102
	} else {
103
		err = validator.Validate(ctx, arg.thenArguments...)
104
	}
105
106
	return unwrapViolationList(err)
107
}
108
109
// SequentialArgument can be used to interrupt validation process when the first violation is raised.
110
type SequentialArgument struct {
111
	isIgnored bool
112
	path      []PropertyPathElement
113
	arguments []Argument
114
}
115
116
// Sequentially function used to run validation process step-by-step.
117
func Sequentially(arguments ...Argument) SequentialArgument {
118
	return SequentialArgument{arguments: arguments}
119
}
120
121
// At returns a copy of SequentialArgument with appended property path suffix.
122
func (arg SequentialArgument) At(path ...PropertyPathElement) SequentialArgument {
123
	arg.path = append(arg.path, path...)
124
	return arg
125
}
126
127
// When enables conditional validation of this argument. If the expression evaluates to false,
128
// then the argument will be ignored.
129
func (arg SequentialArgument) When(condition bool) SequentialArgument {
130
	arg.isIgnored = !condition
131
	return arg
132
}
133
134
func (arg SequentialArgument) setUp(ctx *executionContext) {
135
	ctx.addValidation(arg.validate, arg.path...)
136
}
137
138
func (arg SequentialArgument) validate(ctx context.Context, validator *Validator) (*ViolationList, error) {
139
	if arg.isIgnored {
140
		return nil, nil
141
	}
142
143
	violations := &ViolationList{}
144
145
	for _, argument := range arg.arguments {
146
		err := violations.AppendFromError(validator.Validate(ctx, argument))
147
		if err != nil {
148
			return nil, err
149
		}
150
		if violations.len > 0 {
151
			return violations, nil
152
		}
153
	}
154
155
	return violations, nil
156
}
157
158
// AtLeastOneOfArgument can be used to set up validation process to check that the value satisfies
159
// at least one of the given constraints. The validation stops as soon as one constraint is satisfied.
160
type AtLeastOneOfArgument struct {
161
	isIgnored bool
162
	path      []PropertyPathElement
163
	arguments []Argument
164
}
165
166
// AtLeastOneOf can be used to set up validation process to check that the value satisfies
167
// at least one of the given constraints. The validation stops as soon as one constraint is satisfied.
168
func AtLeastOneOf(arguments ...Argument) AtLeastOneOfArgument {
169
	return AtLeastOneOfArgument{arguments: arguments}
170
}
171
172
// At returns a copy of AtLeastOneOfArgument with appended property path suffix.
173
func (arg AtLeastOneOfArgument) At(path ...PropertyPathElement) AtLeastOneOfArgument {
174
	arg.path = append(arg.path, path...)
175
	return arg
176
}
177
178
// When enables conditional validation of this argument. If the expression evaluates to false,
179
// then the argument will be ignored.
180
func (arg AtLeastOneOfArgument) When(condition bool) AtLeastOneOfArgument {
181
	arg.isIgnored = !condition
182
	return arg
183
}
184
185
func (arg AtLeastOneOfArgument) setUp(ctx *executionContext) {
186
	ctx.addValidation(arg.validate, arg.path...)
187
}
188
189
func (arg AtLeastOneOfArgument) validate(ctx context.Context, validator *Validator) (*ViolationList, error) {
190
	if arg.isIgnored {
191
		return nil, nil
192
	}
193
194
	violations := &ViolationList{}
195
196
	for _, argument := range arg.arguments {
197
		violation := validator.Validate(ctx, argument)
198
		if violation == nil {
199
			return nil, nil
200
		}
201
202
		err := violations.AppendFromError(violation)
203
		if err != nil {
204
			return nil, err
205
		}
206
	}
207
208
	return violations, nil
209
}
210
211
// AllArgument can be used to interrupt validation process when the first violation is raised.
212
type AllArgument struct {
213
	isIgnored bool
214
	path      []PropertyPathElement
215
	arguments []Argument
216
}
217
218
// All runs validation for each argument. It works exactly as validator.Validate method.
219
// It can be helpful to build complex validation process.
220
func All(arguments ...Argument) AllArgument {
221
	return AllArgument{arguments: arguments}
222
}
223
224
// At returns a copy of AllArgument with appended property path suffix.
225
func (arg AllArgument) At(path ...PropertyPathElement) AllArgument {
226
	arg.path = append(arg.path, path...)
227
	return arg
228
}
229
230
// When enables conditional validation of this argument. If the expression evaluates to false,
231
// then the argument will be ignored.
232
func (arg AllArgument) When(condition bool) AllArgument {
233
	arg.isIgnored = !condition
234
	return arg
235
}
236
237
func (arg AllArgument) setUp(ctx *executionContext) {
238
	ctx.addValidation(arg.validate, arg.path...)
239
}
240
241
func (arg AllArgument) validate(ctx context.Context, validator *Validator) (*ViolationList, error) {
242
	if arg.isIgnored {
243
		return nil, nil
244
	}
245
246
	violations := &ViolationList{}
247
248
	for _, argument := range arg.arguments {
249
		err := violations.AppendFromError(validator.Validate(ctx, argument))
250
		if err != nil {
251
			return nil, err
252
		}
253
	}
254
255
	return violations, nil
256
}
257
258
// AsyncArgument can be used to interrupt validation process when the first violation is raised.
259
type AsyncArgument struct {
260
	isIgnored bool
261
	path      []PropertyPathElement
262
	arguments []Argument
263
}
264
265
// Async implements async/await pattern and runs validation for each argument in a separate goroutine.
266
func Async(arguments ...Argument) AsyncArgument {
267
	return AsyncArgument{arguments: arguments}
268
}
269
270
// At returns a copy of AsyncArgument with appended property path suffix.
271
func (arg AsyncArgument) At(path ...PropertyPathElement) AsyncArgument {
272
	arg.path = append(arg.path, path...)
273
	return arg
274
}
275
276
// When enables conditional validation of this argument. If the expression evaluates to false,
277
// then the argument will be ignored.
278
func (arg AsyncArgument) When(condition bool) AsyncArgument {
279
	arg.isIgnored = !condition
280
	return arg
281
}
282
283
func (arg AsyncArgument) setUp(ctx *executionContext) {
284
	ctx.addValidation(arg.validate, arg.path...)
285
}
286
287
func (arg AsyncArgument) validate(ctx context.Context, validator *Validator) (*ViolationList, error) {
288
	if arg.isIgnored {
289
		return nil, nil
290
	}
291
292
	ctx, cancel := context.WithCancel(ctx)
293
	defer cancel()
294
295
	waiter := &sync.WaitGroup{}
296
	waiter.Add(len(arg.arguments))
297
	errs := make(chan error)
298
	for _, argument := range arg.arguments {
299
		go func(argument Argument) {
300
			defer waiter.Done()
301
			errs <- validator.Validate(ctx, argument)
302
		}(argument)
303
	}
304
305
	go func() {
306
		waiter.Wait()
307
		close(errs)
308
	}()
309
310
	violations := &ViolationList{}
311
312
	for violation := range errs {
313
		err := violations.AppendFromError(violation)
314
		if err != nil {
315
			return nil, err
316
		}
317
	}
318
319
	return violations, nil
320
}
321