podman

Форк
0
1768 строк · 49.2 Кб
1
// Package govalidator is package of validators and sanitizers for strings, structs and collections.
2
package govalidator
3

4
import (
5
	"bytes"
6
	"crypto/rsa"
7
	"crypto/x509"
8
	"encoding/base64"
9
	"encoding/json"
10
	"encoding/pem"
11
	"fmt"
12
	"io/ioutil"
13
	"net"
14
	"net/url"
15
	"reflect"
16
	"regexp"
17
	"sort"
18
	"strconv"
19
	"strings"
20
	"time"
21
	"unicode"
22
	"unicode/utf8"
23
)
24

25
var (
26
	fieldsRequiredByDefault bool
27
	nilPtrAllowedByRequired = false
28
	notNumberRegexp         = regexp.MustCompile("[^0-9]+")
29
	whiteSpacesAndMinus     = regexp.MustCompile(`[\s-]+`)
30
	paramsRegexp            = regexp.MustCompile(`\(.*\)$`)
31
)
32

33
const maxURLRuneCount = 2083
34
const minURLRuneCount = 3
35
const rfc3339WithoutZone = "2006-01-02T15:04:05"
36

37
// SetFieldsRequiredByDefault causes validation to fail when struct fields
38
// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
39
// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
40
//     type exampleStruct struct {
41
//         Name  string ``
42
//         Email string `valid:"email"`
43
// This, however, will only fail when Email is empty or an invalid email address:
44
//     type exampleStruct2 struct {
45
//         Name  string `valid:"-"`
46
//         Email string `valid:"email"`
47
// Lastly, this will only fail when Email is an invalid email address but not when it's empty:
48
//     type exampleStruct2 struct {
49
//         Name  string `valid:"-"`
50
//         Email string `valid:"email,optional"`
51
func SetFieldsRequiredByDefault(value bool) {
52
	fieldsRequiredByDefault = value
53
}
54

55
// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required.
56
// The validation will still reject ptr fields in their zero value state. Example with this enabled:
57
//     type exampleStruct struct {
58
//         Name  *string `valid:"required"`
59
// With `Name` set to "", this will be considered invalid input and will cause a validation error.
60
// With `Name` set to nil, this will be considered valid by validation.
61
// By default this is disabled.
62
func SetNilPtrAllowedByRequired(value bool) {
63
	nilPtrAllowedByRequired = value
64
}
65

66
// IsEmail checks if the string is an email.
67
func IsEmail(str string) bool {
68
	// TODO uppercase letters are not supported
69
	return rxEmail.MatchString(str)
70
}
71

72
// IsExistingEmail checks if the string is an email of existing domain
73
func IsExistingEmail(email string) bool {
74

75
	if len(email) < 6 || len(email) > 254 {
76
		return false
77
	}
78
	at := strings.LastIndex(email, "@")
79
	if at <= 0 || at > len(email)-3 {
80
		return false
81
	}
82
	user := email[:at]
83
	host := email[at+1:]
84
	if len(user) > 64 {
85
		return false
86
	}
87
	switch host {
88
	case "localhost", "example.com":
89
		return true
90
	}
91
	if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) {
92
		return false
93
	}
94
	if _, err := net.LookupMX(host); err != nil {
95
		if _, err := net.LookupIP(host); err != nil {
96
			return false
97
		}
98
	}
99

100
	return true
101
}
102

103
// IsURL checks if the string is an URL.
104
func IsURL(str string) bool {
105
	if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") {
106
		return false
107
	}
108
	strTemp := str
109
	if strings.Contains(str, ":") && !strings.Contains(str, "://") {
110
		// support no indicated urlscheme but with colon for port number
111
		// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
112
		strTemp = "http://" + str
113
	}
114
	u, err := url.Parse(strTemp)
115
	if err != nil {
116
		return false
117
	}
118
	if strings.HasPrefix(u.Host, ".") {
119
		return false
120
	}
121
	if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
122
		return false
123
	}
124
	return rxURL.MatchString(str)
125
}
126

127
// IsRequestURL checks if the string rawurl, assuming
128
// it was received in an HTTP request, is a valid
129
// URL confirm to RFC 3986
130
func IsRequestURL(rawurl string) bool {
131
	url, err := url.ParseRequestURI(rawurl)
132
	if err != nil {
133
		return false //Couldn't even parse the rawurl
134
	}
135
	if len(url.Scheme) == 0 {
136
		return false //No Scheme found
137
	}
138
	return true
139
}
140

141
// IsRequestURI checks if the string rawurl, assuming
142
// it was received in an HTTP request, is an
143
// absolute URI or an absolute path.
144
func IsRequestURI(rawurl string) bool {
145
	_, err := url.ParseRequestURI(rawurl)
146
	return err == nil
147
}
148

149
// IsAlpha checks if the string contains only letters (a-zA-Z). Empty string is valid.
150
func IsAlpha(str string) bool {
151
	if IsNull(str) {
152
		return true
153
	}
154
	return rxAlpha.MatchString(str)
155
}
156

157
//IsUTFLetter checks if the string contains only unicode letter characters.
158
//Similar to IsAlpha but for all languages. Empty string is valid.
159
func IsUTFLetter(str string) bool {
160
	if IsNull(str) {
161
		return true
162
	}
163

164
	for _, c := range str {
165
		if !unicode.IsLetter(c) {
166
			return false
167
		}
168
	}
169
	return true
170

171
}
172

173
// IsAlphanumeric checks if the string contains only letters and numbers. Empty string is valid.
174
func IsAlphanumeric(str string) bool {
175
	if IsNull(str) {
176
		return true
177
	}
178
	return rxAlphanumeric.MatchString(str)
179
}
180

181
// IsUTFLetterNumeric checks if the string contains only unicode letters and numbers. Empty string is valid.
182
func IsUTFLetterNumeric(str string) bool {
183
	if IsNull(str) {
184
		return true
185
	}
186
	for _, c := range str {
187
		if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok
188
			return false
189
		}
190
	}
191
	return true
192

193
}
194

195
// IsNumeric checks if the string contains only numbers. Empty string is valid.
196
func IsNumeric(str string) bool {
197
	if IsNull(str) {
198
		return true
199
	}
200
	return rxNumeric.MatchString(str)
201
}
202

203
// IsUTFNumeric checks if the string contains only unicode numbers of any kind.
204
// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
205
func IsUTFNumeric(str string) bool {
206
	if IsNull(str) {
207
		return true
208
	}
209
	if strings.IndexAny(str, "+-") > 0 {
210
		return false
211
	}
212
	if len(str) > 1 {
213
		str = strings.TrimPrefix(str, "-")
214
		str = strings.TrimPrefix(str, "+")
215
	}
216
	for _, c := range str {
217
		if !unicode.IsNumber(c) { //numbers && minus sign are ok
218
			return false
219
		}
220
	}
221
	return true
222

223
}
224

