Test Failed
Push — main ( 7dfe2c...03ceef )
by Igor
01:04 queued 12s
created

it.LengthConstraint.newViolation   A

Complexity

Conditions 4

Size

Total Lines 25
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 21
dl 0
loc 25
ccs 10
cts 10
cp 1
crap 4
rs 9.376
c 0
b 0
f 0
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
package it
2
3
import (
4
	"context"
5
	"regexp"
6
	"strconv"
7
	"unicode/utf8"
8
9
	"github.com/muonsoft/validation"
10
	"github.com/muonsoft/validation/is"
11
)
12
13
// LengthConstraint checks that a given string length is between some minimum and maximum value.
14
// If you want to check the length of the array, slice or a map use CountConstraint.
15
type LengthConstraint struct {
16
	isIgnored              bool
17
	checkMin               bool
18
	checkMax               bool
19
	min                    int
20
	max                    int
21
	groups                 []string
22
	minErr                 error
23
	maxErr                 error
24
	exactErr               error
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
	return LengthConstraint{
35 1
		min:                  min,
36
		max:                  max,
37
		checkMin:             checkMin,
38
		checkMax:             checkMax,
39
		minErr:               validation.ErrTooShort,
40
		maxErr:               validation.ErrTooLong,
41
		exactErr:             validation.ErrNotExactLength,
42
		minMessageTemplate:   validation.ErrTooShort.Message(),
43
		maxMessageTemplate:   validation.ErrTooLong.Message(),
44
		exactMessageTemplate: validation.ErrNotExactLength.Message(),
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
	return newLengthConstraint(min, 0, true, false)
52 1
}
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
	return newLengthConstraint(0, max, false, true)
58 1
}
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
	return newLengthConstraint(min, max, true, true)
64 1
}
65
66
// HasExactLength creates a LengthConstraint that checks the length of the string
67
// has exact value.
68
func HasExactLength(count int) LengthConstraint {
69
	return newLengthConstraint(count, count, true, true)
70 1
}
71
72
// When enables conditional validation of this constraint. If the expression evaluates to false,
73
// then the constraint will be ignored.
74
func (c LengthConstraint) When(condition bool) LengthConstraint {
75 1
	c.isIgnored = !condition
76
	return c
77
}
78
79
// WhenGroups enables conditional validation of the constraint by using the validation groups.
80
func (c LengthConstraint) WhenGroups(groups ...string) LengthConstraint {
81
	c.groups = groups
82
	return c
83
}
84
85
// WithMinError overrides default underlying error for violation that will be shown if the string length
86 1
// is less than the minimum value.
87 1
func (c LengthConstraint) WithMinError(err error) LengthConstraint {
88
	c.minErr = err
89
	return c
90
}
91
92 1
// WithMaxError overrides default underlying error for violation that will be shown if the string length
93 1
// is greater than the maximum value.
94
func (c LengthConstraint) WithMaxError(err error) LengthConstraint {
95
	c.maxErr = err
96
	return c
97
}
98
99 1
// WithExactError overrides default underlying error for violation that will be shown if minimum and maximum values
100 1
// are equal and the length of the string is not exactly this value.
101
func (c LengthConstraint) WithExactError(err error) LengthConstraint {
102
	c.exactErr = err
103
	return c
104
}
105
106 1
// WithMinMessage sets the violation message that will be shown if the string length is less than
107 1
// the minimum value. You can set custom template parameters for injecting its values
108
// into the final message. Also, you can use default parameters:
109
//
110
//	{{ length }} - the current string length;
111
//	{{ limit }} - the lower limit;
112
//	{{ value }} - the current (invalid) value.
113 1
func (c LengthConstraint) WithMinMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
114 1
	c.minMessageTemplate = template
115
	c.minMessageParameters = parameters
116
	return c
117
}
118
119
// WithMaxMessage sets the violation message that will be shown if the string length is greater than
120
// the maximum value. You can set custom template parameters for injecting its values
121
// into the final message. Also, you can use default parameters:
122
//
123
//	{{ length }} - the current string length;
124
//	{{ limit }} - the lower limit;
125 1
//	{{ value }} - the current (invalid) value.
126 1
func (c LengthConstraint) WithMaxMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
127 1
	c.maxMessageTemplate = template
128
	c.maxMessageParameters = parameters
129
	return c
