Passed
Push — main ( aa8c07...4607a7 )
by Igor
02:58 queued 01:09
created

it.RegexConstraint.SetUp   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
nop 0
1
package it
2
3
import (
4
	"github.com/muonsoft/validation"
5
	"github.com/muonsoft/validation/code"
6
	"github.com/muonsoft/validation/is"
7
	"github.com/muonsoft/validation/message"
8
9
	"regexp"
10
	"strconv"
11
	"unicode/utf8"
12
)
13
14
// LengthConstraint checks that a given string length is between some minimum and maximum value.
15
// If you want to check the length of the array, slice or a map use CountConstraint.
16
type LengthConstraint struct {
17
	isIgnored              bool
18
	checkMin               bool
19
	checkMax               bool
20
	min                    int
21
	max                    int
22
	minCode                string
23
	maxCode                string
24
	exactCode              string
25
	minMessageTemplate     string
26
	minMessageParameters   validation.TemplateParameterList
27
	maxMessageTemplate     string
28
	maxMessageParameters   validation.TemplateParameterList
29
	exactMessageTemplate   string
30
	exactMessageParameters validation.TemplateParameterList
31
}
32
33
func newLengthConstraint(min int, max int, checkMin bool, checkMax bool) LengthConstraint {
34 1
	return LengthConstraint{
35
		min:                  min,
36
		max:                  max,
37
		checkMin:             checkMin,
38
		checkMax:             checkMax,
39
		minCode:              code.LengthTooFew,
40
		maxCode:              code.LengthTooMany,
41
		exactCode:            code.LengthExact,
42
		minMessageTemplate:   message.LengthTooFew,
43
		maxMessageTemplate:   message.LengthTooMany,
44
		exactMessageTemplate: message.LengthExact,
45
	}
46
}
47
48
// HasMinLength creates a LengthConstraint that checks the length of the string
49
// is greater than the minimum value.
50
func HasMinLength(min int) LengthConstraint {
51 1
	return newLengthConstraint(min, 0, true, false)
52
}
53
54
// HasMaxLength creates a LengthConstraint that checks the length of the string
55
// is less than the maximum value.
56
func HasMaxLength(max int) LengthConstraint {
57 1
	return newLengthConstraint(0, max, false, true)
58
}
59
60
// HasLengthBetween creates a LengthConstraint that checks the length of the string
61
// is between some minimum and maximum value.
62
func HasLengthBetween(min int, max int) LengthConstraint {
63 1
	return newLengthConstraint(min, max, true, true)
64
}
65
66
// HasExactLength creates a LengthConstraint that checks the length of the string
67
// has exact value.
68
func HasExactLength(count int) LengthConstraint {
69 1
	return newLengthConstraint(count, count, true, true)
70
}
71
72
// Name is the constraint name.
73
func (c LengthConstraint) Name() string {
74
	return "LengthConstraint"
75
}
76
77
// SetUp always returns no error.
78
func (c LengthConstraint) SetUp() error {
79 1
	return nil
80
}
81
82
// When enables conditional validation of this constraint. If the expression evaluates to false,
83
// then the constraint will be ignored.
84
func (c LengthConstraint) When(condition bool) LengthConstraint {
85 1
	c.isIgnored = !condition
86 1
	return c
87
}
88
89
// MinCode overrides default code for violation that will be shown if the string length
90
// is less than the minimum value.
91
func (c LengthConstraint) MinCode(code string) LengthConstraint {
92 1
	c.minCode = code
93 1
	return c
94
}
95
96
// MaxCode overrides default code for violation that will be shown if the string length
97
// is greater than the maximum value.
98
func (c LengthConstraint) MaxCode(code string) LengthConstraint {
99 1
	c.maxCode = code
100 1
	return c
101
}
102
103
// ExactCode overrides default code for violation that will be shown if minimum and maximum values
104
// are equal and the length of the string is not exactly this value.
105
func (c LengthConstraint) ExactCode(code string) LengthConstraint {
106 1
	c.exactCode = code
107 1
	return c
108
}
109
110
// MinMessage sets the violation message that will be shown if the string length is less than
111
// the minimum value. You can set custom template parameters for injecting its values
112
// into the final message. Also, you can use default parameters:
113
//
114
//	{{ length }} - the current string length;
115
//	{{ limit }} - the lower limit;
116
//	{{ value }} - the current (invalid) value.
117
func (c LengthConstraint) MinMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
118 1
	c.minMessageTemplate = template
119 1
	c.minMessageParameters = parameters
120 1
	return c
121
}
122
123
// MaxMessage sets the violation message that will be shown if the string length is greater than
124
// the maximum value. You can set custom template parameters for injecting its values
125
// into the final message. Also, you can use default parameters:
126
//
127
//	{{ length }} - the current string length;
128
//	{{ limit }} - the lower limit;
129
//	{{ value }} - the current (invalid) value.
130
func (c LengthConstraint) MaxMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
131 1
	c.maxMessageTemplate = template
132 1
	c.maxMessageParameters = parameters
133 1
	return c
