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

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