Test Failed
Pull Request — main (#71)
by Igor
02:01
created

it/string.go   A

Size/Duplication

Total Lines 291
Duplicated Lines 0 %

Test Coverage

Coverage 98.33%

Importance

Changes 0
Metric Value
cc 42
eloc 160
dl 0
loc 291
rs 9.0399
c 0
b 0
f 0
ccs 59
cts 60
cp 0.9833
crap 42.0082

25 Methods

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