225
// IsUTFDigit checks if the string contains only unicode radix-10 decimal digits. Empty string is valid.
226
func IsUTFDigit(str string) bool {
227
	if IsNull(str) {
228
		return true
229
	}
230
	if strings.IndexAny(str, "+-") > 0 {
231
		return false
232
	}
233
	if len(str) > 1 {
234
		str = strings.TrimPrefix(str, "-")
235
		str = strings.TrimPrefix(str, "+")
236
	}
237
	for _, c := range str {
238
		if !unicode.IsDigit(c) { //digits && minus sign are ok
239
			return false
240
		}
241
	}
242
	return true
243

244
}
245

246
// IsHexadecimal checks if the string is a hexadecimal number.
247
func IsHexadecimal(str string) bool {
248
	return rxHexadecimal.MatchString(str)
249
}
250

251
// IsHexcolor checks if the string is a hexadecimal color.
252
func IsHexcolor(str string) bool {
253
	return rxHexcolor.MatchString(str)
254
}
255

256
// IsRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
257
func IsRGBcolor(str string) bool {
258
	return rxRGBcolor.MatchString(str)
259
}
260

261
// IsLowerCase checks if the string is lowercase. Empty string is valid.
262
func IsLowerCase(str string) bool {
263
	if IsNull(str) {
264
		return true
265
	}
266
	return str == strings.ToLower(str)
267
}
268

269
// IsUpperCase checks if the string is uppercase. Empty string is valid.
270
func IsUpperCase(str string) bool {
271
	if IsNull(str) {
272
		return true
273
	}
274
	return str == strings.ToUpper(str)
275
}
276

277
// HasLowerCase checks if the string contains at least 1 lowercase. Empty string is valid.
278
func HasLowerCase(str string) bool {
279
	if IsNull(str) {
280
		return true
281
	}
282
	return rxHasLowerCase.MatchString(str)
283
}
284

285
// HasUpperCase checks if the string contains as least 1 uppercase. Empty string is valid.
286
func HasUpperCase(str string) bool {
287
	if IsNull(str) {
288
		return true
289
	}
290
	return rxHasUpperCase.MatchString(str)
291
}
292

293
// IsInt checks if the string is an integer. Empty string is valid.
294
func IsInt(str string) bool {
295
	if IsNull(str) {
296
		return true
297
	}
298
	return rxInt.MatchString(str)
299
}
300

301
// IsFloat checks if the string is a float.
302
func IsFloat(str string) bool {
303
	return str != "" && rxFloat.MatchString(str)
304
}
305

306
// IsDivisibleBy checks if the string is a number that's divisible by another.
307
// If second argument is not valid integer or zero, it's return false.
308
// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
309
func IsDivisibleBy(str, num string) bool {
310
	f, _ := ToFloat(str)
311
	p := int64(f)
312
	q, _ := ToInt(num)
313
	if q == 0 {
314
		return false
315
	}
316
	return (p == 0) || (p%q == 0)
317
}
318

319
// IsNull checks if the string is null.
320
func IsNull(str string) bool {
321
	return len(str) == 0
322
}
323

324
// IsNotNull checks if the string is not null.
325
func IsNotNull(str string) bool {
326
	return !IsNull(str)
327
}
328

329
// HasWhitespaceOnly checks the string only contains whitespace
330
func HasWhitespaceOnly(str string) bool {
331
	return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str)
332
}
333

334
// HasWhitespace checks if the string contains any whitespace
335
func HasWhitespace(str string) bool {
336
	return len(str) > 0 && rxHasWhitespace.MatchString(str)
337
}
338

339
// IsByteLength checks if the string's length (in bytes) falls in a range.
340
func IsByteLength(str string, min, max int) bool {
341
	return len(str) >= min && len(str) <= max
342
}
343

344
// IsUUIDv3 checks if the string is a UUID version 3.
345
func IsUUIDv3(str string) bool {
346
	return rxUUID3.MatchString(str)
347
}
348

349
// IsUUIDv4 checks if the string is a UUID version 4.
350
func IsUUIDv4(str string) bool {
351
	return rxUUID4.MatchString(str)
352
}
353

354
// IsUUIDv5 checks if the string is a UUID version 5.
355
func IsUUIDv5(str string) bool {
356
	return rxUUID5.MatchString(str)
357
}
358

359
// IsUUID checks if the string is a UUID (version 3, 4 or 5).
360
func IsUUID(str string) bool {
361
	return rxUUID.MatchString(str)
362
}
363

364
// Byte to index table for O(1) lookups when unmarshaling.
365
// We use 0xFF as sentinel value for invalid indexes.
366
var ulidDec = [...]byte{
367
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
368
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
369
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
370
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
371
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,
372
	0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF,
373
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
374
	0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF,
375
	0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E,
376
	0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C,
377
	0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14,
378
	0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,
379
	0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
380
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
381
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
382
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
383
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
384
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
385
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
386
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
387
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
388
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
389
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
390
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
391
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
392
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
393
}
394

395
// EncodedSize is the length of a text encoded ULID.
396
const ulidEncodedSize = 26
397

398
// IsULID checks if the string is a ULID.
399
//
400
// Implementation got from:
401
//   https://github.com/oklog/ulid (Apache-2.0 License)
402
//
403
func IsULID(str string) bool {
404
	// Check if a base32 encoded ULID is the right length.
405
	if len(str) != ulidEncodedSize {
406
		return false
407
	}
408

409
	// Check if all the characters in a base32 encoded ULID are part of the
410
	// expected base32 character set.
411
	if ulidDec[str[0]] == 0xFF ||
412
		ulidDec[str[1]] == 0xFF ||
413
		ulidDec[str[2]] == 0xFF ||
414
		ulidDec[str[3]] == 0xFF ||
415
		ulidDec[str[4]] == 0xFF ||
416
		ulidDec[str[5]] == 0xFF ||
417
		ulidDec[str[6]] == 0xFF ||
418
		ulidDec[str[7]] == 0xFF ||
419
		ulidDec[str[8]] == 0xFF ||
420
		ulidDec[str[9]] == 0xFF ||
421
		ulidDec[str[10]] == 0xFF ||
422
		ulidDec[str[11]] == 0xFF ||
423
		ulidDec[str[12]] == 0xFF ||
424
		ulidDec[str[13]] == 0xFF ||
425
		ulidDec[str[14]] == 0xFF ||
426
		ulidDec[str[15]] == 0xFF ||
427
		ulidDec[str[16]] == 0xFF ||
428
		ulidDec[str[17]] == 0xFF ||
429
		ulidDec[str[18]] == 0xFF ||
430
		ulidDec[str[19]] == 0xFF ||
431
		ulidDec[str[20]] == 0xFF ||
432
		ulidDec[str[21]] == 0xFF ||
433
		ulidDec[str[22]] == 0xFF ||
434
		ulidDec[str[23]] == 0xFF ||
435
		ulidDec[str[24]] == 0xFF ||
436
		ulidDec[str[25]] == 0xFF {
437
		return false
438
	}
439

440
	// Check if the first character in a base32 encoded ULID will overflow. This
441
	// happens because the base32 representation encodes 130 bits, while the
442
	// ULID is only 128 bits.
443
	//
444
	// See https://github.com/oklog/ulid/issues/9 for details.
445
	if str[0] > '7' {
446
		return false
447
	}
448
	return true
449
}
450

