Code

< 40 %
40-60 %
> 60 %
1
package validation
2
3
import (
4
	"context"
5
	"time"
6
)
7
8
// Numeric is used as a type parameter for numeric values.
9
type Numeric interface {
10
	~float32 | ~float64 |
11
		~int | ~int8 | ~int16 | ~int32 | ~int64 |
12
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
13
}
14
15
// Constraint is a generic interface for client-side typed constraints.
16
type Constraint[T any] interface {
17
	Validate(ctx context.Context, validator *Validator, v T) error
18
}
19
20
// NilConstraint is used for a special cases to check a value for nil.
21
type NilConstraint interface {
22
	ValidateNil(ctx context.Context, validator *Validator, isNil bool) error
23
}
24
25
// BoolConstraint is used to build constraints for boolean values validation.
26
type BoolConstraint interface {
27
	ValidateBool(ctx context.Context, validator *Validator, value *bool) error
28
}
29
30
// NumberConstraint is used to build constraints for numeric values validation.
31
type NumberConstraint[T Numeric] interface {
32
	ValidateNumber(ctx context.Context, validator *Validator, value *T) error
33
}
34
35
// StringConstraint is used to build constraints for string values validation.
36
type StringConstraint interface {
37
	ValidateString(ctx context.Context, validator *Validator, value *string) error
38
}
39
40
// ComparableConstraint is used to build constraints for generic comparable value validation.
41
type ComparableConstraint[T comparable] interface {
42
	ValidateComparable(ctx context.Context, validator *Validator, value *T) error
43
}
44
45
// ComparablesConstraint is used to build constraints for generic comparable values validation.
46
type ComparablesConstraint[T comparable] interface {
47
	ValidateComparables(ctx context.Context, validator *Validator, values []T) error
48
}
49
50
// CountableConstraint is used to build constraints for simpler validation of iterable elements count.
51
type CountableConstraint interface {
52
	ValidateCountable(ctx context.Context, validator *Validator, count int) error
53
}
54
55
// TimeConstraint is used to build constraints for date/time validation.
56
type TimeConstraint interface {
57
	ValidateTime(ctx context.Context, validator *Validator, value *time.Time) error
58
}
59
60
// StringFuncConstraint can be used to create constraints for validating string values
61
// based on function with signature func(string) bool.
62
type StringFuncConstraint struct {
63
	isIgnored         bool
64
	isValid           func(string) bool
65
	groups            []string
66
	err               error
67
	messageTemplate   string
68
	messageParameters TemplateParameterList
69
}
70
71
// OfStringBy creates a new string constraint from a function with signature func(string) bool.
72
func OfStringBy(isValid func(string) bool) StringFuncConstraint {
73
	return StringFuncConstraint{
74
		isValid:         isValid,
75
		err:             ErrNotValid,
76
		messageTemplate: ErrNotValid.Message(),
77
	}
78
}
79
80
// WithError overrides default error for produced violation.
81
func (c StringFuncConstraint) WithError(err error) StringFuncConstraint {
82
	c.err = err
83
	return c
84
}
85
86
// WithMessage sets the violation message template. You can set custom template parameters
87
// for injecting its values into the final message. Also, you can use default parameters:
88
//
89
//	{{ value }} - the current (invalid) value.
90
func (c StringFuncConstraint) WithMessage(template string, parameters ...TemplateParameter) StringFuncConstraint {
91
	c.messageTemplate = template
92
	c.messageParameters = parameters
93 1
	return c
94
}
95
96
// When enables conditional validation of this constraint. If the expression evaluates to false,
97
// then the constraint will be ignored.
98
func (c StringFuncConstraint) When(condition bool) StringFuncConstraint {
99
	c.isIgnored = !condition
100 1
	return c
101 1
}
102
103 1
// WhenGroups enables conditional validation of the constraint by using the validation groups.
104 1
func (c StringFuncConstraint) WhenGroups(groups ...string) StringFuncConstraint {
105
	c.groups = groups
106 1
	return c
107 1
}
108
109
func (c StringFuncConstraint) ValidateString(ctx context.Context, validator *Validator, value *string) error {
110 1
	if c.isIgnored || validator.IsIgnoredForGroups(c.groups...) || value == nil || *value == "" || c.isValid(*value) {
111
		return nil
112
	}
113
114
	return validator.BuildViolation(ctx, c.err, c.messageTemplate).
115 1
		WithParameters(
116
			c.messageParameters.Prepend(
117
				TemplateParameter{Key: "{{ value }}", Value: *value},
118
			)...,
119
		).
120 1
		WithParameter("{{ value }}", *value).
121
		Create()
122
}
123