Test Setup Failed
Pull Request — main (#57)
by Igor
01:27
created

it.StringComparisonConstraint.ValidateString   A

Complexity

Conditions 4

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 4

Importance

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