451
// IsCreditCard checks if the string is a credit card.
452
func IsCreditCard(str string) bool {
453
	sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")
454
	if !rxCreditCard.MatchString(sanitized) {
455
		return false
456
	}
457
	
458
	number, _ := ToInt(sanitized)
459
	number, lastDigit := number / 10, number % 10	
460

461
	var sum int64
462
	for i:=0; number > 0; i++ {
463
		digit := number % 10
464
		
465
		if i % 2 == 0 {
466
			digit *= 2
467
			if digit > 9 {
468
				digit -= 9
469
			}
470
		}
471
		
472
		sum += digit
473
		number = number / 10
474
	}
475
	
476
	return (sum + lastDigit) % 10 == 0
477
}
478

479
// IsISBN10 checks if the string is an ISBN version 10.
480
func IsISBN10(str string) bool {
481
	return IsISBN(str, 10)
482
}
483

484
// IsISBN13 checks if the string is an ISBN version 13.
485
func IsISBN13(str string) bool {
486
	return IsISBN(str, 13)
487
}
488

489
// IsISBN checks if the string is an ISBN (version 10 or 13).
490
// If version value is not equal to 10 or 13, it will be checks both variants.
491
func IsISBN(str string, version int) bool {
492
	sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")
493
	var checksum int32
494
	var i int32
495
	if version == 10 {
496
		if !rxISBN10.MatchString(sanitized) {
497
			return false
498
		}
499
		for i = 0; i < 9; i++ {
500
			checksum += (i + 1) * int32(sanitized[i]-'0')
501
		}
502
		if sanitized[9] == 'X' {
503
			checksum += 10 * 10
504
		} else {
505
			checksum += 10 * int32(sanitized[9]-'0')
506
		}
507
		if checksum%11 == 0 {
508
			return true
509
		}
510
		return false
511
	} else if version == 13 {
512
		if !rxISBN13.MatchString(sanitized) {
513
			return false
514
		}
515
		factor := []int32{1, 3}
516
		for i = 0; i < 12; i++ {
517
			checksum += factor[i%2] * int32(sanitized[i]-'0')
518
		}
519
		return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0
520
	}
521
	return IsISBN(str, 10) || IsISBN(str, 13)
522
}
523

524
// IsJSON checks if the string is valid JSON (note: uses json.Unmarshal).
525
func IsJSON(str string) bool {
526
	var js json.RawMessage
527
	return json.Unmarshal([]byte(str), &js) == nil
528
}
529

530
// IsMultibyte checks if the string contains one or more multibyte chars. Empty string is valid.
531
func IsMultibyte(str string) bool {
532
	if IsNull(str) {
533
		return true
534
	}
535
	return rxMultibyte.MatchString(str)
536
}
537

538
// IsASCII checks if the string contains ASCII chars only. Empty string is valid.
539
func IsASCII(str string) bool {
540
	if IsNull(str) {
541
		return true
542
	}
543
	return rxASCII.MatchString(str)
544
}
545

546
// IsPrintableASCII checks if the string contains printable ASCII chars only. Empty string is valid.
547
func IsPrintableASCII(str string) bool {
548
	if IsNull(str) {
549
		return true
550
	}
551
	return rxPrintableASCII.MatchString(str)
552
}
553

554
// IsFullWidth checks if the string contains any full-width chars. Empty string is valid.
555
func IsFullWidth(str string) bool {
556
	if IsNull(str) {
557
		return true
558
	}
559
	return rxFullWidth.MatchString(str)
560
}
561

562
// IsHalfWidth checks if the string contains any half-width chars. Empty string is valid.
563
func IsHalfWidth(str string) bool {
564
	if IsNull(str) {
565
		return true
566
	}
567
	return rxHalfWidth.MatchString(str)
568
}
569

570
// IsVariableWidth checks if the string contains a mixture of full and half-width chars. Empty string is valid.
571
func IsVariableWidth(str string) bool {
572
	if IsNull(str) {
573
		return true
574
	}
575
	return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str)
576
}
577

578
// IsBase64 checks if a string is base64 encoded.
579
func IsBase64(str string) bool {
580
	return rxBase64.MatchString(str)
581
}
582

583
// IsFilePath checks is a string is Win or Unix file path and returns it's type.
584
func IsFilePath(str string) (bool, int) {
585
	if rxWinPath.MatchString(str) {
586
		//check windows path limit see:
587
		//  http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
588
		if len(str[3:]) > 32767 {
589
			return false, Win
590
		}
591
		return true, Win
592
	} else if rxUnixPath.MatchString(str) {
593
		return true, Unix
594
	}
595
	return false, Unknown
596
}
597

598
//IsWinFilePath checks both relative & absolute paths in Windows
599
func IsWinFilePath(str string) bool {
600
	if rxARWinPath.MatchString(str) {
601
		//check windows path limit see:
602
		//  http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
603
		if len(str[3:]) > 32767 {
604
			return false
605
		}
606
		return true
607
	}
608
	return false
609
}
610

611
//IsUnixFilePath checks both relative & absolute paths in Unix
612
func IsUnixFilePath(str string) bool {
613
	if rxARUnixPath.MatchString(str) {
614
		return true
615
	}
616
	return false
617
}
618

619
// IsDataURI checks if a string is base64 encoded data URI such as an image
620
func IsDataURI(str string) bool {
621
	dataURI := strings.Split(str, ",")
622
	if !rxDataURI.MatchString(dataURI[0]) {
623
		return false
624
	}
625
	return IsBase64(dataURI[1])
626
}
627

628
// IsMagnetURI checks if a string is valid magnet URI
629
func IsMagnetURI(str string) bool {
630
	return rxMagnetURI.MatchString(str)
631
}
632

633
// IsISO3166Alpha2 checks if a string is valid two-letter country code
634
func IsISO3166Alpha2(str string) bool {
635
	for _, entry := range ISO3166List {
636
		if str == entry.Alpha2Code {
637
			return true
638
		}
639
	}
640
	return false
641
}
642

643
// IsISO3166Alpha3 checks if a string is valid three-letter country code
644
func IsISO3166Alpha3(str string) bool {
645
	for _, entry := range ISO3166List {
646
		if str == entry.Alpha3Code {
647
			return true
648
		}
649
	}
650
	return false
651
}
652

653
// IsISO693Alpha2 checks if a string is valid two-letter language code
654
func IsISO693Alpha2(str string) bool {
655
	for _, entry := range ISO693List {
656
		if str == entry.Alpha2Code {
657
			return true
658
		}
659
	}
660
	return false
661
}
662

663
// IsISO693Alpha3b checks if a string is valid three-letter language code
664
func IsISO693Alpha3b(str string) bool {
665
	for _, entry := range ISO693List {
666
		if str == entry.Alpha3bCode {
667
			return true
668
		}
669
	}
670
	return false
671
}
672

