Passed
Push — main ( 0f6dda...8fe6b9 )
by Igor
02:04 queued 11s
created

it.IsNegativeOrZero   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
dl 0
loc 9
rs 10
c 0
b 0
f 0
ccs 3
cts 3
cp 1
crap 3
nop 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
// Message sets the violation message template. You can set custom template parameters
352
// for injecting its values into the final message. Also, you can use default parameters:
353
//
354
//  {{ comparedValue }} - the expected value;
355
//  {{ value }} - the current (invalid) value.
356
func (c NumberComparisonConstraint) Message(
357
	template string,
358
	parameters ...validation.TemplateParameter,
359
) NumberComparisonConstraint {
360 1
	c.messageTemplate = template
361 1
	c.messageParameters = parameters
362 1
	return c
363
}
364
365
// When enables conditional validation of this constraint. If the expression evaluates to false,
366
// then the constraint will be ignored.
367
func (c NumberComparisonConstraint) When(condition bool) NumberComparisonConstraint {
368 1
	c.isIgnored = !condition
369 1
	return c
370
}
371
372
func (c NumberComparisonConstraint) ValidateNumber(value generic.Number, scope validation.Scope) error {
373 1
	if c.isIgnored || value.IsNil() || c.isValid(value) {
374 1
		return nil
375
	}
376
377 1
	return scope.BuildViolation(c.code, c.messageTemplate).
378
		SetParameters(
379
			c.messageParameters.Prepend(
380
				validation.TemplateParameter{Key: "{{ comparedValue }}", Value: c.comparedValue},
381
				validation.TemplateParameter{Key: "{{ value }}", Value: value.String()},
382
			)...,
383
		).
384
		CreateViolation()
385
}
386
387
// RangeConstraint is used to check that a given number value is between some minimum and maximum.
388
// Values are compared as integers if the compared and specified values are integers.
389
// Otherwise, numbers are always compared as floating point numbers.
390
type RangeConstraint struct {
391
	isIgnored         bool
392
	messageTemplate   string
393
	messageParameters validation.TemplateParameterList
394
	min               generic.Number
395
	max               generic.Number
396
}
397
398
// IsBetweenIntegers checks that the number (integer or float) is between specified minimum and
399
// maximum integer values. Values are compared as integers if the compared and specified
400
// values are integers. Otherwise, numbers are always compared as floating point numbers.
401
//
402
// Example
403
//	v := 1
404
//	err := validator.ValidateNumber(&v, it.IsBetweenIntegers(10, 20))
405
func IsBetweenIntegers(min, max int64) RangeConstraint {
406 1
	return RangeConstraint{
407
		min:             generic.NewNumberFromInt(min),
408
		max:             generic.NewNumberFromInt(max),
409
		messageTemplate: message.NotInRange,
410
	}
411
}
412
413
// IsBetweenFloats checks that the number (integer or float) is between specified minimum and
414
// maximum float values. Values are compared as integers if the compared and specified
415
// values are integers. Otherwise, numbers are always compared as floating point numbers.
416
//
417
// Example
418
//	v := 1.1
419
//	err := validator.ValidateNumber(&v, it.IsBetweenFloats(10.111, 20.222))
420
func IsBetweenFloats(min, max float64) RangeConstraint {
421 1
	return RangeConstraint{
422
		min:             generic.NewNumberFromFloat(min),
423
		max:             generic.NewNumberFromFloat(max),
424
		messageTemplate: message.NotInRange,
425
	}
426
}
427
428
// SetUp returns an error if min is greater than or equal to max.
429
func (c RangeConstraint) SetUp() error {
430 1
	if c.min.IsGreaterThan(c.max) || c.min.IsEqualTo(c.max) {
431 1
		return errInvalidRange
432
	}
433
434 1
	return nil
435
}
436
437
// Name is the constraint name.
438
func (c RangeConstraint) Name() string {
439 1
	return "RangeConstraint"
440
}
441
442
// Message sets the violation message template. You can set custom template parameters
443
// for injecting its values into the final message. Also, you can use default parameters:
444
//
445
//  {{ max }} - the upper limit;
446
//  {{ min }} - the lower limit;
447
//  {{ value }} - the current (invalid) value.
448
func (c RangeConstraint) Message(template string, parameters ...validation.TemplateParameter) RangeConstraint {
449 1
	c.messageTemplate = template
450 1
	c.messageParameters = parameters
451 1
	return c
452
}
453
454
// When enables conditional validation of this constraint. If the expression evaluates to false,
455
// then the constraint will be ignored.
456
func (c RangeConstraint) When(condition bool) RangeConstraint {
457 1
	c.isIgnored = !condition
458 1
	return c
459
}
460
461
func (c RangeConstraint) ValidateNumber(value generic.Number, scope validation.Scope) error {
462 1
	if c.isIgnored {
463 1
		return nil
464
	}
465 1
	if value.IsLessThan(c.min) || value.IsGreaterThan(c.max) {
466 1
		return c.newViolation(value, scope)
467
	}
468
469 1
	return nil
470
}
471
472
func (c RangeConstraint) newViolation(value generic.Number, scope validation.Scope) error {
473 1
	return scope.BuildViolation(code.NotInRange, c.messageTemplate).
474
		SetParameters(
475
			c.messageParameters.Prepend(
476
				validation.TemplateParameter{Key: "{{ min }}", Value: c.min.String()},
477
				validation.TemplateParameter{Key: "{{ max }}", Value: c.max.String()},
478
				validation.TemplateParameter{Key: "{{ value }}", Value: value.String()},
479
			)...,
480
		).
481
		CreateViolation()
482
}
483
484
// StringComparisonConstraint is used to compare strings.
485
type StringComparisonConstraint struct {
486
	isIgnored         bool
487
	code              string
488
	messageTemplate   string
489
	messageParameters validation.TemplateParameterList
490
	comparedValue     string
491
	isValid           func(value string) bool
492
}
493
494
// IsEqualToString checks that the string value is equal to the specified string value.
495
//
496
// Example
497
//  v := "actual"
498
//  err := validator.ValidateString(&v, it.IsEqualToString("expected"))
499
func IsEqualToString(value string) StringComparisonConstraint {
500 1
	return StringComparisonConstraint{
501
		code:            code.Equal,
502
		messageTemplate: message.Equal,
503
		comparedValue:   value,
504
		isValid: func(actualValue string) bool {
505 1
			return value == actualValue
506
		},
507
	}
508
}
509
510
// IsNotEqualToString checks that the string value is not equal to the specified string value.
511
//
512
// Example
513
//  v := "expected"
514
//  err := validator.ValidateString(&v, it.IsNotEqualToString("expected"))
515
func IsNotEqualToString(value string) StringComparisonConstraint {
516 1
	return StringComparisonConstraint{
517
		code:            code.NotEqual,
518
		messageTemplate: message.NotEqual,
519
		comparedValue:   value,
520
		isValid: func(actualValue string) bool {
521 1
			return value != actualValue
522
		},
523
	}
524
}
525
526
// SetUp always returns no error.
527
func (c StringComparisonConstraint) SetUp() error {
528 1
	return nil
529
}
530
531
// Name is the constraint name.
532
func (c StringComparisonConstraint) Name() string {
533
	return "StringComparisonConstraint"
534
}
535
536
// Message sets the violation message template. You can set custom template parameters
537
// for injecting its values into the final message. Also, you can use default parameters:
538
//
539
//  {{ comparedValue }} - the expected value;
540
//  {{ value }} - the current (invalid) value.
541
//
542
// All string values are quoted strings.
543
func (c StringComparisonConstraint) Message(
544
	template string,
545
	parameters ...validation.TemplateParameter,
546
) StringComparisonConstraint {
547 1
	c.messageTemplate = template
548 1
	c.messageParameters = parameters
549 1
	return c
550
}
551
552
// When enables conditional validation of this constraint. If the expression evaluates to false,
553
// then the constraint will be ignored.
554
func (c StringComparisonConstraint) When(condition bool) StringComparisonConstraint {
555 1
	c.isIgnored = !condition
556 1
	return c
557
}
558
559
func (c StringComparisonConstraint) ValidateString(value *string, scope validation.Scope) error {
560 1
	if c.isIgnored || value == nil || c.isValid(*value) {
561 1
		return nil
562
	}
563
564 1
	return scope.BuildViolation(c.code, c.messageTemplate).
565
		SetParameters(
566
			c.messageParameters.Prepend(
567
				validation.TemplateParameter{Key: "{{ comparedValue }}", Value: strconv.Quote(c.comparedValue)},
568
				validation.TemplateParameter{Key: "{{ value }}", Value: strconv.Quote(*value)},
569
			)...,
570
		).
571
		CreateViolation()
572
}
573
574
// TimeComparisonConstraint is used to compare time values.
575
type TimeComparisonConstraint struct {
576
	isIgnored         bool
577
	code              string
578
	messageTemplate   string
579
	messageParameters validation.TemplateParameterList
580
	comparedValue     time.Time
581
	layout            string
582
	isValid           func(value time.Time) bool
583
}
584
585
// IsEarlierThan checks that the given time is earlier than the specified value.
586
//
587
// Example
588
//  t := time.Now()
589
//  err := validator.ValidateTime(&t, it.IsEarlierThan(time.Now().Add(time.Hour)))
590
func IsEarlierThan(value time.Time) TimeComparisonConstraint {
591 1
	return TimeComparisonConstraint{
592
		code:            code.TooLate,
593
		messageTemplate: message.TooLate,
594
		comparedValue:   value,
595
		layout:          time.RFC3339,
596
		isValid: func(actualValue time.Time) bool {
597 1
			return actualValue.Before(value)
598
		},
599
	}
600
}
601
602
// IsEarlierThanOrEqual checks that the given time is earlier or equal to the specified value.
603
//
604
// Example
605
//  t := time.Now()
606
//  err := validator.ValidateTime(&t, it.IsEarlierThanOrEqual(time.Now().Add(time.Hour)))
607
func IsEarlierThanOrEqual(value time.Time) TimeComparisonConstraint {
608 1
	return TimeComparisonConstraint{
609
		code:            code.TooLateOrEqual,
610
		messageTemplate: message.TooLateOrEqual,
611
		comparedValue:   value,
612
		layout:          time.RFC3339,
613
		isValid: func(actualValue time.Time) bool {
614 1
			return actualValue.Before(value) || actualValue.Equal(value)
615
		},
616
	}
617
}
618
619
// IsLaterThan checks that the given time is later than the specified value.
620
//
621
// Example
622
//  t := time.Now()
623
//  err := validator.ValidateTime(&t, it.IsLaterThan(time.Now().Sub(time.Hour)))
624
func IsLaterThan(value time.Time) TimeComparisonConstraint {
625 1
	return TimeComparisonConstraint{
626
		code:            code.TooEarly,
627
		messageTemplate: message.TooEarly,
628
		comparedValue:   value,
629
		layout:          time.RFC3339,
630
		isValid: func(actualValue time.Time) bool {
631 1
			return actualValue.After(value)
632
		},
633
	}
634
}
635
636
// IsLaterThanOrEqual checks that the given time is later or equal to the specified value.
637
//
638
// Example
639
//  t := time.Now()
640
//  err := validator.ValidateTime(&t, it.IsLaterThanOrEqual(time.Now().Sub(time.Hour)))
641
func IsLaterThanOrEqual(value time.Time) TimeComparisonConstraint {
642 1
	return TimeComparisonConstraint{
643
		code:            code.TooEarlyOrEqual,
644
		messageTemplate: message.TooEarlyOrEqual,
645
		comparedValue:   value,
646
		layout:          time.RFC3339,
647
		isValid: func(actualValue time.Time) bool {
648 1
			return actualValue.After(value) || actualValue.Equal(value)
649
		},
650
	}
651
}
652
653
// SetUp always returns no error.
654
func (c TimeComparisonConstraint) SetUp() error {
655 1
	return nil
656
}
657
658
// Name is the constraint name.
659
func (c TimeComparisonConstraint) Name() string {
660
	return "TimeComparisonConstraint"
661
}
662
663
// Message sets the violation message template. You can set custom template parameters
664
// for injecting its values into the final message. Also, you can use default parameters:
665
//
666
//  {{ comparedValue }} - the expected value;
667
//  {{ value }} - the current (invalid) value.
668
//
669
// All values are formatted by the layout that can be defined by the Layout method.
670
// Default layout is time.RFC3339.
671
func (c TimeComparisonConstraint) Message(
672
	template string,
673
	parameters ...validation.TemplateParameter,
674
) TimeComparisonConstraint {
675 1
	c.messageTemplate = template
676 1
	c.messageParameters = parameters
677 1
	return c
678
}
679
680
// Layout can be used to set the layout that is used to format time values.
681
func (c TimeComparisonConstraint) Layout(layout string) TimeComparisonConstraint {
682 1
	c.layout = layout
683 1
	return c
684
}
685
686
// When enables conditional validation of this constraint. If the expression evaluates to false,
687
// then the constraint will be ignored.
688
func (c TimeComparisonConstraint) When(condition bool) TimeComparisonConstraint {
689 1
	c.isIgnored = !condition
690 1
	return c
691
}
692
693
func (c TimeComparisonConstraint) ValidateTime(value *time.Time, scope validation.Scope) error {
694 1
	if c.isIgnored || value == nil || c.isValid(*value) {
695 1
		return nil
696
	}
697
698 1
	return scope.BuildViolation(c.code, c.messageTemplate).
699
		SetParameters(
700
			c.messageParameters.Prepend(
701
				validation.TemplateParameter{Key: "{{ comparedValue }}", Value: c.comparedValue.Format(c.layout)},
702
				validation.TemplateParameter{Key: "{{ value }}", Value: value.Format(c.layout)},
703
			)...,
704
		).
705
		CreateViolation()
706
}
707
708
// TimeRangeConstraint is used to check that a given time value is between some minimum and maximum.
709
type TimeRangeConstraint struct {
710
	isIgnored         bool
711
	messageTemplate   string
712
	messageParameters validation.TemplateParameterList
713
	layout            string
714
	min               time.Time
715
	max               time.Time
716
}
717
718
// IsBetweenTime checks that the time is between specified minimum and maximum time values.
719
//
720
// Example
721
//	t := time.Now()
722
//	err := validator.ValidateTime(&t, it.IsBetweenTime(time.Now().Add(time.Hour), time.Now().Add(2*time.Hour)))
723
func IsBetweenTime(min, max time.Time) TimeRangeConstraint {
724 1
	return TimeRangeConstraint{
725
		messageTemplate: message.NotInRange,
726
		layout:          time.RFC3339,
727
		min:             min,
728
		max:             max,
729
	}
730
}
731
732
// SetUp returns an error if min is greater than or equal to max.
733
func (c TimeRangeConstraint) SetUp() error {
734 1
	if c.min.After(c.max) || c.min.Equal(c.max) {
735 1
		return errInvalidRange
736
	}
737
738 1
	return nil
739
}
740
741
// Name is the constraint name.
742
func (c TimeRangeConstraint) Name() string {
743 1
	return "TimeRangeConstraint"
744
}
745
746
// Message sets the violation message template. You can set custom template parameters
747
// for injecting its values into the final message. Also, you can use default parameters:
748
//
749
//  {{ max }} - the upper limit;
750
//  {{ min }} - the lower limit;
751
//  {{ value }} - the current (invalid) value.
752
//
753
// All values are formatted by the layout that can be defined by the Layout method.
754
// Default layout is time.RFC3339.
755
func (c TimeRangeConstraint) Message(template string, parameters ...validation.TemplateParameter) TimeRangeConstraint {
756 1
	c.messageTemplate = template
757 1
	c.messageParameters = parameters
758 1
	return c
759
}
760
761
// When enables conditional validation of this constraint. If the expression evaluates to false,
762
// then the constraint will be ignored.
763
func (c TimeRangeConstraint) When(condition bool) TimeRangeConstraint {
764 1
	c.isIgnored = !condition
765 1
	return c
766
}
767
768
// Layout can be used to set the layout that is used to format time values.
769
func (c TimeRangeConstraint) Layout(layout string) TimeRangeConstraint {
770 1
	c.layout = layout
771 1
	return c
772
}
773
774
func (c TimeRangeConstraint) ValidateTime(value *time.Time, scope validation.Scope) error {
775 1
	if c.isIgnored || value == nil {
776 1
		return nil
777
	}
778 1
	if value.Before(c.min) || value.After(c.max) {
779 1
		return c.newViolation(value, scope)
780
	}
781
782 1
	return nil
783
}
784
785
func (c TimeRangeConstraint) newViolation(value *time.Time, scope validation.Scope) validation.Violation {
786 1
	return scope.BuildViolation(code.NotInRange, c.messageTemplate).
787
		SetParameters(
788
			c.messageParameters.Prepend(
789
				validation.TemplateParameter{Key: "{{ min }}", Value: c.min.Format(c.layout)},
790
				validation.TemplateParameter{Key: "{{ max }}", Value: c.max.Format(c.layout)},
791
				validation.TemplateParameter{Key: "{{ value }}", Value: value.Format(c.layout)},
792
			)...,
793
		).
794
		CreateViolation()
795
}
796