Passed
Push — main ( 957b58...866640 )
by Igor
02:11 queued 24s
created

it.IsBetweenFloats   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 6
nop 2
dl 0
loc 6
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
package it
2
3
import (
4
	"strconv"
5
	"time"
6
7
	"github.com/muonsoft/validation"
8
	"github.com/muonsoft/validation/code"
9
	"github.com/muonsoft/validation/generic"
10
	"github.com/muonsoft/validation/is"
11
	"github.com/muonsoft/validation/message"
12
)
13
14
// NumberComparisonConstraint is used for various numeric comparisons between integer and float values.
15
// Values are compared as integers if the compared and specified values are integers.
16
// Otherwise, numbers are always compared as floating point numbers.
17
type NumberComparisonConstraint struct {
18
	isIgnored         bool
19
	groups            []string
20
	code              string
21
	messageTemplate   string
22
	messageParameters validation.TemplateParameterList
23
	comparedValue     string
24
	isValid           func(value generic.Number) bool
25
}
26
27
// IsEqualToInteger checks that the number (integer or float) is equal to the specified integer value.
28
// Values are compared as integers if the compared and specified values are integers.
29
// Otherwise, numbers are always compared as floating point numbers.
30
func IsEqualToInteger(value int64) NumberComparisonConstraint {
31 1
	v := generic.NewNumberFromInt(value)
32
33 1
	return NumberComparisonConstraint{
34
		code:            code.Equal,
35
		messageTemplate: message.Templates[code.Equal],
36
		comparedValue:   v.String(),
37
		isValid: func(n generic.Number) bool {
38 1
			return n.IsEqualTo(v)
39
		},
40
	}
41
}
42
43
// IsEqualToFloat checks that the number (integer or float) is equal to the specified float value.
44
// Values are compared as integers if the compared and specified values are integers.
45
// Otherwise, numbers are always compared as floating point numbers.
46
func IsEqualToFloat(value float64) NumberComparisonConstraint {
47 1
	v := generic.NewNumberFromFloat(value)
48
49 1
	return NumberComparisonConstraint{
50
		code:            code.Equal,
51
		messageTemplate: message.Templates[code.Equal],
52
		comparedValue:   v.String(),
53
		isValid: func(n generic.Number) bool {
54 1
			return n.IsEqualTo(v)
55
		},
56
	}
57
}
58
59
// IsNotEqualToInteger checks that the number (integer or float) is not equal to the specified integer value.
60
// Values are compared as integers if the compared and specified values are integers.
61
// Otherwise, numbers are always compared as floating point numbers.
62
func IsNotEqualToInteger(value int64) NumberComparisonConstraint {
63 1
	v := generic.NewNumberFromInt(value)
64
65 1
	return NumberComparisonConstraint{
66
		code:            code.NotEqual,
67
		messageTemplate: message.Templates[code.NotEqual],
68
		comparedValue:   v.String(),
69
		isValid: func(n generic.Number) bool {
70 1
			return !n.IsEqualTo(v)
71
		},
72
	}
73
}
74
75
// IsNotEqualToFloat checks that the number (integer or float) is not equal to the specified float value.
76
// Values are compared as integers if the compared and specified values are integers.
77
// Otherwise, numbers are always compared as floating point numbers.
78
func IsNotEqualToFloat(value float64) NumberComparisonConstraint {
79 1
	v := generic.NewNumberFromFloat(value)
80
81 1
	return NumberComparisonConstraint{
82
		code:            code.NotEqual,
83
		messageTemplate: message.Templates[code.NotEqual],
84
		comparedValue:   v.String(),
85
		isValid: func(n generic.Number) bool {
86 1
			return !n.IsEqualTo(v)
87
		},
88
	}
89
}
90
91
// IsLessThanInteger checks that the number (integer or float) is less than the specified integer value.
92
// Values are compared as integers if the compared and specified values are integers.
93
// Otherwise, numbers are always compared as floating point numbers.
94
func IsLessThanInteger(value int64) NumberComparisonConstraint {
95 1
	v := generic.NewNumberFromInt(value)
96
97 1
	return NumberComparisonConstraint{
98
		code:            code.TooHigh,
99
		messageTemplate: message.Templates[code.TooHigh],
100
		comparedValue:   v.String(),
101
		isValid: func(n generic.Number) bool {
102 1
			return n.IsLessThan(v)
103
		},
104
	}
105
}
106
107
// IsLessThanFloat checks that the number (integer or float) is less than the specified float value.
108
// Values are compared as integers if the compared and specified values are integers.
109
// Otherwise, numbers are always compared as floating point numbers.
110
func IsLessThanFloat(value float64) NumberComparisonConstraint {
111 1
	v := generic.NewNumberFromFloat(value)
112
113 1
	return NumberComparisonConstraint{
114
		code:            code.TooHigh,
115
		messageTemplate: message.Templates[code.TooHigh],
116
		comparedValue:   v.String(),
117
		isValid: func(n generic.Number) bool {
118 1
			return n.IsLessThan(v)
119
		},
120
	}
121
}
122
123
// IsLessThanOrEqualInteger checks that the number (integer or float) is less than or
124
// equal to the specified integer value. Values are compared as integers if the compared
125
// and specified values are integers. Otherwise, numbers are always compared as floating point numbers.
126
func IsLessThanOrEqualInteger(value int64) NumberComparisonConstraint {
127 1
	v := generic.NewNumberFromInt(value)
128
129 1
	return NumberComparisonConstraint{
130
		code:            code.TooHighOrEqual,
131
		messageTemplate: message.Templates[code.TooHighOrEqual],
132
		comparedValue:   v.String(),
133
		isValid: func(n generic.Number) bool {
134 1
			return n.IsLessThan(v) || n.IsEqualTo(v)
135
		},
136
	}
137
}
138
139
// IsLessThanOrEqualFloat checks that the number (integer or float) is less than or
140
// equal to the specified float value. Values are compared as integers if the compared
141
// and specified values are integers. Otherwise, numbers are always compared as floating point numbers.
142
func IsLessThanOrEqualFloat(value float64) NumberComparisonConstraint {
143 1
	v := generic.NewNumberFromFloat(value)
144
145 1
	return NumberComparisonConstraint{
146
		code:            code.TooHighOrEqual,
147
		messageTemplate: message.Templates[code.TooHighOrEqual],
148
		comparedValue:   v.String(),
149
		isValid: func(n generic.Number) bool {
150 1
			return n.IsLessThan(v) || n.IsEqualTo(v)
151
		},
152
	}
153
}
154
155
// IsGreaterThanInteger checks that the number (integer or float) is greater than the specified integer value.
156
// Values are compared as integers if the compared and specified values are integers.
157
// Otherwise, numbers are always compared as floating point numbers.
158
func IsGreaterThanInteger(value int64) NumberComparisonConstraint {
159 1
	v := generic.NewNumberFromInt(value)
160
161 1
	return NumberComparisonConstraint{
162
		code:            code.TooLow,
163
		messageTemplate: message.Templates[code.TooLow],
164
		comparedValue:   v.String(),
165
		isValid: func(n generic.Number) bool {
166 1
			return n.IsGreaterThan(v)
167
		},
168
	}
169
}
170
171
// IsGreaterThanFloat checks that the number (integer or float) is greater than the specified float value.
172
// Values are compared as integers if the compared and specified values are integers.
173
// Otherwise, numbers are always compared as floating point numbers.
174
func IsGreaterThanFloat(value float64) NumberComparisonConstraint {
175 1
	v := generic.NewNumberFromFloat(value)
176
177 1
	return NumberComparisonConstraint{
178
		code:            code.TooLow,
179
		messageTemplate: message.Templates[code.TooLow],
180
		comparedValue:   v.String(),
181
		isValid: func(n generic.Number) bool {
182 1
			return n.IsGreaterThan(v)
183
		},
184
	}
185
}
186
187
// IsGreaterThanOrEqualInteger checks that the number (integer or float) is greater than or
188
// equal to the specified integer value. Values are compared as integers if the compared
189
// and specified values are integers. Otherwise, numbers are always compared as floating point numbers.
190
func IsGreaterThanOrEqualInteger(value int64) NumberComparisonConstraint {
191 1
	v := generic.NewNumberFromInt(value)
192
193 1
	return NumberComparisonConstraint{
194
		code:            code.TooLowOrEqual,
195
		messageTemplate: message.Templates[code.TooLowOrEqual],
196
		comparedValue:   v.String(),
197
		isValid: func(n generic.Number) bool {
198 1
			return n.IsGreaterThan(v) || n.IsEqualTo(v)
199
		},
200
	}
201
}
202
203
// IsGreaterThanOrEqualFloat checks that the number (integer or float) is greater than or
204
// equal to the specified float value. Values are compared as integers if the compared
205
// and specified values are integers. Otherwise, numbers are always compared as floating point numbers.
206
func IsGreaterThanOrEqualFloat(value float64) NumberComparisonConstraint {
207 1
	v := generic.NewNumberFromFloat(value)
208
209 1
	return NumberComparisonConstraint{
210
		code:            code.TooLowOrEqual,
211
		messageTemplate: message.Templates[code.TooLowOrEqual],
212
		comparedValue:   v.String(),
213
		isValid: func(n generic.Number) bool {
214 1
			return n.IsGreaterThan(v) || n.IsEqualTo(v)
215
		},
216
	}
217
}
218
219
// IsPositive checks that the value is a positive number (integer or float). Zero is neither
220
// positive nor negative. If you want to allow zero use IsPositiveOrZero comparison.
221
func IsPositive() NumberComparisonConstraint {
222 1
	v := generic.NewNumberFromInt(0)
223
224 1
	return NumberComparisonConstraint{
225
		code:            code.NotPositive,
226
		messageTemplate: message.Templates[code.NotPositive],
227
		comparedValue:   v.String(),
228
		isValid: func(n generic.Number) bool {
229 1
			return n.IsGreaterThan(v)
230
		},
231
	}
232
}
233
234
// IsPositiveOrZero checks that the value is a positive number (integer or float) or equal to zero.
235
// If you don't want to allow zero as a valid value, use IsPositive comparison.
236
func IsPositiveOrZero() NumberComparisonConstraint {
237 1
	v := generic.NewNumberFromInt(0)
238
239 1
	return NumberComparisonConstraint{
240
		code:            code.NotPositiveOrZero,
241
		messageTemplate: message.Templates[code.NotPositiveOrZero],
242
		comparedValue:   v.String(),
243
		isValid: func(n generic.Number) bool {
244 1
			return n.IsGreaterThan(v) || n.IsEqualTo(v)
245
		},
246
	}
247
}
248
249
// IsNegative checks that the value is a negative number (integer or float). Zero is neither
250
// positive nor negative. If you want to allow zero use IsNegativeOrZero comparison.
251
func IsNegative() NumberComparisonConstraint {
252 1
	v := generic.NewNumberFromInt(0)
253
254 1
	return NumberComparisonConstraint{
255
		code:            code.NotNegative,
256
		messageTemplate: message.Templates[code.NotNegative],
257
		comparedValue:   v.String(),
258
		isValid: func(n generic.Number) bool {
259 1
			return n.IsLessThan(v)
260
		},
261
	}
262
}
263
264
// IsNegativeOrZero checks that the value is a negative number (integer or float) or equal to zero.
265
// If you don't want to allow zero as a valid value, use IsNegative comparison.
266
func IsNegativeOrZero() NumberComparisonConstraint {
267 1
	v := generic.NewNumberFromInt(0)
268
269 1
	return NumberComparisonConstraint{
270
		code:            code.NotNegativeOrZero,
271
		messageTemplate: message.Templates[code.NotNegativeOrZero],
272
		comparedValue:   v.String(),
273
		isValid: func(n generic.Number) bool {
274 1
			return n.IsLessThan(v) || n.IsEqualTo(v)
275
		},
276
	}
277
}
278
279
// SetUp always returns no error.
280
func (c NumberComparisonConstraint) SetUp() error {
281 1
	return nil
282
}
283
284
// Name is the constraint name.
285
func (c NumberComparisonConstraint) Name() string {
286
	return "NumberComparisonConstraint"
287
}
288
289
// Code overrides default code for produced violation.
290
func (c NumberComparisonConstraint) Code(code string) NumberComparisonConstraint {
291 1
	c.code = code
292 1
	return c
293
}
294
295
// Message sets the violation message template. You can set custom template parameters
296
// for injecting its values into the final message. Also, you can use default parameters:
297
//
298
//  {{ comparedValue }} - the expected value;
299
//  {{ value }} - the current (invalid) value.
300
func (c NumberComparisonConstraint) Message(
301
	template string,
302
	parameters ...validation.TemplateParameter,
303
) NumberComparisonConstraint {
304 1
	c.messageTemplate = template
305 1
	c.messageParameters = parameters
306 1
	return c
307
}
308
309
// When enables conditional validation of this constraint. If the expression evaluates to false,
310
// then the constraint will be ignored.
311
func (c NumberComparisonConstraint) When(condition bool) NumberComparisonConstraint {
312 1
	c.isIgnored = !condition
313 1
	return c
314
}
315
316
// WhenGroups enables conditional validation of the constraint by using the validation groups.
317
func (c NumberComparisonConstraint) WhenGroups(groups ...string) NumberComparisonConstraint {
318 1
	c.groups = groups
319 1
	return c
320
}
321
322
func (c NumberComparisonConstraint) ValidateNumber(value generic.Number, scope validation.Scope) error {
323 1
	if c.isIgnored || scope.IsIgnored(c.groups...) || value.IsNil() || c.isValid(value) {
324 1
		return nil
325
	}
326
327 1
	return scope.BuildViolation(c.code, c.messageTemplate).
328
		SetParameters(
329
			c.messageParameters.Prepend(
330
				validation.TemplateParameter{Key: "{{ comparedValue }}", Value: c.comparedValue},
331
				validation.TemplateParameter{Key: "{{ value }}", Value: value.String()},
332
			)...,
333
		).
334
		CreateViolation()
335
}
336
337
// RangeConstraint is used to check that a given number value is between some minimum and maximum.
338
// Values are compared as integers if the compared and specified values are integers.
339
// Otherwise, numbers are always compared as floating point numbers.
340
type RangeConstraint struct {
341
	isIgnored         bool
342
	groups            []string
343
	code              string
344
	messageTemplate   string
345
	messageParameters validation.TemplateParameterList
346
	min               generic.Number
347
	max               generic.Number
348
}
349
350
// IsBetweenIntegers checks that the number (integer or float) is between specified minimum and
351
// maximum integer values. Values are compared as integers if the compared and specified
352
// values are integers. Otherwise, numbers are always compared as floating point numbers.
353
func IsBetweenIntegers(min, max int64) RangeConstraint {
354 1
	return RangeConstraint{
355
		min:             generic.NewNumberFromInt(min),
356
		max:             generic.NewNumberFromInt(max),
357
		code:            code.NotInRange,
358
		messageTemplate: message.Templates[code.NotInRange],
359
	}
360
}
361
362
// IsBetweenFloats checks that the number (integer or float) is between specified minimum and
363
// maximum float values. Values are compared as integers if the compared and specified
364
// values are integers. Otherwise, numbers are always compared as floating point numbers.
365
func IsBetweenFloats(min, max float64) RangeConstraint {
366 1
	return RangeConstraint{
367
		min:             generic.NewNumberFromFloat(min),
368
		max:             generic.NewNumberFromFloat(max),
369
		code:            code.NotInRange,
370
		messageTemplate: message.Templates[code.NotInRange],
371
	}
372
}
373
374
// SetUp returns an error if min is greater than or equal to max.
375
func (c RangeConstraint) SetUp() error {
376 1
	if c.min.IsGreaterThan(c.max) || c.min.IsEqualTo(c.max) {
377 1
		return errInvalidRange
378
	}
379
380 1
	return nil
381
}
382
383
// Name is the constraint name.
384
func (c RangeConstraint) Name() string {
385 1
	return "RangeConstraint"
386
}
387
388
// Code overrides default code for produced violation.
389
func (c RangeConstraint) Code(code string) RangeConstraint {
390 1
	c.code = code
391 1
	return c
392
}
393
394
// Message sets the violation message template. You can set custom template parameters
395
// for injecting its values into the final message. Also, you can use default parameters:
396
//
397
//  {{ max }} - the upper limit;
398
//  {{ min }} - the lower limit;
399
//  {{ value }} - the current (invalid) value.
400
func (c RangeConstraint) Message(template string, parameters ...validation.TemplateParameter) RangeConstraint {
401 1
	c.messageTemplate = template
402 1
	c.messageParameters = parameters
403 1
	return c
404
}
405
406
// When enables conditional validation of this constraint. If the expression evaluates to false,
407
// then the constraint will be ignored.
408
func (c RangeConstraint) When(condition bool) RangeConstraint {
409 1
	c.isIgnored = !condition
410 1
	return c
411
}
412
413
// WhenGroups enables conditional validation of the constraint by using the validation groups.
414
func (c RangeConstraint) WhenGroups(groups ...string) RangeConstraint {
415 1
	c.groups = groups
416 1
	return c
417
}
418
419
func (c RangeConstraint) ValidateNumber(value generic.Number, scope validation.Scope) error {
420 1
	if c.isIgnored || scope.IsIgnored(c.groups...) {
421 1
		return nil
422
	}
423 1
	if value.IsLessThan(c.min) || value.IsGreaterThan(c.max) {
424 1
		return c.newViolation(value, scope)
425
	}
426
427 1
	return nil
428
}
429
430
func (c RangeConstraint) newViolation(value generic.Number, scope validation.Scope) error {
431 1
	return scope.BuildViolation(c.code, c.messageTemplate).
432
		SetParameters(
433
			c.messageParameters.Prepend(
434
				validation.TemplateParameter{Key: "{{ min }}", Value: c.min.String()},
435
				validation.TemplateParameter{Key: "{{ max }}", Value: c.max.String()},
436
				validation.TemplateParameter{Key: "{{ value }}", Value: value.String()},
437
			)...,
438
		).
439
		CreateViolation()
440
}
441
442
// StringComparisonConstraint is used to compare strings.
443
type StringComparisonConstraint struct {
444
	isIgnored         bool
445
	groups            []string
446
	code              string
447
	messageTemplate   string
448
	messageParameters validation.TemplateParameterList
449
	comparedValue     string
450
	isValid           func(value string) bool
451
}
452
453
// IsEqualToString checks that the string value is equal to the specified string value.
454
func IsEqualToString(value string) StringComparisonConstraint {
455 1
	return StringComparisonConstraint{
456
		code:            code.Equal,
457
		messageTemplate: message.Templates[code.Equal],
458
		comparedValue:   value,
459
		isValid: func(actualValue string) bool {
460 1
			return value == actualValue
461
		},
462
	}
463
}
464
465
// IsNotEqualToString checks that the string value is not equal to the specified string value.
466
func IsNotEqualToString(value string) StringComparisonConstraint {
467 1
	return StringComparisonConstraint{
468
		code:            code.NotEqual,
469
		messageTemplate: message.Templates[code.NotEqual],
470
		comparedValue:   value,
471
		isValid: func(actualValue string) bool {
472 1
			return value != actualValue
473
		},
474
	}
475
}
476
477
// SetUp always returns no error.
478
func (c StringComparisonConstraint) SetUp() error {
479 1
	return nil
480
}
481
482
// Name is the constraint name.
483
func (c StringComparisonConstraint) Name() string {
484
	return "StringComparisonConstraint"
485
}
486
487
// Code overrides default code for produced violation.
488
func (c StringComparisonConstraint) Code(code string) StringComparisonConstraint {
489 1
	c.code = code
490 1
	return c
491
}
492
493
// Message sets the violation message template. You can set custom template parameters
494
// for injecting its values into the final message. Also, you can use default parameters:
495
//
496
//  {{ comparedValue }} - the expected value;
497
//  {{ value }} - the current (invalid) value.
498
//
499
// All string values are quoted strings.
500
func (c StringComparisonConstraint) Message(
501
	template string,
502
	parameters ...validation.TemplateParameter,
503
) StringComparisonConstraint {
504 1
	c.messageTemplate = template
505 1
	c.messageParameters = parameters
506 1
	return c
507
}
508
509
// When enables conditional validation of this constraint. If the expression evaluates to false,
510
// then the constraint will be ignored.
511
func (c StringComparisonConstraint) When(condition bool) StringComparisonConstraint {
512 1
	c.isIgnored = !condition
513 1
	return c
514
}
515
516
// WhenGroups enables conditional validation of the constraint by using the validation groups.
517
func (c StringComparisonConstraint) WhenGroups(groups ...string) StringComparisonConstraint {
518 1
	c.groups = groups
519 1
	return c
520
}
521
522
func (c StringComparisonConstraint) ValidateString(value *string, scope validation.Scope) error {
523 1
	if c.isIgnored || scope.IsIgnored(c.groups...) || value == nil || c.isValid(*value) {
524 1
		return nil
525
	}
526
527 1
	return scope.BuildViolation(c.code, c.messageTemplate).
528
		SetParameters(
529
			c.messageParameters.Prepend(
530
				validation.TemplateParameter{Key: "{{ comparedValue }}", Value: strconv.Quote(c.comparedValue)},
531
				validation.TemplateParameter{Key: "{{ value }}", Value: strconv.Quote(*value)},
532
			)...,
533
		).
534
		CreateViolation()
535
}
536
537
// TimeComparisonConstraint is used to compare time values.
538
type TimeComparisonConstraint struct {
539
	isIgnored         bool
540
	groups            []string
541
	code              string
542
	messageTemplate   string
543
	messageParameters validation.TemplateParameterList
544
	comparedValue     time.Time
545
	layout            string
546
	isValid           func(value time.Time) bool
547
}
548
549
// IsEarlierThan checks that the given time is earlier than the specified value.
550
func IsEarlierThan(value time.Time) TimeComparisonConstraint {
551 1
	return TimeComparisonConstraint{
552
		code:            code.TooLate,
553
		messageTemplate: message.Templates[code.TooLate],
554
		comparedValue:   value,
555
		layout:          time.RFC3339,
556
		isValid: func(actualValue time.Time) bool {
557 1
			return actualValue.Before(value)
558
		},
559
	}
560
}
561
562
// IsEarlierThanOrEqual checks that the given time is earlier or equal to the specified value.
563
func IsEarlierThanOrEqual(value time.Time) TimeComparisonConstraint {
564 1
	return TimeComparisonConstraint{
565
		code:            code.TooLateOrEqual,
566
		messageTemplate: message.Templates[code.TooLateOrEqual],
567
		comparedValue:   value,
568
		layout:          time.RFC3339,
569
		isValid: func(actualValue time.Time) bool {
570 1
			return actualValue.Before(value) || actualValue.Equal(value)
571
		},
572
	}
573
}
574
575
// IsLaterThan checks that the given time is later than the specified value.
576
func IsLaterThan(value time.Time) TimeComparisonConstraint {
577 1
	return TimeComparisonConstraint{
578
		code:            code.TooEarly,
579
		messageTemplate: message.Templates[code.TooEarly],
580
		comparedValue:   value,
581
		layout:          time.RFC3339,
582
		isValid: func(actualValue time.Time) bool {
583 1
			return actualValue.After(value)
584
		},
585
	}
586
}
587
588
// IsLaterThanOrEqual checks that the given time is later or equal to the specified value.
589
func IsLaterThanOrEqual(value time.Time) TimeComparisonConstraint {
590 1
	return TimeComparisonConstraint{
591
		code:            code.TooEarlyOrEqual,
592
		messageTemplate: message.Templates[code.TooEarlyOrEqual],
593
		comparedValue:   value,
594
		layout:          time.RFC3339,
595
		isValid: func(actualValue time.Time) bool {
596 1
			return actualValue.After(value) || actualValue.Equal(value)
597
		},
598
	}
599
}
600
601
// SetUp always returns no error.
602
func (c TimeComparisonConstraint) SetUp() error {
603 1
	return nil
604
}
605
606
// Name is the constraint name.
607
func (c TimeComparisonConstraint) Name() string {
608
	return "TimeComparisonConstraint"
609
}
610
611
// Code overrides default code for produced violation.
612
func (c TimeComparisonConstraint) Code(code string) TimeComparisonConstraint {
613 1
	c.code = code
614 1
	return c
615
}
616
617
// Message sets the violation message template. You can set custom template parameters
618
// for injecting its values into the final message. Also, you can use default parameters:
619
//
620
//  {{ comparedValue }} - the expected value;
621
//  {{ value }} - the current (invalid) value.
622
//
623
// All values are formatted by the layout that can be defined by the Layout method.
624
// Default layout is time.RFC3339.
625
func (c TimeComparisonConstraint) Message(
626
	template string,
627
	parameters ...validation.TemplateParameter,
628
) TimeComparisonConstraint {
629 1
	c.messageTemplate = template
630 1
	c.messageParameters = parameters
631 1
	return c
632
}
633
634
// Layout can be used to set the layout that is used to format time values.
635
func (c TimeComparisonConstraint) Layout(layout string) TimeComparisonConstraint {
636 1
	c.layout = layout
637 1
	return c
638
}
639
640
// When enables conditional validation of this constraint. If the expression evaluates to false,
641
// then the constraint will be ignored.
642
func (c TimeComparisonConstraint) When(condition bool) TimeComparisonConstraint {
643 1
	c.isIgnored = !condition
644 1
	return c
645
}
646
647
// WhenGroups enables conditional validation of the constraint by using the validation groups.
648
func (c TimeComparisonConstraint) WhenGroups(groups ...string) TimeComparisonConstraint {
649 1
	c.groups = groups
650 1
	return c
651
}
652
653
func (c TimeComparisonConstraint) ValidateTime(value *time.Time, scope validation.Scope) error {
654 1
	if c.isIgnored || scope.IsIgnored(c.groups...) || value == nil || c.isValid(*value) {
655 1
		return nil
656
	}
657
658 1
	return scope.BuildViolation(c.code, c.messageTemplate).
659
		SetParameters(
660
			c.messageParameters.Prepend(
661
				validation.TemplateParameter{Key: "{{ comparedValue }}", Value: c.comparedValue.Format(c.layout)},
662
				validation.TemplateParameter{Key: "{{ value }}", Value: value.Format(c.layout)},
663
			)...,
664
		).
665
		CreateViolation()
666
}
667
668
// TimeRangeConstraint is used to check that a given time value is between some minimum and maximum.
669
type TimeRangeConstraint struct {
670
	isIgnored         bool
671
	groups            []string
672
	code              string
673
	messageTemplate   string
674
	messageParameters validation.TemplateParameterList
675
	layout            string
676
	min               time.Time
677
	max               time.Time
678
}
679
680
// IsBetweenTime checks that the time is between specified minimum and maximum time values.
681
func IsBetweenTime(min, max time.Time) TimeRangeConstraint {
682 1
	return TimeRangeConstraint{
683
		code:            code.NotInRange,
684
		messageTemplate: message.Templates[code.NotInRange],
685
		layout:          time.RFC3339,
686
		min:             min,
687
		max:             max,
688
	}
689
}
690
691
// SetUp returns an error if min is greater than or equal to max.
692
func (c TimeRangeConstraint) SetUp() error {
693 1
	if c.min.After(c.max) || c.min.Equal(c.max) {
694 1
		return errInvalidRange
695
	}
696
697 1
	return nil
698
}
699
700
// Name is the constraint name.
701
func (c TimeRangeConstraint) Name() string {
702 1
	return "TimeRangeConstraint"
703
}
704
705
// Code overrides default code for produced violation.
706
func (c TimeRangeConstraint) Code(code string) TimeRangeConstraint {
707 1
	c.code = code
708 1
	return c
709
}
710
711
// Message sets the violation message template. You can set custom template parameters
712
// for injecting its values into the final message. Also, you can use default parameters:
713
//
714
//  {{ max }} - the upper limit;
715
//  {{ min }} - the lower limit;
716
//  {{ value }} - the current (invalid) value.
717
//
718
// All values are formatted by the layout that can be defined by the Layout method.
719
// Default layout is time.RFC3339.
720
func (c TimeRangeConstraint) Message(template string, parameters ...validation.TemplateParameter) TimeRangeConstraint {
721 1
	c.messageTemplate = template
722 1
	c.messageParameters = parameters
723 1
	return c
724
}
725
726
// When enables conditional validation of this constraint. If the expression evaluates to false,
727
// then the constraint will be ignored.
728
func (c TimeRangeConstraint) When(condition bool) TimeRangeConstraint {
729 1
	c.isIgnored = !condition
730 1
	return c
731
}
732
733
// WhenGroups enables conditional validation of the constraint by using the validation groups.
734
func (c TimeRangeConstraint) WhenGroups(groups ...string) TimeRangeConstraint {
735 1
	c.groups = groups
736 1
	return c
737
}
738
739
// Layout can be used to set the layout that is used to format time values.
740
func (c TimeRangeConstraint) Layout(layout string) TimeRangeConstraint {
741 1
	c.layout = layout
742 1
	return c
743
}
744
745
func (c TimeRangeConstraint) ValidateTime(value *time.Time, scope validation.Scope) error {
746 1
	if c.isIgnored || scope.IsIgnored(c.groups...) || value == nil {
747 1
		return nil
748
	}
749 1
	if value.Before(c.min) || value.After(c.max) {
750 1
		return c.newViolation(value, scope)
751
	}
752
753 1
	return nil
754
}
755
756
func (c TimeRangeConstraint) newViolation(value *time.Time, scope validation.Scope) validation.Violation {
757 1
	return scope.BuildViolation(c.code, c.messageTemplate).
758
		SetParameters(
759
			c.messageParameters.Prepend(
760
				validation.TemplateParameter{Key: "{{ min }}", Value: c.min.Format(c.layout)},
761
				validation.TemplateParameter{Key: "{{ max }}", Value: c.max.Format(c.layout)},
762
				validation.TemplateParameter{Key: "{{ value }}", Value: value.Format(c.layout)},
763
			)...,
764
		).
765
		CreateViolation()
766
}
767
768
// UniqueConstraint is used to check that all elements of the given collection are unique.
769
type UniqueConstraint struct {
770
	isIgnored         bool
771
	groups            []string
772
	code              string
773
	messageTemplate   string
774
	messageParameters validation.TemplateParameterList
775
}
776
777
// HasUniqueValues checks that all elements of the given collection are unique
778
// (none of them is present more than once).
779
func HasUniqueValues() UniqueConstraint {
780 1
	return UniqueConstraint{
781
		code:            code.NotUnique,
782
		messageTemplate: message.Templates[code.NotUnique],
783
	}
784
}
785
786
// SetUp always returns no error.
787
func (c UniqueConstraint) SetUp() error {
788 1
	return nil
789
}
790
791
// Name is the constraint name.
792
func (c UniqueConstraint) Name() string {
793
	return "UniqueConstraint"
794
}
795
796
// Code overrides default code for produced violation.
797
func (c UniqueConstraint) Code(code string) UniqueConstraint {
798 1
	c.code = code
799 1
	return c
800
}
801
802
// Message sets the violation message template. You can set custom template parameters
803
// for injecting its values into the final message.
804
func (c UniqueConstraint) Message(template string, parameters ...validation.TemplateParameter) UniqueConstraint {
805 1
	c.messageTemplate = template
806 1
	c.messageParameters = parameters
807 1
	return c
808
}
809
810
// When enables conditional validation of this constraint. If the expression evaluates to false,
811
// then the constraint will be ignored.
812
func (c UniqueConstraint) When(condition bool) UniqueConstraint {
813 1
	c.isIgnored = !condition
814 1
	return c
815
}
816
817
// WhenGroups enables conditional validation of the constraint by using the validation groups.
818
func (c UniqueConstraint) WhenGroups(groups ...string) UniqueConstraint {
819 1
	c.groups = groups
820 1
	return c
821
}
822
823
func (c UniqueConstraint) ValidateStrings(values []string, scope validation.Scope) error {
824 1
	if c.isIgnored || scope.IsIgnored(c.groups...) || is.UniqueStrings(values) {
825 1
		return nil
826
	}
827
828 1
	return scope.BuildViolation(c.code, c.messageTemplate).
829
		SetParameters(c.messageParameters...).
830
		CreateViolation()
831
}
832