673
// IsDNSName will validate the given string as a DNS name
674
func IsDNSName(str string) bool {
675
	if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 {
676
		// constraints already violated
677
		return false
678
	}
679
	return !IsIP(str) && rxDNSName.MatchString(str)
680
}
681

682
// IsHash checks if a string is a hash of type algorithm.
683
// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b']
684
func IsHash(str string, algorithm string) bool {
685
	var len string
686
	algo := strings.ToLower(algorithm)
687

688
	if algo == "crc32" || algo == "crc32b" {
689
		len = "8"
690
	} else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" {
691
		len = "32"
692
	} else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" {
693
		len = "40"
694
	} else if algo == "tiger192" {
695
		len = "48"
696
	} else if algo == "sha3-224" {
697
		len = "56"
698
	} else if algo == "sha256" || algo == "sha3-256" {
699
		len = "64"
700
	} else if algo == "sha384" || algo == "sha3-384" {
701
		len = "96"
702
	} else if algo == "sha512" || algo == "sha3-512" {
703
		len = "128"
704
	} else {
705
		return false
706
	}
707

708
	return Matches(str, "^[a-f0-9]{"+len+"}$")
709
}
710

711
// IsSHA3224 checks is a string is a SHA3-224 hash. Alias for `IsHash(str, "sha3-224")`
712
func IsSHA3224(str string) bool {
713
	return IsHash(str, "sha3-224")
714
}
715

716
// IsSHA3256 checks is a string is a SHA3-256 hash. Alias for `IsHash(str, "sha3-256")`
717
func IsSHA3256(str string) bool {
718
	return IsHash(str, "sha3-256")
719
}
720

721
// IsSHA3384 checks is a string is a SHA3-384 hash. Alias for `IsHash(str, "sha3-384")`
722
func IsSHA3384(str string) bool {
723
	return IsHash(str, "sha3-384")
724
}
725

726
// IsSHA3512 checks is a string is a SHA3-512 hash. Alias for `IsHash(str, "sha3-512")`
727
func IsSHA3512(str string) bool {
728
	return IsHash(str, "sha3-512")
729
}
730

731
// IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")`
732
func IsSHA512(str string) bool {
733
	return IsHash(str, "sha512")
734
}
735

736
// IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")`
737
func IsSHA384(str string) bool {
738
	return IsHash(str, "sha384")
739
}
740

741
// IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")`
742
func IsSHA256(str string) bool {
743
	return IsHash(str, "sha256")
744
}
745

746
// IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")`
747
func IsTiger192(str string) bool {
748
	return IsHash(str, "tiger192")
749
}
750

751
// IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")`
752
func IsTiger160(str string) bool {
753
	return IsHash(str, "tiger160")
754
}
755

756
// IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")`
757
func IsRipeMD160(str string) bool {
758
	return IsHash(str, "ripemd160")
759
}
760

761
// IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")`
762
func IsSHA1(str string) bool {
763
	return IsHash(str, "sha1")
764
}
765

766
// IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")`
767
func IsTiger128(str string) bool {
768
	return IsHash(str, "tiger128")
769
}
770

771
// IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")`
772
func IsRipeMD128(str string) bool {
773
	return IsHash(str, "ripemd128")
774
}
775

776
// IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")`
777
func IsCRC32(str string) bool {
778
	return IsHash(str, "crc32")
779
}
780

781
// IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")`
782
func IsCRC32b(str string) bool {
783
	return IsHash(str, "crc32b")
784
}
785

786
// IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")`
787
func IsMD5(str string) bool {
788
	return IsHash(str, "md5")
789
}
790

791
// IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")`
792
func IsMD4(str string) bool {
793
	return IsHash(str, "md4")
794
}
795

796
// IsDialString validates the given string for usage with the various Dial() functions
797
func IsDialString(str string) bool {
798
	if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) {
799
		return true
800
	}
801

802
	return false
803
}
804

805
// IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP`
806
func IsIP(str string) bool {
807
	return net.ParseIP(str) != nil
808
}
809

810
// IsPort checks if a string represents a valid port
811
func IsPort(str string) bool {
812
	if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 {
813
		return true
814
	}
815
	return false
816
}
817

818
// IsIPv4 checks if the string is an IP version 4.
819
func IsIPv4(str string) bool {
820
	ip := net.ParseIP(str)
821
	return ip != nil && strings.Contains(str, ".")
822
}
823

824
// IsIPv6 checks if the string is an IP version 6.
825
func IsIPv6(str string) bool {
826
	ip := net.ParseIP(str)
827
	return ip != nil && strings.Contains(str, ":")
828
}
829

830
// IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6)
831
func IsCIDR(str string) bool {
832
	_, _, err := net.ParseCIDR(str)
833
	return err == nil
834
}
835

836
// IsMAC checks if a string is valid MAC address.
837
// Possible MAC formats:
838
// 01:23:45:67:89:ab
839
// 01:23:45:67:89:ab:cd:ef
840
// 01-23-45-67-89-ab
841
// 01-23-45-67-89-ab-cd-ef
842
// 0123.4567.89ab
843
// 0123.4567.89ab.cdef
844
func IsMAC(str string) bool {
845
	_, err := net.ParseMAC(str)
846
	return err == nil
847
}
848

849
// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name
850
func IsHost(str string) bool {
851
	return IsIP(str) || IsDNSName(str)
852
}
853

854
// IsMongoID checks if the string is a valid hex-encoded representation of a MongoDB ObjectId.
855
func IsMongoID(str string) bool {
856
	return rxHexadecimal.MatchString(str) && (len(str) == 24)
857
}
858

859
// IsLatitude checks if a string is valid latitude.
860
func IsLatitude(str string) bool {
861
	return rxLatitude.MatchString(str)
862
}
863

864
// IsLongitude checks if a string is valid longitude.
865
func IsLongitude(str string) bool {
866
	return rxLongitude.MatchString(str)
867
}
868

869
// IsIMEI checks if a string is valid IMEI
870
func IsIMEI(str string) bool {
871
	return rxIMEI.MatchString(str)
872
}
873

