Test Failed
Pull Request — main (#71)
by Igor
02:01
created

arguments.go   F

Size/Duplication

Total Lines 384
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
cc 68
eloc 175
dl 0
loc 384
ccs 103
cts 103
cp 1
crap 68
rs 2.96
c 0
b 0
f 0
1
package validation
2
3
import (
4
	"time"
5
6
	"github.com/muonsoft/validation/code"
7
	"github.com/muonsoft/validation/message"
8
	"golang.org/x/text/language"
9
)
10
11
// Argument used to set up the validation process. It is used to set up the current validation scope and to pass
12
// arguments for validation values.
13
type Argument interface {
14
	setUp(ctx *executionContext)
15
}
16
17
// Nil argument is used to validate nil values of any nillable types.
18
func Nil(isNil bool, constraints ...NilConstraint) ValidatorArgument {
19
	return NewArgument(validateNil(isNil, constraints))
20
}
21
22 1
// NilProperty argument is an alias for Nil that automatically adds property name to the current scope.
23
func NilProperty(name string, isNil bool, constraints ...NilConstraint) ValidatorArgument {
24
	return NewArgument(validateNil(isNil, constraints)).With(PropertyName(name))
25
}
26
27
// Bool argument is used to validate boolean values.
28
func Bool(value bool, constraints ...BoolConstraint) ValidatorArgument {
29
	return NewArgument(validateBool(&value, constraints))
30
}
31 1
32
// BoolProperty argument is an alias for Bool that automatically adds property name to the current scope.
33
func BoolProperty(name string, value bool, constraints ...BoolConstraint) ValidatorArgument {
34
	return NewArgument(validateBool(&value, constraints)).With(PropertyName(name))
35
}
36
37
// NilBool argument is used to validate nillable boolean values.
38
func NilBool(value *bool, constraints ...BoolConstraint) ValidatorArgument {
39
	return NewArgument(validateBool(value, constraints))
40 1
}
41 1
42 1
// NilBoolProperty argument is an alias for NilBool that automatically adds property name to the current scope.
43 1
func NilBoolProperty(name string, value *bool, constraints ...BoolConstraint) ValidatorArgument {
44
	return NewArgument(validateBool(value, constraints)).With(PropertyName(name))
45
}
46 1
47
// Number argument is used to validate numbers.
48 1
func Number[T Numeric](value T, constraints ...NumberConstraint[T]) ValidatorArgument {
49
	return NewArgument(validateNumber(&value, constraints))
50
}
51
52
// NumberProperty argument is an alias for Number that automatically adds property name to the current scope.
53
func NumberProperty[T Numeric](name string, value T, constraints ...NumberConstraint[T]) ValidatorArgument {
54 1
	return NewArgument(validateNumber(&value, constraints)).With(PropertyName(name))
55
}
56
57
// NilNumber argument is used to validate nillable numbers.
58
func NilNumber[T Numeric](value *T, constraints ...NumberConstraint[T]) ValidatorArgument {
59 1
	return NewArgument(validateNumber(value, constraints))
60 1
}
61
62 1
// NilNumberProperty argument is an alias for NilNumber that automatically adds property name to the current scope.
63
func NilNumberProperty[T Numeric](name string, value *T, constraints ...NumberConstraint[T]) ValidatorArgument {
64
	return NewArgument(validateNumber(value, constraints)).With(PropertyName(name))
65
}
66
67
// String argument is used to validate strings.
68 1
func String(value string, constraints ...StringConstraint) ValidatorArgument {
69
	return NewArgument(validateString(&value, constraints))
70
}
71
72
// StringProperty argument is an alias for String that automatically adds property name to the current scope.
73 1
func StringProperty(name string, value string, constraints ...StringConstraint) ValidatorArgument {
74 1
	return NewArgument(validateString(&value, constraints)).With(PropertyName(name))
75
}
76 1
77
// NilString argument is used to validate nillable strings.
78
func NilString(value *string, constraints ...StringConstraint) ValidatorArgument {
79
	return NewArgument(validateString(value, constraints))
80
}
81
82 1
// NilStringProperty argument is an alias for NilString that automatically adds property name to the current scope.
83
func NilStringProperty(name string, value *string, constraints ...StringConstraint) ValidatorArgument {
84
	return NewArgument(validateString(value, constraints)).With(PropertyName(name))
85
}
86
87
// Countable argument can be used to validate size of an array, slice, or map. You can pass result of len()
88
// function as an argument.
89
func Countable(count int, constraints ...CountableConstraint) ValidatorArgument {
90 1
	return NewArgument(validateCountable(count, constraints))
91 1
}
92 1
93 1
// CountableProperty argument is an alias for Countable that automatically adds property name to the current scope.
94
func CountableProperty(name string, count int, constraints ...CountableConstraint) ValidatorArgument {
95
	return NewArgument(validateCountable(count, constraints)).With(PropertyName(name))
96 1
}
97
98 1
// Time argument is used to validate time.Time value.
99
func Time(value time.Time, constraints ...TimeConstraint) ValidatorArgument {
100
	return NewArgument(validateTime(&value, constraints))
101
}
102
103
// TimeProperty argument is an alias for Time that automatically adds property name to the current scope.
104 1
func TimeProperty(name string, value time.Time, constraints ...TimeConstraint) ValidatorArgument {
105
	return NewArgument(validateTime(&value, constraints)).With(PropertyName(name))
106
}
107
108
// NilTime argument is used to validate nillable time.Time value.
109 1
func NilTime(value *time.Time, constraints ...TimeConstraint) ValidatorArgument {
110 1
	return NewArgument(validateTime(value, constraints))
111
}
112 1
113
// NilTimeProperty argument is an alias for NilTime that automatically adds property name to the current scope.
114
func NilTimeProperty(name string, value *time.Time, constraints ...TimeConstraint) ValidatorArgument {
115
	return NewArgument(validateTime(value, constraints)).With(PropertyName(name))
116
}
117
118 1
// Valid is used to run validation on the Validatable type. This method is recommended
119
// to build a complex validation process.
120
func Valid(value Validatable) ValidatorArgument {
121
	return NewArgument(validateIt(value))
122
}
123 1
124 1
// ValidProperty argument is an alias for Valid that automatically adds property name to the current scope.
125
func ValidProperty(name string, value Validatable) ValidatorArgument {
126 1
	return NewArgument(validateIt(value)).With(PropertyName(name))
127
}
128
129
// ValidSlice is a generic argument used to run validation on the slice of Validatable types.
130
// This method is recommended to build a complex validation process.
131
func ValidSlice[T Validatable](values []T) ValidatorArgument {
132 1
	return NewArgument(validateSlice(values))
133
}
134
135
// ValidSliceProperty argument is an alias for ValidSlice that automatically adds property name to the current scope.
136
func ValidSliceProperty[T Validatable](name string, values []T) ValidatorArgument {
137 1
	return NewArgument(validateSlice(values)).With(PropertyName(name))
138 1
}
139
140 1
// ValidMap is a generic argument used to run validation on the map of Validatable types.
141
// This method is recommended to build a complex validation process.
142
func ValidMap[T Validatable](values map[string]T) ValidatorArgument {
143
	return NewArgument(validateMap(values))
144
}
145
146 1
// ValidMapProperty argument is an alias for ValidSlice that automatically adds property name to the current scope.
147
func ValidMapProperty[T Validatable](name string, values map[string]T) ValidatorArgument {
148
	return NewArgument(validateMap(values)).With(PropertyName(name))
149
}
150
151
// Comparable argument is used to validate generic comparable value.
152
func Comparable[T comparable](value T, constraints ...ComparableConstraint[T]) ValidatorArgument {
153
	return NewArgument(validateComparable(&value, constraints))
154
}
155 1
156 1
// ComparableProperty argument is an alias for Comparable that automatically adds property name to the current scope.
157 1
func ComparableProperty[T comparable](name string, value T, constraints ...ComparableConstraint[T]) ValidatorArgument {
158 1
	return NewArgument(validateComparable(&value, constraints)).With(PropertyName(name))
159
}
160
161 1
// NilComparable argument is used to validate nillable generic comparable value.
162
func NilComparable[T comparable](value *T, constraints ...ComparableConstraint[T]) ValidatorArgument {
163 1
	return NewArgument(validateComparable(value, constraints))
164
}
165
166
// NilComparableProperty argument is an alias for NilComparable that automatically adds property name to the current scope.
167
func NilComparableProperty[T comparable](name string, value *T, constraints ...ComparableConstraint[T]) ValidatorArgument {
168
	return NewArgument(validateComparable(value, constraints)).With(PropertyName(name))
169 1
}
170
171
// Comparables argument is used to validate generic comparable types.
172
func Comparables[T comparable](values []T, constraints ...ComparablesConstraint[T]) ValidatorArgument {
173
	return NewArgument(validateComparables(values, constraints))
174
}
175 1
176 1
// ComparablesProperty argument is an alias for Comparables that automatically adds property name to the current scope.
177
func ComparablesProperty[T comparable](name string, values []T, constraints ...ComparablesConstraint[T]) ValidatorArgument {
178 1
	return NewArgument(validateComparables(values, constraints)).With(PropertyName(name))
179
}
180
181
// EachString is used to validate a slice of strings.
182
func EachString(values []string, constraints ...StringConstraint) ValidatorArgument {
183
	return NewArgument(validateEachString(values, constraints))
184 1
}
185
186
// EachStringProperty argument is an alias for EachString that automatically adds property name to the current scope.
187
func EachStringProperty(name string, values []string, constraints ...StringConstraint) ValidatorArgument {
188
	return NewArgument(validateEachString(values, constraints)).With(PropertyName(name))
189 1
}
190 1
191
// EachNumber is used to validate a slice of numbers.
192 1
func EachNumber[T Numeric](values []T, constraints ...NumberConstraint[T]) ValidatorArgument {
193
	return NewArgument(validateEachNumber(values, constraints))
194
}
195
196
// EachNumberProperty argument is an alias for EachString that automatically adds property name to the current scope.
197
func EachNumberProperty[T Numeric](name string, values []T, constraints ...NumberConstraint[T]) ValidatorArgument {
198 1
	return NewArgument(validateEachNumber(values, constraints)).With(PropertyName(name))
199
}
200
201
// EachComparable is used to validate a slice of generic comparables.
202
func EachComparable[T comparable](values []T, constraints ...ComparableConstraint[T]) ValidatorArgument {
203 1
	return NewArgument(validateEachComparable(values, constraints))
204 1
}
205
206 1
// EachComparableProperty argument is an alias for EachComparable that automatically adds property name to the current scope.
207
func EachComparableProperty[T comparable](name string, values []T, constraints ...ComparableConstraint[T]) ValidatorArgument {
208
	return NewArgument(validateEachComparable(values, constraints)).With(PropertyName(name))
209
}
210
211
// CheckNoViolations is a special argument that checks err for violations. If err contains Violation or ViolationList
212 1
// then these violations will be appended into returned violation list from the validator. If err contains an error
213
// that does not implement an error interface, then the validation process will be terminated and
214
// this error will be returned.
215
func CheckNoViolations(err error) ValidatorArgument {
216
	return NewArgument(func(scope Scope) (*ViolationList, error) {
217
		return unwrapViolationList(err)
218
	})
219
}
220
221
// Check argument can be useful for quickly checking the result of some simple expression
222 1
// that returns a boolean value.
223 1
func Check(isValid bool) Checker {
224 1
	return Checker{
225 1
		isValid:         isValid,
226
		code:            code.NotValid,
227
		messageTemplate: message.Templates[code.NotValid],
228 1
	}
229
}
230 1
231
// CheckProperty argument is an alias for Check that automatically adds property name to the current scope.
232
// It is useful to apply a simple checks on structs.
233
func CheckProperty(name string, isValid bool) Checker {
234
	return Checker{
235
		propertyName:    name,
236 1
		isValid:         isValid,
237
		code:            code.NotValid,
238
		messageTemplate: message.Templates[code.NotValid],
239
	}
240
}
241 1
242 1
// Language argument sets the current language for translation of a violation message.
243
func Language(tag language.Tag) Argument {
244 1
	return argumentFunc(func(ctx *executionContext) {
245
		ctx.scope.language = tag
246
	})
247
}
248
249
type ValidateOnScopeFunc func(scope Scope) (*ViolationList, error)
250 1
251
// NewArgument can be used to implement validation functional arguments for the specific types.
252
func NewArgument(validate ValidateOnScopeFunc) ValidatorArgument {
253
	return ValidatorArgument{validate: validate}
254
}
255
256 1
// ValidatorArgument is common implementation of Argument that is used to run validation
257 1
// process on given argument.
258
type ValidatorArgument struct {
259 1
	isIgnored bool
260
	validate  ValidateOnScopeFunc
261
	options   []Option
262
}
263
264
// With returns a copy of ValidatorArgument with appended options.
265 1
func (arg ValidatorArgument) With(options ...Option) ValidatorArgument {
266
	arg.options = append(arg.options, options...)
267
	return arg
268
}
269
270
// When enables conditional validation of this argument. If the expression evaluates to false,
271
// then the argument will be ignored.
272
func (arg ValidatorArgument) When(condition bool) ValidatorArgument {
273 1
	arg.isIgnored = !condition
274 1
	return arg
275 1
}
276 1
277 1
func (arg ValidatorArgument) setUp(ctx *executionContext) {
278 1
	if !arg.isIgnored {
279
		ctx.addValidator(arg.options, arg.validate)
280
	}
281 1
}
282
283
type argumentFunc func(ctx *executionContext)
284 1
285
func (f argumentFunc) setUp(ctx *executionContext) {
286
	f(ctx)
287
}
288
289
// Checker is an argument that can be useful for quickly checking the result of
290
// some simple expression that returns a boolean value.
291 1
type Checker struct {
292
	options           []Option
293
	isIgnored         bool
294
	isValid           bool
295
	propertyName      string
296
	groups            []string
297
	code              string
298
	messageTemplate   string
299
	messageParameters TemplateParameterList
300
}
301 1
302
// With returns a copy of Checker with appended options.
303
func (c Checker) With(options ...Option) Checker {
304
	c.options = append(c.options, options...)
305
	return c
306
}
307
308
// When enables conditional validation of this constraint. If the expression evaluates to false,
309
// then the constraint will be ignored.
310
func (c Checker) When(condition bool) Checker {
311 1
	c.isIgnored = !condition
312 1
	return c
313
}
314 1
315
// WhenGroups enables conditional validation of the constraint by using the validation groups.
316
func (c Checker) WhenGroups(groups ...string) Checker {
317
	c.groups = groups
318
	return c
319
}
320
321 1
// Code overrides default code for produced violation.
322 1
func (c Checker) Code(code string) Checker {
323
	c.code = code
324 1
	return c
325
}
326
327
// Message sets the violation message template. You can set custom template parameters
328
// for injecting its values into the final message.
329
func (c Checker) Message(template string, parameters ...TemplateParameter) Checker {
330
	c.messageTemplate = template
331
	c.messageParameters = parameters
332
	return c
333
}
334
335
func (c Checker) setUp(arguments *executionContext) {
336
	arguments.addValidator(c.options, c.validate)
337
}
338
339
func (c Checker) validate(scope Scope) (*ViolationList, error) {
340
	if c.isValid || c.isIgnored || scope.IsIgnored(c.groups...) {
341
		return nil, nil
342
	}
343 1
	if c.propertyName != "" {
344 1
		scope = scope.AtProperty(c.propertyName)
345
	}
346
347
	violation := scope.BuildViolation(c.code, c.messageTemplate).
348
		SetParameters(c.messageParameters...).
349 1
		CreateViolation()
350 1
351
	return NewViolationList(violation), nil
352
}
353