Passed
Pull Request — main (#50)
by Igor
02:03
created

it.TimeComparisonConstraint.Code   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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