874
// IsIMSI checks if a string is valid IMSI
875
func IsIMSI(str string) bool {
876
	if !rxIMSI.MatchString(str) {
877
		return false
878
	}
879

880
	mcc, err := strconv.ParseInt(str[0:3], 10, 32)
881
	if err != nil {
882
		return false
883
	}
884

885
	switch mcc {
886
	case 202, 204, 206, 208, 212, 213, 214, 216, 218, 219:
887
	case 220, 221, 222, 226, 228, 230, 231, 232, 234, 235:
888
	case 238, 240, 242, 244, 246, 247, 248, 250, 255, 257:
889
	case 259, 260, 262, 266, 268, 270, 272, 274, 276, 278:
890
	case 280, 282, 283, 284, 286, 288, 289, 290, 292, 293:
891
	case 294, 295, 297, 302, 308, 310, 311, 312, 313, 314:
892
	case 315, 316, 330, 332, 334, 338, 340, 342, 344, 346:
893
	case 348, 350, 352, 354, 356, 358, 360, 362, 363, 364:
894
	case 365, 366, 368, 370, 372, 374, 376, 400, 401, 402:
895
	case 404, 405, 406, 410, 412, 413, 414, 415, 416, 417:
896
	case 418, 419, 420, 421, 422, 424, 425, 426, 427, 428:
897
	case 429, 430, 431, 432, 434, 436, 437, 438, 440, 441:
898
	case 450, 452, 454, 455, 456, 457, 460, 461, 466, 467:
899
	case 470, 472, 502, 505, 510, 514, 515, 520, 525, 528:
900
	case 530, 536, 537, 539, 540, 541, 542, 543, 544, 545:
901
	case 546, 547, 548, 549, 550, 551, 552, 553, 554, 555:
902
	case 602, 603, 604, 605, 606, 607, 608, 609, 610, 611:
903
	case 612, 613, 614, 615, 616, 617, 618, 619, 620, 621:
904
	case 622, 623, 624, 625, 626, 627, 628, 629, 630, 631:
905
	case 632, 633, 634, 635, 636, 637, 638, 639, 640, 641:
906
	case 642, 643, 645, 646, 647, 648, 649, 650, 651, 652:
907
	case 653, 654, 655, 657, 658, 659, 702, 704, 706, 708:
908
	case 710, 712, 714, 716, 722, 724, 730, 732, 734, 736:
909
	case 738, 740, 742, 744, 746, 748, 750, 995:
910
		return true
911
	default:
912
		return false
913
	}
914
	return true
915
}
916

917
// IsRsaPublicKey checks if a string is valid public key with provided length
918
func IsRsaPublicKey(str string, keylen int) bool {
919
	bb := bytes.NewBufferString(str)
920
	pemBytes, err := ioutil.ReadAll(bb)
921
	if err != nil {
922
		return false
923
	}
924
	block, _ := pem.Decode(pemBytes)
925
	if block != nil && block.Type != "PUBLIC KEY" {
926
		return false
927
	}
928
	var der []byte
929

930
	if block != nil {
931
		der = block.Bytes
932
	} else {
933
		der, err = base64.StdEncoding.DecodeString(str)
934
		if err != nil {
935
			return false
936
		}
937
	}
938

939
	key, err := x509.ParsePKIXPublicKey(der)
940
	if err != nil {
941
		return false
942
	}
943
	pubkey, ok := key.(*rsa.PublicKey)
944
	if !ok {
945
		return false
946
	}
947
	bitlen := len(pubkey.N.Bytes()) * 8
948
	return bitlen == int(keylen)
949
}
950

951
// IsRegex checks if a give string is a valid regex with RE2 syntax or not
952
func IsRegex(str string) bool {
953
	if _, err := regexp.Compile(str); err == nil {
954
		return true
955
	}
956
	return false
957
}
958

959
func toJSONName(tag string) string {
960
	if tag == "" {
961
		return ""
962
	}
963

964
	// JSON name always comes first. If there's no options then split[0] is
965
	// JSON name, if JSON name is not set, then split[0] is an empty string.
966
	split := strings.SplitN(tag, ",", 2)
967

968
	name := split[0]
969

970
	// However it is possible that the field is skipped when
971
	// (de-)serializing from/to JSON, in which case assume that there is no
972
	// tag name to use
973
	if name == "-" {
974
		return ""
975
	}
976
	return name
977
}
978

979
func prependPathToErrors(err error, path string) error {
980
	switch err2 := err.(type) {
981
	case Error:
982
		err2.Path = append([]string{path}, err2.Path...)
983
		return err2
984
	case Errors:
985
		errors := err2.Errors()
986
		for i, err3 := range errors {
987
			errors[i] = prependPathToErrors(err3, path)
988
		}
989
		return err2
990
	}
991
	return err
992
}
993

994
// ValidateArray performs validation according to condition iterator that validates every element of the array
995
func ValidateArray(array []interface{}, iterator ConditionIterator) bool {
996
	return Every(array, iterator)
997
}
998

999
// ValidateMap use validation map for fields.
1000
// result will be equal to `false` if there are any errors.
1001
// s is the map containing the data to be validated.
1002
// m is the validation map in the form:
1003
//   map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}}
1004
func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) {
1005
	if s == nil {
1006
		return true, nil
1007
	}
1008
	result := true
1009
	var err error
1010
	var errs Errors
1011
	var index int
1012
	val := reflect.ValueOf(s)
1013
	for key, value := range s {
1014
		presentResult := true
1015
		validator, ok := m[key]
1016
		if !ok {
1017
			presentResult = false
1018
			var err error
1019
			err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key)
1020
			err = prependPathToErrors(err, key)
1021
			errs = append(errs, err)
1022
		}
1023
		valueField := reflect.ValueOf(value)
1024
		mapResult := true
1025
		typeResult := true
1026
		structResult := true
1027
		resultField := true
1028
		switch subValidator := validator.(type) {
1029
		case map[string]interface{}:
1030
			var err error
1031
			if v, ok := value.(map[string]interface{}); !ok {
1032
				mapResult = false
1033
				err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String())
1034
				err = prependPathToErrors(err, key)
1035
				errs = append(errs, err)
1036
			} else {
1037
				mapResult, err = ValidateMap(v, subValidator)
1038
				if err != nil {
1039
					mapResult = false
1040
					err = prependPathToErrors(err, key)
1041
					errs = append(errs, err)
1042
				}
1043
			}
1044
		case string:
1045
			if (valueField.Kind() == reflect.Struct ||
1046
				(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
1047
				subValidator != "-" {
1048
				var err error
1049
				structResult, err = ValidateStruct(valueField.Interface())
1050
				if err != nil {
1051
					err = prependPathToErrors(err, key)
1052
					errs = append(errs, err)
1053
				}
1054
			}
1055
			resultField, err = typeCheck(valueField, reflect.StructField{
1056
				Name:      key,
1057
				PkgPath:   "",
1058
				Type:      val.Type(),
1059
				Tag:       reflect.StructTag(fmt.Sprintf("%s:%q", tagName, subValidator)),
1060
				Offset:    0,
1061
				Index:     []int{index},
1062
				Anonymous: false,
1063
			}, val, nil)
1064
			if err != nil {
1065
				errs = append(errs, err)
1066
			}
1067
		case nil:
1068
			// already handlerd when checked before
1069
		default:
1070
			typeResult = false
1071
			err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String())
1072
			err = prependPathToErrors(err, key)
1073
			errs = append(errs, err)
1074
		}
1075
		result = result && presentResult && typeResult && resultField && structResult && mapResult
1076
		index++
1077
	}
1078
	// checks required keys
1079
	requiredResult := true
1080
	for key, value := range m {
1081
		if schema, ok := value.(string); ok {
1082
			tags := parseTagIntoMap(schema)
1083
			if required, ok := tags["required"]; ok {
1084
				if _, ok := s[key]; !ok {
1085
					requiredResult = false
1086
					if required.customErrorMessage != "" {
1087
						err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}}
1088
					} else {
1089
						err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}}
1090
					}