130
}
131
132
// WithExactMessage sets the violation message that will be shown if minimum and maximum values are equal and
133
// the length of the string is not exactly this value. You can set custom template parameters
134
// for injecting its values into the final message. Also, you can use default parameters:
135
//
136
//	{{ length }} - the current string length;
137
//	{{ limit }} - the lower limit;
138 1
//	{{ value }} - the current (invalid) value.
139 1
func (c LengthConstraint) WithExactMessage(template string, parameters ...validation.TemplateParameter) LengthConstraint {
140 1
	c.exactMessageTemplate = template
141
	c.exactMessageParameters = parameters
142
	return c
143
}
144
145
func (c LengthConstraint) ValidateString(ctx context.Context, validator *validation.Validator, value *string) error {
146
	if c.isIgnored || validator.IsIgnoredForGroups(c.groups...) || value == nil || *value == "" {
147
		return nil
148
	}
149
150
	count := utf8.RuneCountInString(*value)
151 1
152 1
	if c.checkMax && count > c.max {
153 1
		return c.newViolation(ctx, validator, count, c.max, *value, c.maxErr, c.maxMessageTemplate, c.maxMessageParameters)
154
	}
155
	if c.checkMin && count < c.min {
156
		return c.newViolation(ctx, validator, count, c.min, *value, c.minErr, c.minMessageTemplate, c.minMessageParameters)
157 1
	}
158 1
159
	return nil
160
}
161 1
162
func (c LengthConstraint) newViolation(
163 1
	ctx context.Context,
164 1
	validator *validation.Validator,
165
	count, limit int,
166 1
	value string,
167 1
	err error,
168
	template string,
169
	parameters validation.TemplateParameterList,
170 1
) validation.Violation {
171
	if c.checkMin && c.checkMax && c.min == c.max {
172
		template = c.exactMessageTemplate
173
		parameters = c.exactMessageParameters
174
		err = c.exactErr
175
	}
176
177
	return validator.BuildViolation(ctx, err, template).
178
		WithPluralCount(limit).
179 1
		WithParameters(
180 1
			parameters.Prepend(
181 1
				validation.TemplateParameter{Key: "{{ value }}", Value: strconv.Quote(value)},
182 1
				validation.TemplateParameter{Key: "{{ length }}", Value: strconv.Itoa(count)},
183
				validation.TemplateParameter{Key: "{{ limit }}", Value: strconv.Itoa(limit)},
184
			)...,
185 1
		).
186
		Create()
187
}
188
189
// RegexpConstraint is used to ensure that the given value corresponds to regex pattern.
190
type RegexpConstraint struct {
191
	isIgnored         bool
192
	match             bool
193
	groups            []string
194
	err               error
195
	messageTemplate   string
196
	messageParameters validation.TemplateParameterList
197
	regex             *regexp.Regexp
198
}
199
200
// Matches creates a RegexpConstraint for checking whether a value matches a regular expression.
201
func Matches(regex *regexp.Regexp) RegexpConstraint {
202
	return RegexpConstraint{
203
		regex:           regex,
204
		match:           true,
205
		err:             validation.ErrNotValid,
206
		messageTemplate: validation.ErrNotValid.Message(),
207
	}
208
}
209
210 1
// DoesNotMatch creates a RegexpConstraint for checking whether a value does not match a regular expression.
211
func DoesNotMatch(regex *regexp.Regexp) RegexpConstraint {
212
	return RegexpConstraint{
213
		regex:           regex,
214
		match:           false,
215
		err:             validation.ErrNotValid,
216
		messageTemplate: validation.ErrNotValid.Message(),
217
	}
218
}
219
220 1
// WithError overrides default error for produced violation.
221
func (c RegexpConstraint) WithError(err error) RegexpConstraint {
222
	c.err = err
223
	return c
224
}
225
226
// WithMessage sets the violation message template. You can set custom template parameters
227
// for injecting its values into the final message. Also, you can use default parameters:
228
//
229
//	{{ value }} - the current (invalid) value.
230 1
func (c RegexpConstraint) WithMessage(template string, parameters ...validation.TemplateParameter) RegexpConstraint {
231 1
	c.messageTemplate = template
232
	c.messageParameters = parameters
233
	return c
234 1
}
235
236
// When enables conditional validation of this constraint. If the expression evaluates to false,
237
// then the constraint will be ignored.
238
func (c RegexpConstraint) When(condition bool) RegexpConstraint {
239 1
	c.isIgnored = !condition
240
	return c
241
}
242
243
// WhenGroups enables conditional validation of the constraint by using the validation groups.
244 1
func (c RegexpConstraint) WhenGroups(groups ...string) RegexpConstraint {
245 1
	c.groups = groups
246
	return c
247
}
248
249
func (c RegexpConstraint) ValidateString(ctx context.Context, validator *validation.Validator, value *string) error {
250
	if c.regex == nil {
251
		return validator.CreateConstraintError("RegexpConstraint", "nil regex")
252
	}
253 1
	if c.isIgnored || validator.IsIgnoredForGroups(c.groups...) || value == nil || *value == "" {
254 1
		return nil
255 1
	}
256
	if c.match == c.regex.MatchString(*value) {
257
		return nil
258
	}
259
260
	return validator.
261 1
		BuildViolation(ctx, c.err, c.messageTemplate).
262 1
		WithParameters(
263
			c.messageParameters.Prepend(
264
				validation.TemplateParameter{Key: "{{ value }}", Value: *value},
265
			)...,
266
		).
267 1
		Create()
268 1
}
269
270
// IsJSON validates that a value is a valid JSON.
271
func IsJSON() validation.StringFuncConstraint {
272 1
	return validation.OfStringBy(is.JSON).
273 1
		WithError(validation.ErrInvalidJSON).
274
		WithMessage(validation.ErrInvalidJSON.Message())
275 1
}
276 1
277
// IsInteger checks that string value is an integer.
278
func IsInteger() validation.StringFuncConstraint {
279 1
	return validation.OfStringBy(is.Integer).
280
		WithError(validation.ErrNotInteger).
281
		WithMessage(validation.ErrNotInteger.Message())
282
}
283
284
// IsNumeric checks that string value is a valid numeric (integer or float).
285
func IsNumeric() validation.StringFuncConstraint {
286
	return validation.OfStringBy(is.Number).
287
		WithError(validation.ErrNotNumeric).
288
		WithMessage(validation.ErrNotNumeric.Message())
289
}
290