Passed
Push — main ( 10a5f7...41473a )
by Igor
02:50 queued 55s
created

it.URLConstraint.Code   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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