Test Failed
Push — main ( a33214...7dfe2c )
by Igor
02:53
created

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