Passed
Push — main ( 8dabea...99fce0 )
by Rushan
02:12 queued 22s
created

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