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

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