Passed
Pull Request — main (#61)
by Igor
02:32
created

validation.Checker.Message   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nop 2
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
package validation
2
3
import (
4
	"fmt"
5
	"time"
6
7
	"github.com/muonsoft/validation/code"
8
	"github.com/muonsoft/validation/generic"
9
	"github.com/muonsoft/validation/message"
10
	"golang.org/x/text/language"
11
)
12
13
// Argument used to set up the validation process. It is used to set up the current validation scope and to pass
14
// arguments for validation values.
15
type Argument interface {
16
	set(arguments *Arguments) error
17
}
18
19
type argumentFunc func(arguments *Arguments) error
20
21
func (f argumentFunc) set(arguments *Arguments) error {
22 1
	return f(arguments)
23
}
24
25
type Arguments struct {
26
	scope      Scope
27
	validators []validateFunc
28
}
29
30
func (args *Arguments) addValidator(validator validateFunc) {
31 1
	args.validators = append(args.validators, validator)
32
}
33
34
// Value argument is used to validate any supported value. It uses reflection to detect the type of the argument
35
// and pass it to a specific validation method.
36
//
37
// If the validator cannot determine the value or it is not supported, then NotValidatableError will be returned
38
// when calling the validator.Validate method.
39
func Value(value interface{}, options ...Option) Argument {
40 1
	return argumentFunc(func(arguments *Arguments) error {
41 1
		v, err := newValueValidator(value, options)
42 1
		if err != nil {
43 1
			return err
44
		}
45
46 1
		arguments.addValidator(v)
47
48 1
		return nil
49
	})
50
}
51
52
// PropertyValue argument is an alias for Value that automatically adds property name to the current scope.
53
func PropertyValue(name string, value interface{}, options ...Option) Argument {
54 1
	return Value(value, append([]Option{PropertyName(name)}, options...)...)
55
}
56
57
// Bool argument is used to validate boolean values.
58
func Bool(value bool, options ...Option) Argument {
59 1
	return argumentFunc(func(arguments *Arguments) error {
60 1
		arguments.addValidator(newBoolValidator(&value, options))
61
62 1
		return nil
63
	})
64
}
65
66
// BoolProperty argument is an alias for Bool that automatically adds property name to the current scope.
67
func BoolProperty(name string, value bool, options ...Option) Argument {
68 1
	return Bool(value, append([]Option{PropertyName(name)}, options...)...)
69
}
70
71
// NilBool argument is used to validate nillable boolean values.
72
func NilBool(value *bool, options ...Option) Argument {
73 1
	return argumentFunc(func(arguments *Arguments) error {
74 1
		arguments.addValidator(newBoolValidator(value, options))
75
76 1
		return nil
77
	})
78
}
79
80
// NilBoolProperty argument is an alias for NilBool that automatically adds property name to the current scope.
81
func NilBoolProperty(name string, value *bool, options ...Option) Argument {
82 1
	return NilBool(value, append([]Option{PropertyName(name)}, options...)...)
83
}
84
85
// Number argument is used to validate numbers (any types of integers or floats). At the moment it uses
86
// reflection to detect numeric value. Given value is internally converted into int64 or float64 to make comparisons.
87
//
88
// Warning! This method will be changed after generics implementation in Go.
89
func Number(value interface{}, options ...Option) Argument {
90 1
	return argumentFunc(func(arguments *Arguments) error {
91 1
		number, err := generic.NewNumber(value)
92 1
		if err != nil {
93 1
			return fmt.Errorf(`cannot convert value "%v" to number: %w`, value, err)
94
		}
95
96 1
		arguments.addValidator(newNumberValidator(*number, options))
97
98 1
		return nil
99
	})
100
}
101
102
// NumberProperty argument is an alias for Number that automatically adds property name to the current scope.
103
func NumberProperty(name string, value interface{}, options ...Option) Argument {
104 1
	return Number(value, append([]Option{PropertyName(name)}, options...)...)
105
}
106
107
// String argument is used to validate strings.
108
func String(value string, options ...Option) Argument {
109 1
	return argumentFunc(func(arguments *Arguments) error {
110 1
		arguments.addValidator(newStringValidator(&value, options))
111
112 1
		return nil
113
	})
114
}
115
116
// StringProperty argument is an alias for String that automatically adds property name to the current scope.
117
func StringProperty(name string, value string, options ...Option) Argument {
118 1
	return String(value, append([]Option{PropertyName(name)}, options...)...)
119
}
120
121
// NilString argument is used to validate nillable strings.
122
func NilString(value *string, options ...Option) Argument {
123 1
	return argumentFunc(func(arguments *Arguments) error {
124 1
		arguments.addValidator(newStringValidator(value, options))
125
126 1
		return nil
127
	})
128
}
129
130
// NilStringProperty argument is an alias for NilString that automatically adds property name to the current scope.
131
func NilStringProperty(name string, value *string, options ...Option) Argument {
132 1
	return NilString(value, append([]Option{PropertyName(name)}, options...)...)
133
}
134
135
// Strings argument is used to validate slice of strings.
136
func Strings(values []string, options ...Option) Argument {
137 1
	return argumentFunc(func(arguments *Arguments) error {
138 1
		arguments.addValidator(newStringsValidator(values, options))
139
140 1
		return nil
141
	})
142
}
143
144
// StringsProperty argument is an alias for Strings that automatically adds property name to the current scope.
145
func StringsProperty(name string, values []string, options ...Option) Argument {
146 1
	return Strings(values, append([]Option{PropertyName(name)}, options...)...)
147
}
148
149
// Iterable argument is used to validate arrays, slices, or maps. At the moment it uses reflection
150
// to iterate over values. So you can expect a performance hit using this method. For better performance
151
// it is recommended to make a custom type that implements the Validatable interface.
152
//
153
// Warning! This argument is subject to change in the final versions of the library.
154
func Iterable(value interface{}, options ...Option) Argument {
155 1
	return argumentFunc(func(arguments *Arguments) error {
156 1
		iterable, err := generic.NewIterable(value)
157 1
		if err != nil {
158 1
			return fmt.Errorf(`cannot convert value "%v" to iterable: %w`, value, err)
159
		}
160
161 1
		arguments.addValidator(newIterableValidator(iterable, options))
162
163 1
		return nil
164
	})
165
}
166
167
// IterableProperty argument is an alias for Iterable that automatically adds property name to the current scope.
168
func IterableProperty(name string, value interface{}, options ...Option) Argument {
169 1
	return Iterable(value, append([]Option{PropertyName(name)}, options...)...)
170
}
171
172
// Countable argument can be used to validate size of an array, slice, or map. You can pass result of len()
173
// function as an argument.
174
func Countable(count int, options ...Option) Argument {
175 1
	return argumentFunc(func(arguments *Arguments) error {
176 1
		arguments.addValidator(newCountableValidator(count, options))
177
178 1
		return nil
179
	})
180
}
181
182
// CountableProperty argument is an alias for Countable that automatically adds property name to the current scope.
183
func CountableProperty(name string, count int, options ...Option) Argument {
184 1
	return Countable(count, append([]Option{PropertyName(name)}, options...)...)
185
}
186
187
// Time argument is used to validate time.Time value.
188
func Time(value time.Time, options ...Option) Argument {
189 1
	return argumentFunc(func(arguments *Arguments) error {
190 1
		arguments.addValidator(newTimeValidator(&value, options))
191
192 1
		return nil
193
	})
194
}
195
196
// TimeProperty argument is an alias for Time that automatically adds property name to the current scope.
197
func TimeProperty(name string, value time.Time, options ...Option) Argument {
198 1
	return Time(value, append([]Option{PropertyName(name)}, options...)...)
199
}
200
201
// NilTime argument is used to validate nillable time.Time value.
202
func NilTime(value *time.Time, options ...Option) Argument {
203 1
	return argumentFunc(func(arguments *Arguments) error {
204 1
		arguments.addValidator(newTimeValidator(value, options))
205
206 1
		return nil
207
	})
208
}
209
210
// NilTimeProperty argument is an alias for NilTime that automatically adds property name to the current scope.
211
func NilTimeProperty(name string, value *time.Time, options ...Option) Argument {
212 1
	return NilTime(value, append([]Option{PropertyName(name)}, options...)...)
213
}
214
215
// Each is used to validate each value of iterable (array, slice, or map). At the moment it uses reflection
216
// to iterate over values. So you can expect a performance hit using this method. For better performance
217
// it is recommended to make a custom type that implements the Validatable interface. Also, you can use
218
// EachString argument to validate slice of strings.
219
//
220
// Warning! This argument is subject to change in the final versions of the library.
221
func Each(value interface{}, options ...Option) Argument {
222 1
	return argumentFunc(func(arguments *Arguments) error {
223 1
		iterable, err := generic.NewIterable(value)
224 1
		if err != nil {
225 1
			return fmt.Errorf(`cannot convert value "%v" to iterable: %w`, value, err)
226
		}
227
228 1
		arguments.addValidator(newEachValidator(iterable, options))
229
230 1
		return nil
231
	})
232
}
233
234
// EachProperty argument is an alias for Each that automatically adds property name to the current scope.
235
func EachProperty(name string, value interface{}, options ...Option) Argument {
236 1
	return Each(value, append([]Option{PropertyName(name)}, options...)...)
237
}
238
239
// EachString is used to validate a slice of strings. This is a more performant version of Each argument.
240
func EachString(values []string, options ...Option) Argument {
241 1
	return argumentFunc(func(arguments *Arguments) error {
242 1
		arguments.addValidator(newEachStringValidator(values, options))
243
244 1
		return nil
245
	})
246
}
247
248
// EachStringProperty argument is an alias for EachString that automatically adds property name to the current scope.
249
func EachStringProperty(name string, values []string, options ...Option) Argument {
250 1
	return EachString(values, append([]Option{PropertyName(name)}, options...)...)
251
}
252
253
// Valid is used to run validation on the Validatable type. This method is recommended
254
// to build a complex validation process.
255
func Valid(value Validatable, options ...Option) Argument {
256 1
	return argumentFunc(func(arguments *Arguments) error {
257 1
		arguments.addValidator(newValidValidator(value, options))
258
259 1
		return nil
260
	})
261
}
262
263
// ValidProperty argument is an alias for Valid that automatically adds property name to the current scope.
264
func ValidProperty(name string, value Validatable, options ...Option) Argument {
265 1
	return Valid(value, append([]Option{PropertyName(name)}, options...)...)
266
}
267
268
// Check argument can be useful for quickly checking the result of some simple expression
269
// that returns a boolean value.
270
func Check(isValid bool) Checker {
271 1
	return Checker{
272
		isValid:         isValid,
273
		code:            code.NotValid,
274
		messageTemplate: message.NotValid,
275
	}
276
}
277
278
// CheckProperty argument is an alias for Check that automatically adds property name to the current scope.
279
// It is useful to apply a simple checks on structs.
280
func CheckProperty(name string, isValid bool) Checker {
281 1
	return Checker{
282
		propertyName:    name,
283
		isValid:         isValid,
284
		code:            code.NotValid,
285
		messageTemplate: message.NotValid,
286
	}
287
}
288
289
// Language argument sets the current language for translation of a violation message.
290
func Language(tag language.Tag) Argument {
291 1
	return argumentFunc(func(arguments *Arguments) error {
292 1
		arguments.scope.language = tag
293
294 1
		return nil
295
	})
296
}
297
298
// NewArgument can be used to implement your own validation arguments for the specific types.
299
// See example for more details.
300
func NewArgument(options []Option, validate ValidateByConstraintFunc) Argument {
301 1
	return argumentFunc(func(arguments *Arguments) error {
302 1
		arguments.addValidator(newValidator(options, validate))
303
304 1
		return nil
305
	})
306
}
307
308
// Checker is an argument that can be useful for quickly checking the result of
309
// some simple expression that returns a boolean value.
310
type Checker struct {
311
	isValid           bool
312
	propertyName      string
313
	code              string
314
	messageTemplate   string
315
	messageParameters TemplateParameterList
316
}
317
318
// Code overrides default code for produced violation.
319
func (c Checker) Code(code string) Checker {
320 1
	c.code = code
321 1
	return c
322
}
323
324
// Message sets the violation message template. You can set custom template parameters
325
// for injecting its values into the final message.
326
func (c Checker) Message(template string, parameters ...TemplateParameter) Checker {
327 1
	c.messageTemplate = template
328 1
	c.messageParameters = parameters
329 1
	return c
330
}
331
332
func (c Checker) set(arguments *Arguments) error {
333 1
	arguments.addValidator(c.validate)
334 1
	return nil
335
}
336
337
func (c Checker) validate(scope Scope) (*ViolationList, error) {
338 1
	if c.isValid {
339 1
		return nil, nil
340
	}
341 1
	if c.propertyName != "" {
342 1
		scope = scope.AtProperty(c.propertyName)
343
	}
344
345 1
	violation := scope.BuildViolation(c.code, c.messageTemplate).
346
		SetParameters(c.messageParameters...).
347
		CreateViolation()
348
349 1
	return NewViolationList(violation), nil
350
}
351