1091
					errs = append(errs, err)
1092
				}
1093
			}
1094
		}
1095
	}
1096

1097
	if len(errs) > 0 {
1098
		err = errs
1099
	}
1100
	return result && requiredResult, err
1101
}
1102

1103
// ValidateStruct use tags for fields.
1104
// result will be equal to `false` if there are any errors.
1105
// todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail)
1106
func ValidateStruct(s interface{}) (bool, error) {
1107
	if s == nil {
1108
		return true, nil
1109
	}
1110
	result := true
1111
	var err error
1112
	val := reflect.ValueOf(s)
1113
	if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
1114
		val = val.Elem()
1115
	}
1116
	// we only accept structs
1117
	if val.Kind() != reflect.Struct {
1118
		return false, fmt.Errorf("function only accepts structs; got %s", val.Kind())
1119
	}
1120
	var errs Errors
1121
	for i := 0; i < val.NumField(); i++ {
1122
		valueField := val.Field(i)
1123
		typeField := val.Type().Field(i)
1124
		if typeField.PkgPath != "" {
1125
			continue // Private field
1126
		}
1127
		structResult := true
1128
		if valueField.Kind() == reflect.Interface {
1129
			valueField = valueField.Elem()
1130
		}
1131
		if (valueField.Kind() == reflect.Struct ||
1132
			(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
1133
			typeField.Tag.Get(tagName) != "-" {
1134
			var err error
1135
			structResult, err = ValidateStruct(valueField.Interface())
1136
			if err != nil {
1137
				err = prependPathToErrors(err, typeField.Name)
1138
				errs = append(errs, err)
1139
			}
1140
		}
1141
		resultField, err2 := typeCheck(valueField, typeField, val, nil)
1142
		if err2 != nil {
1143

1144
			// Replace structure name with JSON name if there is a tag on the variable
1145
			jsonTag := toJSONName(typeField.Tag.Get("json"))
1146
			if jsonTag != "" {
1147
				switch jsonError := err2.(type) {
1148
				case Error:
1149
					jsonError.Name = jsonTag
1150
					err2 = jsonError
1151
				case Errors:
1152
					for i2, err3 := range jsonError {
1153
						switch customErr := err3.(type) {
1154
						case Error:
1155
							customErr.Name = jsonTag
1156
							jsonError[i2] = customErr
1157
						}
1158
					}
1159

1160
					err2 = jsonError
1161
				}
1162
			}
1163

1164
			errs = append(errs, err2)
1165
		}
1166
		result = result && resultField && structResult
1167
	}
1168
	if len(errs) > 0 {
1169
		err = errs
1170
	}
1171
	return result, err
1172
}
1173

1174
// ValidateStructAsync performs async validation of the struct and returns results through the channels
1175
func ValidateStructAsync(s interface{}) (<-chan bool, <-chan error) {
1176
	res := make(chan bool)
1177
	errors := make(chan error)
1178

1179
	go func() {
1180
		defer close(res)
1181
		defer close(errors)
1182

1183
		isValid, isFailed := ValidateStruct(s)
1184

1185
		res <- isValid
1186
		errors <- isFailed
1187
	}()
1188

1189
	return res, errors
1190
}
1191

1192
// ValidateMapAsync performs async validation of the map and returns results through the channels
1193
func ValidateMapAsync(s map[string]interface{}, m map[string]interface{}) (<-chan bool, <-chan error) {
1194
	res := make(chan bool)
1195
	errors := make(chan error)
1196

1197
	go func() {
1198
		defer close(res)
1199
		defer close(errors)
1200

1201
		isValid, isFailed := ValidateMap(s, m)
1202

1203
		res <- isValid
1204
		errors <- isFailed
1205
	}()
1206

1207
	return res, errors
1208
}
1209

1210
// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
1211
func parseTagIntoMap(tag string) tagOptionsMap {
1212
	optionsMap := make(tagOptionsMap)
1213
	options := strings.Split(tag, ",")
1214

1215
	for i, option := range options {
1216
		option = strings.TrimSpace(option)
1217

1218
		validationOptions := strings.Split(option, "~")
1219
		if !isValidTag(validationOptions[0]) {
1220
			continue
1221
		}
1222
		if len(validationOptions) == 2 {
1223
			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i}
1224
		} else {
1225
			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i}
1226
		}
1227
	}
1228
	return optionsMap
1229
}
1230

1231
func isValidTag(s string) bool {
1232
	if s == "" {
1233
		return false
1234
	}
1235
	for _, c := range s {
1236
		switch {
1237
		case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
1238
			// Backslash and quote chars are reserved, but
1239
			// otherwise any punctuation chars are allowed
1240
			// in a tag name.
1241
		default:
1242
			if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
1243
				return false
1244
			}
1245
		}
1246
	}
1247
	return true
1248
}
1249

1250
// IsSSN will validate the given string as a U.S. Social Security Number
1251
func IsSSN(str string) bool {
1252
	if str == "" || len(str) != 11 {
1253
		return false
1254
	}
1255
	return rxSSN.MatchString(str)
1256
}
1257

1258
// IsSemver checks if string is valid semantic version
1259
func IsSemver(str string) bool {
1260
	return rxSemver.MatchString(str)
1261
}
1262

1263
// IsType checks if interface is of some type
1264
func IsType(v interface{}, params ...string) bool {
1265
	if len(params) == 1 {
1266
		typ := params[0]
1267
		return strings.Replace(reflect.TypeOf(v).String(), " ", "", -1) == strings.Replace(typ, " ", "", -1)
1268
	}
1269
	return false
1270
}
1271

1272
// IsTime checks if string is valid according to given format
1273
func IsTime(str string, format string) bool {
1274
	_, err := time.Parse(format, str)
1275
	return err == nil
1276
}
1277

1278
// IsUnixTime checks if string is valid unix timestamp value
1279
func IsUnixTime(str string) bool {
1280
	if _, err := strconv.Atoi(str); err == nil {
1281
		return true
1282
	}
1283
	return false
1284
}
1285

1286
// IsRFC3339 checks if string is valid timestamp value according to RFC3339
1287
func IsRFC3339(str string) bool {
1288
	return IsTime(str, time.RFC3339)
1289
}
1290

1291
// IsRFC3339WithoutZone checks if string is valid timestamp value according to RFC3339 which excludes the timezone.
1292
func IsRFC3339WithoutZone(str string) bool {
1293
	return IsTime(str, rfc3339WithoutZone)
1294
}
1295

1296
// IsISO4217 checks if string is valid ISO currency code
1297
func IsISO4217(str string) bool {
1298
	for _, currency := range ISO4217List {
1299
		if str == currency {
1300
			return true
1301
		}
1302
	}
1303

1304
	return false
1305
}
1306

1307
// ByteLength checks string's length
1308
func ByteLength(str string, params ...string) bool {
1309
	if len(params) == 2 {
1310
		min, _ := ToInt(params[0])
1311
		max, _ := ToInt(params[1])
1312
		return len(str) >= int(min) && len(str) <= int(max)
1313
	}
1314

1315
	return false
1316
}
1317