134
}
135
136
// ExactMessage sets the violation message that will be shown if minimum and maximum values are equal and
137
// the length of the string is not exactly this value. You can set custom template parameters
138
// for injecting its values into the final message. Also, you can use default parameters:
139
//
140
//	{{ length }} - the current string length;
141
//	{{ limit }} - the lower limit;
142
//	{{ value }} - the current (invalid) value.
143
func (c LengthConstraint) ExactMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
144 1
	c.exactMessageTemplate = template
145 1
	c.exactMessageParameters = parameters
146 1
	return c
147
}
148
149
func (c LengthConstraint) ValidateString(value *string, scope validation.Scope) error {
150 1
	if c.isIgnored || value == nil || *value == "" {
151 1
		return nil
152
	}
153
154 1
	count := utf8.RuneCountInString(*value)
155
156 1
	if c.checkMax && count > c.max {
157 1
		return c.newViolation(count, c.max, *value, c.maxCode, c.maxMessageTemplate, c.maxMessageParameters, scope)
158
	}
159 1
	if c.checkMin && count < c.min {
160 1
		return c.newViolation(count, c.min, *value, c.minCode, c.minMessageTemplate, c.minMessageParameters, scope)
161
	}
162
163 1
	return nil
164
}
165
166
func (c LengthConstraint) newViolation(
167
	count, limit int,
168
	value, violationCode, template string,
169
	parameters validation.TemplateParameterList,
170
	scope validation.Scope,
171
) validation.Violation {
172 1
	if c.checkMin && c.checkMax && c.min == c.max {
173 1
		template = c.exactMessageTemplate
174 1
		parameters = c.exactMessageParameters
175 1
		violationCode = c.exactCode
176
	}
177
178 1
	return scope.BuildViolation(violationCode, template).
179
		SetPluralCount(limit).
180
		SetParameters(
181
			parameters.Prepend(
182
				validation.TemplateParameter{Key: "{{ value }}", Value: strconv.Quote(value)},
183
				validation.TemplateParameter{Key: "{{ length }}", Value: strconv.Itoa(count)},
184
				validation.TemplateParameter{Key: "{{ limit }}", Value: strconv.Itoa(limit)},
185
			)...,
186
		).
187
		CreateViolation()
188
}
189
190
// RegexConstraint is used to ensure that the given value corresponds to regex pattern.
191
type RegexConstraint struct {
192
	isIgnored         bool
193
	match             bool
194
	code              string
195
	messageTemplate   string
196
	messageParameters validation.TemplateParameterList
197
	regex             *regexp.Regexp
198
}
199
200
// Matches creates a RegexConstraint for checking whether a value matches a regular expression.
201
func Matches(regex *regexp.Regexp) RegexConstraint {
202 1
	return RegexConstraint{
203
		regex:           regex,
204
		match:           true,
205
		code:            code.MatchingFailed,
206
		messageTemplate: message.NotValid,
207
	}
208
}
209
210
// DoesNotMatch creates a RegexConstraint for checking whether a value does not match a regular expression.
211
func DoesNotMatch(regex *regexp.Regexp) RegexConstraint {
212 1
	return RegexConstraint{
213
		regex:           regex,
214
		match:           false,
215
		code:            code.MatchingFailed,
216
		messageTemplate: message.NotValid,
217
	}
218
}
219
220
// SetUp will return an error if the pattern is empty.
221
func (c RegexConstraint) SetUp() error {
222 1
	if c.regex == nil {
223 1
		return errEmptyRegex
224
	}
225
226 1
	return nil
227
}
228
229
// Name is the constraint name.
230
func (c RegexConstraint) Name() string {
231 1
	return "RegexConstraint"
232
}
233
234
// Code overrides default code for produced violation.
235
func (c RegexConstraint) Code(code string) RegexConstraint {
236 1
	c.code = code
237 1
	return c
238
}
239
240
// Message sets the violation message template. You can set custom template parameters
241
// for injecting its values into the final message. Also, you can use default parameters:
242
//
243
//	{{ value }} - the current (invalid) value.
244
func (c RegexConstraint) Message(template string, parameters ...validation.TemplateParameter) RegexConstraint {
245 1
	c.messageTemplate = template
246 1
	c.messageParameters = parameters
247 1
	return c
248
}
249
250
// When enables conditional validation of this constraint. If the expression evaluates to false,
251
// then the constraint will be ignored.
252
func (c RegexConstraint) When(condition bool) RegexConstraint {
253 1
	c.isIgnored = !condition
254 1
	return c
255
}
256
257
func (c RegexConstraint) ValidateString(value *string, scope validation.Scope) error {
258 1
	if c.isIgnored || value == nil || *value == "" {
259 1
		return nil
260
	}
261 1
	if c.match == c.regex.MatchString(*value) {
262 1
		return nil
263
	}
264
265 1
	return scope.
266
		BuildViolation(c.code, c.messageTemplate).
267
		SetParameters(
268
			c.messageParameters.Prepend(
269
				validation.TemplateParameter{Key: "{{ value }}", Value: *value},
270
			)...,
271
		).
272
		CreateViolation()
273
}
274
275
// IsJSON validates that a value is a valid JSON.
276
func IsJSON() validation.CustomStringConstraint {
277 1
	return validation.NewCustomStringConstraint(
278
		is.JSON,
279
		"JSONConstraint",
280
		code.InvalidJSON,
281
		message.InvalidJSON,
282
	)
283
}
284