Passed
Pull Request — main (#50)
by Igor
02:06
created

it.HasMinLength   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
nop 1
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
// Codes overrides default codes for produced violation.
106
func (c LengthConstraint) Codes(minCode, maxCode, exactCode string) LengthConstraint {
107 1
	c.minCode = minCode
108 1
	c.maxCode = maxCode
109 1
	c.exactCode = exactCode
110 1
	return c
111
}
112
113
// MinMessage sets the violation message that will be shown if the string length is less than
114
// the minimum value. You can set custom template parameters for injecting its values
115
// into the final message. Also, you can use default parameters:
116
//
117
//	{{ length }} - the current string length;
118
//	{{ limit }} - the lower limit;
119
//	{{ value }} - the current (invalid) value.
120
func (c LengthConstraint) MinMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
121 1
	c.minMessageTemplate = template
122 1
	c.minMessageParameters = parameters
123 1
	return c
124
}
125
126
// MaxMessage sets the violation message that will be shown if the string length is greater than
127
// the maximum 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) MaxMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
134 1
	c.maxMessageTemplate = template
135 1
	c.maxMessageParameters = parameters
136 1
	return c
137
}
138
139
// ExactMessage sets the violation message that will be shown if minimum and maximum values are equal and
140
// the length of the string is not exactly this value. You can set custom template parameters
141
// for injecting its values 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) ExactMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
147 1
	c.exactMessageTemplate = template
148 1
	c.exactMessageParameters = parameters
149 1
	return c
150
}
151
152
func (c LengthConstraint) ValidateString(value *string, scope validation.Scope) error {
153 1
	if c.isIgnored || value == nil || *value == "" {
154 1
		return nil
155
	}
156
157 1
	count := utf8.RuneCountInString(*value)
158
159 1
	if c.checkMax && count > c.max {
160 1
		return c.newViolation(count, c.max, *value, c.maxCode, c.maxMessageTemplate, c.maxMessageParameters, scope)
161
	}
162 1
	if c.checkMin && count < c.min {
163 1
		return c.newViolation(count, c.min, *value, c.minCode, c.minMessageTemplate, c.minMessageParameters, scope)
164
	}
165
166 1
	return nil
167
}
168
169
func (c LengthConstraint) newViolation(
170
	count, limit int,
171
	value, violationCode, template string,
172
	parameters validation.TemplateParameterList,
173
	scope validation.Scope,
174
) validation.Violation {
175 1
	if c.checkMin && c.checkMax && c.min == c.max {
176 1
		template = c.exactMessageTemplate
177 1
		parameters = c.exactMessageParameters
178 1
		violationCode = c.exactCode
179
	}
180
181 1
	return scope.BuildViolation(violationCode, template).
182
		SetPluralCount(limit).
183
		SetParameters(
184
			parameters.Prepend(
185
				validation.TemplateParameter{Key: "{{ value }}", Value: strconv.Quote(value)},
186
				validation.TemplateParameter{Key: "{{ length }}", Value: strconv.Itoa(count)},
187
				validation.TemplateParameter{Key: "{{ limit }}", Value: strconv.Itoa(limit)},
188
			)...,
189
		).
190
		CreateViolation()
191
}
192
193
// RegexConstraint is used to ensure that the given value corresponds to regex pattern.
194
type RegexConstraint struct {
195
	isIgnored         bool
196
	match             bool
197
	code              string
198
	messageTemplate   string
199
	messageParameters validation.TemplateParameterList
200
	regex             *regexp.Regexp
201
}
202
203
// Matches creates a RegexConstraint for checking whether a value matches a regular expression.
204
//
205
// Example
206
//	err := validator.ValidateString(&s, it.Matches(regexp.MustCompile("^[a-z]+$")))
207
func Matches(regex *regexp.Regexp) RegexConstraint {
208 1
	return RegexConstraint{
209
		regex:           regex,
210
		match:           true,
211
		code:            code.MatchingFailed,
212
		messageTemplate: message.NotValid,
213
	}
214
}
215
216
// DoesNotMatch creates a RegexConstraint for checking whether a value does not match a regular expression.
217
//
218
// Example
219
//	err := validator.ValidateString(&s, it.DoesNotMatch(regexp.MustCompile("^[a-z]+$")))
220
func DoesNotMatch(regex *regexp.Regexp) RegexConstraint {
221 1
	return RegexConstraint{
222
		regex:           regex,
223
		match:           false,
224
		code:            code.MatchingFailed,
225
		messageTemplate: message.NotValid,
226
	}
227
}
228
229
// SetUp will return an error if the pattern is empty.
230
func (c RegexConstraint) SetUp() error {
231 1
	if c.regex == nil {
232 1
		return errEmptyRegex
233
	}
234
235 1
	return nil
236
}
237
238
// Name is the constraint name.
239
func (c RegexConstraint) Name() string {
240 1
	return "RegexConstraint"
241
}
242
243
// Code overrides default code for produced violation.
244
func (c RegexConstraint) Code(code string) RegexConstraint {
245 1
	c.code = code
246 1
	return c
247
}
248
249
// Message sets the violation message template. You can set custom template parameters
250
// for injecting its values into the final message. Also, you can use default parameters:
251
//
252
//	{{ value }} - the current (invalid) value.
253
func (c RegexConstraint) Message(template string, parameters ...validation.TemplateParameter) RegexConstraint {
254 1
	c.messageTemplate = template
255 1
	c.messageParameters = parameters
256 1
	return c
257
}
258
259
// When enables conditional validation of this constraint. If the expression evaluates to false,
260
// then the constraint will be ignored.
261
func (c RegexConstraint) When(condition bool) RegexConstraint {
262 1
	c.isIgnored = !condition
263 1
	return c
264
}
265
266
func (c RegexConstraint) ValidateString(value *string, scope validation.Scope) error {
267 1
	if c.isIgnored || value == nil || *value == "" {
268 1
		return nil
269
	}
270 1
	if c.match == c.regex.MatchString(*value) {
271 1
		return nil
272
	}
273
274 1
	return scope.
275
		BuildViolation(c.code, c.messageTemplate).
276
		SetParameters(
277
			c.messageParameters.Prepend(
278
				validation.TemplateParameter{Key: "{{ value }}", Value: *value},
279
			)...,
280
		).
281
		CreateViolation()
282
}
283
284
// IsJSON validates that a value is a valid JSON.
285
func IsJSON() validation.CustomStringConstraint {
286 1
	return validation.NewCustomStringConstraint(
287
		is.JSON,
288
		"JSONConstraint",
289
		code.InvalidJSON,
290
		message.InvalidJSON,
291
	)
292
}
293