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

it.IPConstraint.WithInvalidMessage   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 4
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 1
cp 0
crap 2
nop 2
1
package it
2
3
import (
4
	"errors"
5
	"net"
6
7
	"github.com/muonsoft/validation"
8
	"github.com/muonsoft/validation/is"
9
	"github.com/muonsoft/validation/validate"
10
)
11
12
// IsEmail is used for simplified validation of an email address. It allows all values
13
// with an "@" symbol in, and a "." in the second host part of the email address.
14
func IsEmail() validation.CustomStringConstraint {
15
	return validation.NewCustomStringConstraint(is.Email).
16
		WithError(validation.ErrInvalidEmail).
17 1
		WithMessage(validation.ErrInvalidEmail.Template())
18
}
19
20
// IsHTML5Email is used for validation of an email address based on pattern for HTML5
21
// (see https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address).
22
func IsHTML5Email() validation.CustomStringConstraint {
23
	return validation.NewCustomStringConstraint(is.HTML5Email).
24
		WithError(validation.ErrInvalidEmail).
25
		WithMessage(validation.ErrInvalidEmail.Template())
26
}
27
28 1
// IsHostname validates that a value is a valid hostname. It checks that:
29
//	• each label within a valid hostname may be no more than 63 octets long;
30
//	• the total length of the hostname must not exceed 255 characters;
31
//	• hostname is fully qualified and include its top-level domain name
32
//	  (for instance, example.com is valid but example is not);
33
//	• checks for reserved top-level domains according to RFC 2606
34
//	  (hostnames containing them are not considered valid:
35
//	  .example, .invalid, .localhost, and .test).
36
//
37
// If you do not want to check for top-level domains use IsLooseHostname version of constraint.
38
func IsHostname() validation.CustomStringConstraint {
39
	return validation.NewCustomStringConstraint(is.StrictHostname).
40
		WithError(validation.ErrInvalidHostname).
41
		WithMessage(validation.ErrInvalidHostname.Template())
42
}
43
44
// IsLooseHostname validates that a value is a valid hostname. It checks that:
45
//	• each label within a valid hostname may be no more than 63 octets long;
46
//	• the total length of the hostname must not exceed 255 characters.
47 1
func IsLooseHostname() validation.CustomStringConstraint {
48
	return validation.NewCustomStringConstraint(is.Hostname).
49
		WithError(validation.ErrInvalidHostname).
50
		WithMessage(validation.ErrInvalidHostname.Template())
51
}
52
53
// URLConstraint is used to validate URL string. This constraint doesn’t check that the host of the
54
// given URL really exists, because the information of the DNS records is not reliable.
55
//
56
// This constraint doesn't check the length of the URL. Use LengthConstraint to check the length of the given value.
57
type URLConstraint struct {
58
	isIgnored              bool
59 1
	supportsRelativeSchema bool
60
	schemas                []string
61
	groups                 []string
62
	err                    error
63
	messageTemplate        string
64
	messageParameters      validation.TemplateParameterList
65
}
66
67
// IsURL creates a URLConstraint to validate an URL. By default, constraint checks
68
// only for the http:// and https:// schemas. Use the WithSchemas method to configure
69
// the list of expected schemas. Also, you can use WithRelativeSchema to enable support
70
// of the relative schema (without schema, e.g. "//example.com").
71
func IsURL() URLConstraint {
72
	return URLConstraint{
73
		schemas:         []string{"http", "https"},
74
		err:             validation.ErrInvalidURL,
75
		messageTemplate: validation.ErrInvalidURL.Template(),
76
	}
77
}
78
79
// WithRelativeSchema enables support of relative URL schema, which means that URL value
80
// may be treated as relative (without schema, e.g. "//example.com").
81
func (c URLConstraint) WithRelativeSchema() URLConstraint {
82
	c.supportsRelativeSchema = true
83
	return c
84
}
85
86 1
// WithSchemas is used to set up a list of accepted schemas. For example, if you also consider the ftp:// type URLs
87
// to be valid, redefine the schemas list, listing http, https, and also ftp.
88
// If the list is empty, then an error will be returned.
89
func (c URLConstraint) WithSchemas(schemas ...string) URLConstraint {
90
	c.schemas = schemas
91
	return c
92
}
93
94
// WithError overrides default error for produced violation.
95 1
func (c URLConstraint) WithError(err error) URLConstraint {
96 1
	c.err = err
97
	return c
98
}
99 1
100
// WithMessage sets the violation message template. You can set custom template parameters
101
// for injecting its values into the final message. Also, you can use default parameters:
102
//
103
//	{{ value }} - the current (invalid) value.
104 1
func (c URLConstraint) WithMessage(template string, parameters ...validation.TemplateParameter) URLConstraint {
105
	c.messageTemplate = template
106
	c.messageParameters = parameters
107
	return c
108
}
109
110 1
// When enables conditional validation of this constraint. If the expression evaluates to false,
111 1
// then the constraint will be ignored.
112
func (c URLConstraint) When(condition bool) URLConstraint {
113
	c.isIgnored = !condition
114
	return c
115
}
116
117
// WhenGroups enables conditional validation of the constraint by using the validation groups.
118 1
func (c URLConstraint) WhenGroups(groups ...string) URLConstraint {
119 1
	c.groups = groups
120
	return c
121
}
122
123
func (c URLConstraint) ValidateString(value *string, scope validation.Scope) error {
124 1
	if len(c.schemas) == 0 {
125 1
		return scope.NewConstraintError("URLConstraint", "empty list of schemas")
126
	}
127
	if c.isIgnored || scope.IsIgnored(c.groups...) || value == nil || *value == "" {
128
		return nil
129
	}
130
131
	schemas := c.schemas
132
	if c.supportsRelativeSchema {
133 1
		schemas = append(schemas, "")
134 1
	}
135 1
	if is.URL(*value, schemas...) {
136
		return nil
137
	}
138
139
	return scope.BuildViolation(c.err, c.messageTemplate).
140
		WithParameters(
141 1
			c.messageParameters.Prepend(
142 1
				validation.TemplateParameter{Key: "{{ value }}", Value: *value},
143
			)...,
144
		).
145
		Create()
146
}
147 1
148 1
// IPConstraint is used to validate IP address. You can check for different versions
149
// and restrict some ranges by additional options.
150
type IPConstraint struct {
151
	isIgnored    bool
152 1
	validate     func(value string, restrictions ...validate.IPRestriction) error
153 1
	restrictions []validate.IPRestriction
154
155
	groups []string
156 1
157 1
	invalidErr    error
158 1
	prohibitedErr error
159
160 1
	invalidMessageTemplate      string
161 1
	invalidMessageParameters    validation.TemplateParameterList
162
	prohibitedMessageTemplate   string
163
	prohibitedMessageParameters validation.TemplateParameterList
164 1
}
165
166
// IsIP creates an IPConstraint to validate an IP address (IPv4 or IPv6).
167
func IsIP() IPConstraint {
168
	return newIPConstraint(validate.IP)
169
}
170
171
// IsIPv4 creates an IPConstraint to validate an IPv4 address.
172
func IsIPv4() IPConstraint {
173
	return newIPConstraint(validate.IPv4)
174
}
175
176
// IsIPv6 creates an IPConstraint to validate an IPv4 address.
177
func IsIPv6() IPConstraint {
178
	return newIPConstraint(validate.IPv6)
179
}
180
181
func newIPConstraint(validate func(value string, restrictions ...validate.IPRestriction) error) IPConstraint {
182
	return IPConstraint{
183
		validate:                  validate,
184
		invalidErr:                validation.ErrInvalidIP,
185
		prohibitedErr:             validation.ErrProhibitedIP,
186
		invalidMessageTemplate:    validation.ErrInvalidIP.Template(),
187
		prohibitedMessageTemplate: validation.ErrProhibitedIP.Template(),
188
	}
189
}
190
191
// DenyPrivateIP denies using of private IPs according to RFC 1918 (IPv4 addresses)
192
// and RFC 4193 (IPv6 addresses).
193 1
func (c IPConstraint) DenyPrivateIP() IPConstraint {
194
	c.restrictions = append(c.restrictions, validate.DenyPrivateIP())
195
	return c
196
}
197
198 1
// DenyIP can be used to deny custom range of IP addresses.
199
func (c IPConstraint) DenyIP(restrict func(ip net.IP) bool) IPConstraint {
200
	c.restrictions = append(c.restrictions, restrict)
201
	return c
202
}
203 1
204
// WithInvalidError overrides default underlying error for violation produced on invalid IP case.
205
func (c IPConstraint) WithInvalidError(err error) IPConstraint {
206
	c.invalidErr = err
207 1
	return c
208
}
209
210
// WithProhibitedError overrides default underlying error for violation produced on prohibited IP case.
211
func (c IPConstraint) WithProhibitedError(err error) IPConstraint {
212
	c.prohibitedErr = err
213
	return c
214
}
215
216
// WithInvalidMessage sets the violation message template for invalid IP case.
217
// You can set custom template parameters for injecting its values into the final message.
218 1
// Also, you can use default parameters:
219
//
220
//	{{ value }} - the current (invalid) value.
221
func (c IPConstraint) WithInvalidMessage(template string, parameters ...validation.TemplateParameter) IPConstraint {
222
	c.invalidMessageTemplate = template
223
	c.invalidMessageParameters = parameters
224
	return c
225
}
226
227
// WithProhibitedMessage sets the violation message template for prohibited IP case.
228
// You can set custom template parameters for injecting its values into the final message.
229 1
// Also, you can use default parameters:
230 1
//
231
//	{{ value }} - the current (invalid) value.
232
func (c IPConstraint) WithProhibitedMessage(template string, parameters ...validation.TemplateParameter) IPConstraint {
233
	c.prohibitedMessageTemplate = template
234
	c.prohibitedMessageParameters = parameters
235 1
	return c
236 1
}
237
238
// When enables conditional validation of this constraint. If the expression evaluates to false,
239
// then the constraint will be ignored.
240
func (c IPConstraint) When(condition bool) IPConstraint {
241 1
	c.isIgnored = !condition
242 1
	return c
243
}
244
245
// WhenGroups enables conditional validation of the constraint by using the validation groups.
246
func (c IPConstraint) WhenGroups(groups ...string) IPConstraint {
247 1
	c.groups = groups
248 1
	return c
249
}
250
251
func (c IPConstraint) ValidateString(value *string, scope validation.Scope) error {
252
	if c.isIgnored || scope.IsIgnored(c.groups...) || value == nil || *value == "" {
253
		return nil
254
	}
255
256
	return c.validateIP(*value, scope)
257 1
}
258 1
259 1
func (c IPConstraint) validateIP(value string, scope validation.Scope) error {
260
	err := c.validate(value, c.restrictions...)
261
	if err == nil {
262
		return nil
263
	}
264
265
	var builder *validation.ViolationBuilder
266
	var parameters validation.TemplateParameterList
267
268 1
	if errors.Is(err, validate.ErrProhibited) {
269 1
		builder = scope.BuildViolation(c.prohibitedErr, c.prohibitedMessageTemplate)
270 1
		parameters = c.prohibitedMessageParameters
271
	} else {
272
		builder = scope.BuildViolation(c.invalidErr, c.invalidMessageTemplate)
273
		parameters = c.invalidMessageParameters
274
	}
275
276 1
	return builder.
277 1
		WithParameters(
278
			parameters.Prepend(
279
				validation.TemplateParameter{Key: "{{ value }}", Value: value},
280
			)...,
281
		).
282 1
		Create()
283
}
284