Passed
Push — main ( 10a5f7...41473a )
by Igor
02:50 queued 55s
created

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