Passed
Push — main ( 0f6dda...8fe6b9 )
by Igor
02:04 queued 11s
created

it.LengthConstraint.Name   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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