Test Setup Failed
Pull Request — main (#71)
by Igor
01:48
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
// EachString is used to validate a slice of strings.
119
func EachString(values []string, constraints ...StringConstraint) ValidatorArgument {
120
	return NewArgument(validateEachString(values, constraints))
121
}
122
123 1
// EachStringProperty argument is an alias for EachString that automatically adds property name to the current scope.
124 1
func EachStringProperty(name string, values []string, constraints ...StringConstraint) ValidatorArgument {
125
	return NewArgument(validateEachString(values, constraints)).With(PropertyName(name))
126 1
}
127
128
// EachNumber is used to validate a slice of numbers.
129
func EachNumber[T Numeric](values []T, constraints ...NumberConstraint[T]) ValidatorArgument {
130
	return NewArgument(validateEachNumber(values, constraints))
131
}
132 1
133
// EachNumberProperty argument is an alias for EachString that automatically adds property name to the current scope.
134
func EachNumberProperty[T Numeric](name string, values []T, constraints ...NumberConstraint[T]) ValidatorArgument {
135
	return NewArgument(validateEachNumber(values, constraints)).With(PropertyName(name))
136
}
137 1
138 1
// Valid is used to run validation on the Validatable type. This method is recommended
139
// to build a complex validation process.
140 1
func Valid(value Validatable) ValidatorArgument {
141
	return NewArgument(validateIt(value))
142
}
143
144
// ValidProperty argument is an alias for Valid that automatically adds property name to the current scope.
145
func ValidProperty(name string, value Validatable) ValidatorArgument {
146 1
	return NewArgument(validateIt(value)).With(PropertyName(name))
147
}
148
149
// ValidSlice is a generic argument used to run validation on the slice of Validatable types.
150
// This method is recommended to build a complex validation process.
151
func ValidSlice[T Validatable](values []T) ValidatorArgument {
152
	return NewArgument(validateSlice(values))
153
}
154
155 1
// ValidSliceProperty argument is an alias for ValidSlice that automatically adds property name to the current scope.
156 1
func ValidSliceProperty[T Validatable](name string, values []T) ValidatorArgument {
157 1
	return NewArgument(validateSlice(values)).With(PropertyName(name))
158 1
}
159
160
// ValidMap is a generic argument used to run validation on the map of Validatable types.
161 1
// This method is recommended to build a complex validation process.
162
func ValidMap[T Validatable](values map[string]T) ValidatorArgument {
163 1
	return NewArgument(validateMap(values))
164
}
165
166
// ValidMapProperty argument is an alias for ValidSlice that automatically adds property name to the current scope.
167
func ValidMapProperty[T Validatable](name string, values map[string]T) ValidatorArgument {
168
	return NewArgument(validateMap(values)).With(PropertyName(name))
169 1
}
170
171
// Comparable argument is used to validate generic comparable value.
172
func Comparable[T comparable](value T, constraints ...ComparableConstraint[T]) ValidatorArgument {
173
	return NewArgument(validateComparable(&value, constraints))
174
}
175 1
176 1
// ComparableProperty argument is an alias for Comparable that automatically adds property name to the current scope.
177
func ComparableProperty[T comparable](name string, value T, constraints ...ComparableConstraint[T]) ValidatorArgument {
178 1
	return NewArgument(validateComparable(&value, constraints)).With(PropertyName(name))
179
}
180
181
// NilComparable argument is used to validate nillable generic comparable value.
182
func NilComparable[T comparable](value *T, constraints ...ComparableConstraint[T]) ValidatorArgument {
183
	return NewArgument(validateComparable(value, constraints))
184 1
}
185
186
// NilComparableProperty argument is an alias for NilComparable that automatically adds property name to the current scope.
187
func NilComparableProperty[T comparable](name string, value *T, constraints ...ComparableConstraint[T]) ValidatorArgument {
188
	return NewArgument(validateComparable(value, constraints)).With(PropertyName(name))
189 1
}
190 1
191
// Comparables argument is used to validate generic comparable types.
192 1
func Comparables[T comparable](values []T, constraints ...ComparablesConstraint[T]) ValidatorArgument {
193
	return NewArgument(validateComparables(values, constraints))
194
}
195
196
// ComparablesProperty argument is an alias for Comparables that automatically adds property name to the current scope.
197
func ComparablesProperty[T comparable](name string, values []T, constraints ...ComparablesConstraint[T]) ValidatorArgument {
198 1
	return NewArgument(validateComparables(values, constraints)).With(PropertyName(name))
199
}
200
201
// CheckNoViolations is a special argument that checks err for violations. If err contains Violation or ViolationList
202
// then these violations will be appended into returned violation list from the validator. If err contains an error
203 1
// that does not implement an error interface, then the validation process will be terminated and
204 1
// this error will be returned.
205
func CheckNoViolations(err error) ValidatorArgument {
206 1
	return NewArgument(func(scope Scope) (*ViolationList, error) {
207
		return unwrapViolationList(err)
208
	})
209
}
210
211
// Check argument can be useful for quickly checking the result of some simple expression
212 1
// that returns a boolean value.
213
func Check(isValid bool) Checker {
214
	return Checker{
215
		isValid:         isValid,
216
		code:            code.NotValid,
217
		messageTemplate: message.Templates[code.NotValid],
218
	}
219
}
220
221
// CheckProperty argument is an alias for Check that automatically adds property name to the current scope.
222 1
// It is useful to apply a simple checks on structs.
223 1
func CheckProperty(name string, isValid bool) Checker {
224 1
	return Checker{
225 1
		propertyName:    name,
226
		isValid:         isValid,
227
		code:            code.NotValid,
228 1
		messageTemplate: message.Templates[code.NotValid],
229
	}
230 1
}
231
232
// Language argument sets the current language for translation of a violation message.
233
func Language(tag language.Tag) Argument {
234
	return argumentFunc(func(ctx *executionContext) {
235
		ctx.scope.language = tag
236 1
	})
237
}
238
239
type ValidateOnScopeFunc func(scope Scope) (*ViolationList, error)
240
241 1
// NewArgument can be used to implement validation functional arguments for the specific types.
242 1
func NewArgument(validate ValidateOnScopeFunc) ValidatorArgument {
243
	return ValidatorArgument{validate: validate}
244 1
}
245
246
// ValidatorArgument is common implementation of Argument that is used to run validation
247
// process on given argument.
248
type ValidatorArgument struct {
249
	isIgnored bool
250 1
	validate  ValidateOnScopeFunc
251
	options   []Option
252
}
253
254
// With returns a copy of ValidatorArgument with appended options.
255
func (arg ValidatorArgument) With(options ...Option) ValidatorArgument {
256 1
	arg.options = append(arg.options, options...)
257 1
	return arg
258
}
259 1
260
// When enables conditional validation of this argument. If the expression evaluates to false,
261
// then the argument will be ignored.
262
func (arg ValidatorArgument) When(condition bool) ValidatorArgument {
263
	arg.isIgnored = !condition
264
	return arg
265 1
}
266
267
func (arg ValidatorArgument) setUp(ctx *executionContext) {
268
	if !arg.isIgnored {
269
		ctx.addValidator(arg.options, arg.validate)
270
	}
271
}
272
273 1
type argumentFunc func(ctx *executionContext)
274 1
275 1
func (f argumentFunc) setUp(ctx *executionContext) {
276 1
	f(ctx)
277 1
}
278 1
279
// Checker is an argument that can be useful for quickly checking the result of
280
// some simple expression that returns a boolean value.
281 1
type Checker struct {
282
	options           []Option
283
	isIgnored         bool
284 1
	isValid           bool
285
	propertyName      string
286
	groups            []string
287
	code              string
288
	messageTemplate   string
289
	messageParameters TemplateParameterList
290
}
291 1
292
// With returns a copy of Checker with appended options.
293
func (c Checker) With(options ...Option) Checker {
294
	c.options = append(c.options, options...)
295
	return c
296
}
297
298
// When enables conditional validation of this constraint. If the expression evaluates to false,
299
// then the constraint will be ignored.
300
func (c Checker) When(condition bool) Checker {
301 1
	c.isIgnored = !condition
302
	return c
303
}
304
305
// WhenGroups enables conditional validation of the constraint by using the validation groups.
306
func (c Checker) WhenGroups(groups ...string) Checker {
307
	c.groups = groups
308
	return c
309
}
310
311 1
// Code overrides default code for produced violation.
312 1
func (c Checker) Code(code string) Checker {
313
	c.code = code
314 1
	return c
315
}
316
317
// Message sets the violation message template. You can set custom template parameters
318
// for injecting its values into the final message.
319
func (c Checker) Message(template string, parameters ...TemplateParameter) Checker {
320
	c.messageTemplate = template
321 1
	c.messageParameters = parameters
322 1
	return c
323
}
324 1
325
func (c Checker) setUp(arguments *executionContext) {
326
	arguments.addValidator(c.options, c.validate)
327
}
328
329
func (c Checker) validate(scope Scope) (*ViolationList, error) {
330
	if c.isValid || c.isIgnored || scope.IsIgnored(c.groups...) {
331
		return nil, nil
332
	}
333
	if c.propertyName != "" {
334
		scope = scope.AtProperty(c.propertyName)
335
	}
336
337
	violation := scope.BuildViolation(c.code, c.messageTemplate).
338
		SetParameters(c.messageParameters...).
339
		CreateViolation()
340
341
	return NewViolationList(violation), nil
342
}
343