1318
// RuneLength checks string's length
1319
// Alias for StringLength
1320
func RuneLength(str string, params ...string) bool {
1321
	return StringLength(str, params...)
1322
}
1323

1324
// IsRsaPub checks whether string is valid RSA key
1325
// Alias for IsRsaPublicKey
1326
func IsRsaPub(str string, params ...string) bool {
1327
	if len(params) == 1 {
1328
		len, _ := ToInt(params[0])
1329
		return IsRsaPublicKey(str, int(len))
1330
	}
1331

1332
	return false
1333
}
1334

1335
// StringMatches checks if a string matches a given pattern.
1336
func StringMatches(s string, params ...string) bool {
1337
	if len(params) == 1 {
1338
		pattern := params[0]
1339
		return Matches(s, pattern)
1340
	}
1341
	return false
1342
}
1343

1344
// StringLength checks string's length (including multi byte strings)
1345
func StringLength(str string, params ...string) bool {
1346

1347
	if len(params) == 2 {
1348
		strLength := utf8.RuneCountInString(str)
1349
		min, _ := ToInt(params[0])
1350
		max, _ := ToInt(params[1])
1351
		return strLength >= int(min) && strLength <= int(max)
1352
	}
1353

1354
	return false
1355
}
1356

1357
// MinStringLength checks string's minimum length (including multi byte strings)
1358
func MinStringLength(str string, params ...string) bool {
1359

1360
	if len(params) == 1 {
1361
		strLength := utf8.RuneCountInString(str)
1362
		min, _ := ToInt(params[0])
1363
		return strLength >= int(min)
1364
	}
1365

1366
	return false
1367
}
1368

1369
// MaxStringLength checks string's maximum length (including multi byte strings)
1370
func MaxStringLength(str string, params ...string) bool {
1371

1372
	if len(params) == 1 {
1373
		strLength := utf8.RuneCountInString(str)
1374
		max, _ := ToInt(params[0])
1375
		return strLength <= int(max)
1376
	}
1377

1378
	return false
1379
}
1380

1381
// Range checks string's length
1382
func Range(str string, params ...string) bool {
1383
	if len(params) == 2 {
1384
		value, _ := ToFloat(str)
1385
		min, _ := ToFloat(params[0])
1386
		max, _ := ToFloat(params[1])
1387
		return InRange(value, min, max)
1388
	}
1389

1390
	return false
1391
}
1392

1393
// IsInRaw checks if string is in list of allowed values
1394
func IsInRaw(str string, params ...string) bool {
1395
	if len(params) == 1 {
1396
		rawParams := params[0]
1397

1398
		parsedParams := strings.Split(rawParams, "|")
1399

1400
		return IsIn(str, parsedParams...)
1401
	}
1402

1403
	return false
1404
}
1405

1406
// IsIn checks if string str is a member of the set of strings params
1407
func IsIn(str string, params ...string) bool {
1408
	for _, param := range params {
1409
		if str == param {
1410
			return true
1411
		}
1412
	}
1413

1414
	return false
1415
}
1416

1417
func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) {
1418
	if nilPtrAllowedByRequired {
1419
		k := v.Kind()
1420
		if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() {
1421
			return true, nil
1422
		}
1423
	}
1424

1425
	if requiredOption, isRequired := options["required"]; isRequired {
1426
		if len(requiredOption.customErrorMessage) > 0 {
1427
			return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}}
1428
		}
1429
		return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}}
1430
	} else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional {
1431
		return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}}
1432
	}
1433
	// not required and empty is valid
1434
	return true, nil
1435
}
1436

1437
func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) {
1438
	if !v.IsValid() {
1439
		return false, nil
1440
	}
1441

1442
	tag := t.Tag.Get(tagName)
1443

1444
	// checks if the field should be ignored
1445
	switch tag {
1446
	case "":
1447
		if v.Kind() != reflect.Slice && v.Kind() != reflect.Map {
1448
			if !fieldsRequiredByDefault {
1449
				return true, nil
1450
			}
1451
			return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}}
1452
		}
1453
	case "-":
1454
		return true, nil
1455
	}
1456

1457
	isRootType := false
1458
	if options == nil {
1459
		isRootType = true
1460
		options = parseTagIntoMap(tag)
1461
	}
1462

1463
	if isEmptyValue(v) {
1464
		// an empty value is not validated, checks only required
1465
		isValid, resultErr = checkRequired(v, t, options)
1466
		for key := range options {
1467
			delete(options, key)
1468
		}
1469
		return isValid, resultErr
1470
	}
1471

1472
	var customTypeErrors Errors
1473
	optionsOrder := options.orderedKeys()
1474
	for _, validatorName := range optionsOrder {
1475
		validatorStruct := options[validatorName]
1476
		if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok {
1477
			delete(options, validatorName)
1478

1479
			if result := validatefunc(v.Interface(), o.Interface()); !result {
1480
				if len(validatorStruct.customErrorMessage) > 0 {
1481
					customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)})
1482
					continue
1483
				}
1484
				customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)})
1485
			}
1486
		}
1487
	}
1488

1489
	if len(customTypeErrors.Errors()) > 0 {
1490
		return false, customTypeErrors
1491
	}
1492

1493
	if isRootType {
1494
		// Ensure that we've checked the value by all specified validators before report that the value is valid
1495
		defer func() {
1496
			delete(options, "optional")
1497
			delete(options, "required")
1498

1499
			if isValid && resultErr == nil && len(options) != 0 {
1500
				optionsOrder := options.orderedKeys()
1501
				for _, validator := range optionsOrder {
1502
					isValid = false
1503
					resultErr = Error{t.Name, fmt.Errorf(
1504
						"The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}}
1505
					return
1506
				}
1507
			}
1508
		}()
1509
	}
1510

1511
	for _, validatorSpec := range optionsOrder {
1512
		validatorStruct := options[validatorSpec]
1513
		var negate bool
1514
		validator := validatorSpec
1515
		customMsgExists := len(validatorStruct.customErrorMessage) > 0
1516

1517
		// checks whether the tag looks like '!something' or 'something'
1518
		if validator[0] == '!' {
1519
			validator = validator[1:]
1520
			negate = true
1521
		}
1522

1523
		// checks for interface param validators
1524
		for key, value := range InterfaceParamTagRegexMap {
1525
			ps := value.FindStringSubmatch(validator)
1526
			if len(ps) == 0 {
1527
				continue
1528
			}
1529

1530
			validatefunc, ok := InterfaceParamTagMap[key]
1531
			if !ok {
1532
				continue
1533
			}
1534

1535
			delete(options, validatorSpec)
1536

1537
			field := fmt.Sprint(v)
1538
			if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) {
1539
				if customMsgExists {
1540
					return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1541
				}
1542
				if negate {
1543
					return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1544
				}
1545
				return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1546
			}
1547
		}
1548
	}
1549

1550
	switch v.Kind() {
1551
	case reflect.Bool,
1552
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
1553
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
1554
		reflect.Float32, reflect.Float64,
1555
		reflect.String:
1556
		// for each tag option checks the map of validator functions
1557
		for _, validatorSpec := range optionsOrder {
1558
			validatorStruct := options[validatorSpec]
1559
			var negate bool
1560
			validator := validatorSpec
1561
			customMsgExists := len(validatorStruct.customErrorMessage) > 0
1562

1563
			// checks whether the tag looks like '!something' or 'something'
1564
			if validator[0] == '!' {
1565
				validator = validator[1:]
1566
				negate = true
1567
			}
1568

1569
			// checks for param validators
1570
			for key, value := range ParamTagRegexMap {
1571
				ps := value.FindStringSubmatch(validator)
1572
				if len(ps) == 0 {
1573
					continue
1574
				}
1575

1576
				validatefunc, ok := ParamTagMap[key]
1577
				if !ok {
1578
					continue
1579
				}
1580

1581
				delete(options, validatorSpec)
1582

1583
				switch v.Kind() {
1584
				case reflect.String,
1585
					reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
1586
					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
1587
					reflect.Float32, reflect.Float64:
1588

1589
					field := fmt.Sprint(v) // make value into string, then validate with regex
1590
					if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) {
1591
						if customMsgExists {
1592
							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1593
						}
1594
						if negate {
1595
							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1596
						}
1597
						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1598
					}
1599
				default:
1600
					// type not yet supported, fail
1601
					return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}}
1602
				}
1603
			}
1604

1605
			if validatefunc, ok := TagMap[validator]; ok {
1606
				delete(options, validatorSpec)
1607

1608
				switch v.Kind() {
1609
				case reflect.String,
1610
					reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
1611
					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
1612
					reflect.Float32, reflect.Float64:
1613
					field := fmt.Sprint(v) // make value into string, then validate with regex
1614
					if result := validatefunc(field); !result && !negate || result && negate {
1615
						if customMsgExists {
1616
							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1617
						}
1618
						if negate {
1619
							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1620
						}
1621
						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1622
					}
1623
				default:
1624
					//Not Yet Supported Types (Fail here!)
1625
					err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v)
1626
					return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}}
1627
				}
1628
			}
1629
		}
1630
		return true, nil
1631
	case reflect.Map:
1632
		if v.Type().Key().Kind() != reflect.String {
1633
			return false, &UnsupportedTypeError{v.Type()}
1634
		}
1635
		var sv stringValues
1636
		sv = v.MapKeys()
1637
		sort.Sort(sv)
1638
		result := true
1639
		for i, k := range sv {
1640
			var resultItem bool
1641
			var err error
1642
			if v.MapIndex(k).Kind() != reflect.Struct {
1643
				resultItem, err = typeCheck(v.MapIndex(k), t, o, options)
1644
				if err != nil {
1645
					return false, err
1646
				}
1647
			} else {
1648
				resultItem, err = ValidateStruct(v.MapIndex(k).Interface())
1649
				if err != nil {
1650
					err = prependPathToErrors(err, t.Name+"."+sv[i].Interface().(string))
1651
					return false, err
1652
				}
1653
			}
1654
			result = result && resultItem
1655
		}
1656
		return result, nil
1657
	case reflect.Slice, reflect.Array:
1658
		result := true
1659
		for i := 0; i < v.Len(); i++ {
1660
			var resultItem bool
1661
			var err error
1662
			if v.Index(i).Kind() != reflect.Struct {
1663
				resultItem, err = typeCheck(v.Index(i), t, o, options)
1664
				if err != nil {
1665
					return false, err
1666
				}
1667
			} else {
1668
				resultItem, err = ValidateStruct(v.Index(i).Interface())
1669
				if err != nil {
1670
					err = prependPathToErrors(err, t.Name+"."+strconv.Itoa(i))
1671
					return false, err
1672
				}
1673
			}
1674
			result = result && resultItem
1675
		}
1676
		return result, nil
1677
	case reflect.Interface:
1678
		// If the value is an interface then encode its element
1679
		if v.IsNil() {
1680
			return true, nil
1681
		}
1682
		return ValidateStruct(v.Interface())
1683
	case reflect.Ptr:
1684
		// If the value is a pointer then checks its element
1685
		if v.IsNil() {
1686
			return true, nil
1687
		}
1688
		return typeCheck(v.Elem(), t, o, options)
1689
	case reflect.Struct:
1690
		return true, nil
1691
	default:
1692
		return false, &UnsupportedTypeError{v.Type()}
1693
	}
1694
}
1695

1696
func stripParams(validatorString string) string {
1697
	return paramsRegexp.ReplaceAllString(validatorString, "")
1698
}
1699

1700
// isEmptyValue checks whether value empty or not
1701
func isEmptyValue(v reflect.Value) bool {
1702
	switch v.Kind() {
1703
	case reflect.String, reflect.Array:
1704
		return v.Len() == 0
1705
	case reflect.Map, reflect.Slice:
1706
		return v.Len() == 0 || v.IsNil()
1707
	case reflect.Bool:
1708
		return !v.Bool()
1709
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1710
		return v.Int() == 0
1711
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1712
		return v.Uint() == 0
1713
	case reflect.Float32, reflect.Float64:
1714
		return v.Float() == 0
1715
	case reflect.Interface, reflect.Ptr:
1716
		return v.IsNil()
1717
	}
1718

1719
	return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
1720
}
1721

1722
// ErrorByField returns error for specified field of the struct
1723
// validated by ValidateStruct or empty string if there are no errors
1724
// or this field doesn't exists or doesn't have any errors.
1725
func ErrorByField(e error, field string) string {
1726
	if e == nil {
1727
		return ""
1728
	}
1729
	return ErrorsByField(e)[field]
1730
}
1731

1732
// ErrorsByField returns map of errors of the struct validated
1733
// by ValidateStruct or empty map if there are no errors.
1734
func ErrorsByField(e error) map[string]string {
1735
	m := make(map[string]string)
1736
	if e == nil {
1737
		return m
1738
	}
1739
	// prototype for ValidateStruct
1740

1741
	switch e := e.(type) {
1742
	case Error:
1743
		m[e.Name] = e.Err.Error()
1744
	case Errors:
1745
		for _, item := range e.Errors() {
1746
			n := ErrorsByField(item)
1747
			for k, v := range n {
1748
				m[k] = v
1749
			}
1750
		}
1751
	}
1752

1753
	return m
1754
}
1755

1756
// Error returns string equivalent for reflect.Type
1757
func (e *UnsupportedTypeError) Error() string {
1758
	return "validator: unsupported type: " + e.Type.String()
1759
}
1760

1761
func (sv stringValues) Len() int           { return len(sv) }
1762
func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
1763
func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
1764
func (sv stringValues) get(i int) string   { return sv[i].String() }
1765

1766
func IsE164(str string) bool {
1767
	return rxE164.MatchString(str)
1768
}
1769

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.