podman

Форк
0
2955 строк · 84.6 Кб
1
package validator
2

3
import (
4
	"bytes"
5
	"context"
6
	"crypto/sha256"
7
	"encoding/hex"
8
	"encoding/json"
9
	"fmt"
10
	"io/fs"
11
	"net"
12
	"net/url"
13
	"os"
14
	"reflect"
15
	"strconv"
16
	"strings"
17
	"sync"
18
	"syscall"
19
	"time"
20
	"unicode/utf8"
21

22
	"golang.org/x/crypto/sha3"
23
	"golang.org/x/text/language"
24

25
	"github.com/gabriel-vasile/mimetype"
26
	urn "github.com/leodido/go-urn"
27
)
28

29
// Func accepts a FieldLevel interface for all validation needs. The return
30
// value should be true when validation succeeds.
31
type Func func(fl FieldLevel) bool
32

33
// FuncCtx accepts a context.Context and FieldLevel interface for all
34
// validation needs. The return value should be true when validation succeeds.
35
type FuncCtx func(ctx context.Context, fl FieldLevel) bool
36

37
// wrapFunc wraps normal Func makes it compatible with FuncCtx
38
func wrapFunc(fn Func) FuncCtx {
39
	if fn == nil {
40
		return nil // be sure not to wrap a bad function.
41
	}
42
	return func(ctx context.Context, fl FieldLevel) bool {
43
		return fn(fl)
44
	}
45
}
46

47
var (
48
	restrictedTags = map[string]struct{}{
49
		diveTag:           {},
50
		keysTag:           {},
51
		endKeysTag:        {},
52
		structOnlyTag:     {},
53
		omitempty:         {},
54
		omitnil:           {},
55
		skipValidationTag: {},
56
		utf8HexComma:      {},
57
		utf8Pipe:          {},
58
		noStructLevelTag:  {},
59
		requiredTag:       {},
60
		isdefault:         {},
61
	}
62

63
	// bakedInAliases is a default mapping of a single validation tag that
64
	// defines a common or complex set of validation(s) to simplify
65
	// adding validation to structs.
66
	bakedInAliases = map[string]string{
67
		"iscolor":      "hexcolor|rgb|rgba|hsl|hsla",
68
		"country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
69
	}
70

71
	// bakedInValidators is the default map of ValidationFunc
72
	// you can add, remove or even replace items to suite your needs,
73
	// or even disregard and use your own map if so desired.
74
	bakedInValidators = map[string]Func{
75
		"required":                      hasValue,
76
		"required_if":                   requiredIf,
77
		"required_unless":               requiredUnless,
78
		"skip_unless":                   skipUnless,
79
		"required_with":                 requiredWith,
80
		"required_with_all":             requiredWithAll,
81
		"required_without":              requiredWithout,
82
		"required_without_all":          requiredWithoutAll,
83
		"excluded_if":                   excludedIf,
84
		"excluded_unless":               excludedUnless,
85
		"excluded_with":                 excludedWith,
86
		"excluded_with_all":             excludedWithAll,
87
		"excluded_without":              excludedWithout,
88
		"excluded_without_all":          excludedWithoutAll,
89
		"isdefault":                     isDefault,
90
		"len":                           hasLengthOf,
91
		"min":                           hasMinOf,
92
		"max":                           hasMaxOf,
93
		"eq":                            isEq,
94
		"eq_ignore_case":                isEqIgnoreCase,
95
		"ne":                            isNe,
96
		"ne_ignore_case":                isNeIgnoreCase,
97
		"lt":                            isLt,
98
		"lte":                           isLte,
99
		"gt":                            isGt,
100
		"gte":                           isGte,
101
		"eqfield":                       isEqField,
102
		"eqcsfield":                     isEqCrossStructField,
103
		"necsfield":                     isNeCrossStructField,
104
		"gtcsfield":                     isGtCrossStructField,
105
		"gtecsfield":                    isGteCrossStructField,
106
		"ltcsfield":                     isLtCrossStructField,
107
		"ltecsfield":                    isLteCrossStructField,
108
		"nefield":                       isNeField,
109
		"gtefield":                      isGteField,
110
		"gtfield":                       isGtField,
111
		"ltefield":                      isLteField,
112
		"ltfield":                       isLtField,
113
		"fieldcontains":                 fieldContains,
114
		"fieldexcludes":                 fieldExcludes,
115
		"alpha":                         isAlpha,
116
		"alphanum":                      isAlphanum,
117
		"alphaunicode":                  isAlphaUnicode,
118
		"alphanumunicode":               isAlphanumUnicode,
119
		"boolean":                       isBoolean,
120
		"numeric":                       isNumeric,
121
		"number":                        isNumber,
122
		"hexadecimal":                   isHexadecimal,
123
		"hexcolor":                      isHEXColor,
124
		"rgb":                           isRGB,
125
		"rgba":                          isRGBA,
126
		"hsl":                           isHSL,
127
		"hsla":                          isHSLA,
128
		"e164":                          isE164,
129
		"email":                         isEmail,
130
		"url":                           isURL,
131
		"http_url":                      isHttpURL,
132
		"uri":                           isURI,
133
		"urn_rfc2141":                   isUrnRFC2141, // RFC 2141
134
		"file":                          isFile,
135
		"filepath":                      isFilePath,
136
		"base64":                        isBase64,
137
		"base64url":                     isBase64URL,
138
		"base64rawurl":                  isBase64RawURL,
139
		"contains":                      contains,
140
		"containsany":                   containsAny,
141
		"containsrune":                  containsRune,
142
		"excludes":                      excludes,
143
		"excludesall":                   excludesAll,
144
		"excludesrune":                  excludesRune,
145
		"startswith":                    startsWith,
146
		"endswith":                      endsWith,
147
		"startsnotwith":                 startsNotWith,
148
		"endsnotwith":                   endsNotWith,
149
		"image":                         isImage,
150
		"isbn":                          isISBN,
151
		"isbn10":                        isISBN10,
152
		"isbn13":                        isISBN13,
153
		"issn":                          isISSN,
154
		"eth_addr":                      isEthereumAddress,
155
		"eth_addr_checksum":             isEthereumAddressChecksum,
156
		"btc_addr":                      isBitcoinAddress,
157
		"btc_addr_bech32":               isBitcoinBech32Address,
158
		"uuid":                          isUUID,
159
		"uuid3":                         isUUID3,
160
		"uuid4":                         isUUID4,
161
		"uuid5":                         isUUID5,
162
		"uuid_rfc4122":                  isUUIDRFC4122,
163
		"uuid3_rfc4122":                 isUUID3RFC4122,
164
		"uuid4_rfc4122":                 isUUID4RFC4122,
165
		"uuid5_rfc4122":                 isUUID5RFC4122,
166
		"ulid":                          isULID,
167
		"md4":                           isMD4,
168
		"md5":                           isMD5,
169
		"sha256":                        isSHA256,
170
		"sha384":                        isSHA384,
171
		"sha512":                        isSHA512,
172
		"ripemd128":                     isRIPEMD128,
173
		"ripemd160":                     isRIPEMD160,
174
		"tiger128":                      isTIGER128,
175
		"tiger160":                      isTIGER160,
176
		"tiger192":                      isTIGER192,
177
		"ascii":                         isASCII,
178
		"printascii":                    isPrintableASCII,
179
		"multibyte":                     hasMultiByteCharacter,
180
		"datauri":                       isDataURI,
181
		"latitude":                      isLatitude,
182
		"longitude":                     isLongitude,
183
		"ssn":                           isSSN,
184
		"ipv4":                          isIPv4,
185
		"ipv6":                          isIPv6,
186
		"ip":                            isIP,
187
		"cidrv4":                        isCIDRv4,
188
		"cidrv6":                        isCIDRv6,
189
		"cidr":                          isCIDR,
190
		"tcp4_addr":                     isTCP4AddrResolvable,
191
		"tcp6_addr":                     isTCP6AddrResolvable,
192
		"tcp_addr":                      isTCPAddrResolvable,
193
		"udp4_addr":                     isUDP4AddrResolvable,
194
		"udp6_addr":                     isUDP6AddrResolvable,
195
		"udp_addr":                      isUDPAddrResolvable,
196
		"ip4_addr":                      isIP4AddrResolvable,
197
		"ip6_addr":                      isIP6AddrResolvable,
198
		"ip_addr":                       isIPAddrResolvable,
199
		"unix_addr":                     isUnixAddrResolvable,
200
		"mac":                           isMAC,
201
		"hostname":                      isHostnameRFC952,  // RFC 952
202
		"hostname_rfc1123":              isHostnameRFC1123, // RFC 1123
203
		"fqdn":                          isFQDN,
204
		"unique":                        isUnique,
205
		"oneof":                         isOneOf,
206
		"html":                          isHTML,
207
		"html_encoded":                  isHTMLEncoded,
208
		"url_encoded":                   isURLEncoded,
209
		"dir":                           isDir,
210
		"dirpath":                       isDirPath,
211
		"json":                          isJSON,
212
		"jwt":                           isJWT,
213
		"hostname_port":                 isHostnamePort,
214
		"lowercase":                     isLowercase,
215
		"uppercase":                     isUppercase,
216
		"datetime":                      isDatetime,
217
		"timezone":                      isTimeZone,
218
		"iso3166_1_alpha2":              isIso3166Alpha2,
219
		"iso3166_1_alpha3":              isIso3166Alpha3,
220
		"iso3166_1_alpha_numeric":       isIso3166AlphaNumeric,
221
		"iso3166_2":                     isIso31662,
222
		"iso4217":                       isIso4217,
223
		"iso4217_numeric":               isIso4217Numeric,
224
		"bcp47_language_tag":            isBCP47LanguageTag,
225
		"postcode_iso3166_alpha2":       isPostcodeByIso3166Alpha2,
226
		"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
227
		"bic":                           isIsoBicFormat,
228
		"semver":                        isSemverFormat,
229
		"dns_rfc1035_label":             isDnsRFC1035LabelFormat,
230
		"credit_card":                   isCreditCard,
231
		"cve":                           isCveFormat,
232
		"luhn_checksum":                 hasLuhnChecksum,
233
		"mongodb":                       isMongoDB,
234
		"cron":                          isCron,
235
		"spicedb":                       isSpiceDB,
236
	}
237
)
238

239
var (
240
	oneofValsCache       = map[string][]string{}
241
	oneofValsCacheRWLock = sync.RWMutex{}
242
)
243

244
func parseOneOfParam2(s string) []string {
245
	oneofValsCacheRWLock.RLock()
246
	vals, ok := oneofValsCache[s]
247
	oneofValsCacheRWLock.RUnlock()
248
	if !ok {
249
		oneofValsCacheRWLock.Lock()
250
		vals = splitParamsRegex.FindAllString(s, -1)
251
		for i := 0; i < len(vals); i++ {
252
			vals[i] = strings.Replace(vals[i], "'", "", -1)
253
		}
254
		oneofValsCache[s] = vals
255
		oneofValsCacheRWLock.Unlock()
256
	}
257
	return vals
258
}
259

260
func isURLEncoded(fl FieldLevel) bool {
261
	return uRLEncodedRegex.MatchString(fl.Field().String())
262
}
263

264
func isHTMLEncoded(fl FieldLevel) bool {
265
	return hTMLEncodedRegex.MatchString(fl.Field().String())
266
}
267

268
func isHTML(fl FieldLevel) bool {
269
	return hTMLRegex.MatchString(fl.Field().String())
270
}
271

272
func isOneOf(fl FieldLevel) bool {
273
	vals := parseOneOfParam2(fl.Param())
274

275
	field := fl.Field()
276

277
	var v string
278
	switch field.Kind() {
279
	case reflect.String:
280
		v = field.String()
281
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
282
		v = strconv.FormatInt(field.Int(), 10)
283
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
284
		v = strconv.FormatUint(field.Uint(), 10)
285
	default:
286
		panic(fmt.Sprintf("Bad field type %T", field.Interface()))
287
	}
288
	for i := 0; i < len(vals); i++ {
289
		if vals[i] == v {
290
			return true
291
		}
292
	}
293
	return false
294
}
295

296
// isUnique is the validation function for validating if each array|slice|map value is unique
297
func isUnique(fl FieldLevel) bool {
298
	field := fl.Field()
299
	param := fl.Param()
300
	v := reflect.ValueOf(struct{}{})
301

302
	switch field.Kind() {
303
	case reflect.Slice, reflect.Array:
304
		elem := field.Type().Elem()
305
		if elem.Kind() == reflect.Ptr {
306
			elem = elem.Elem()
307
		}
308

309
		if param == "" {
310
			m := reflect.MakeMap(reflect.MapOf(elem, v.Type()))
311

312
			for i := 0; i < field.Len(); i++ {
313
				m.SetMapIndex(reflect.Indirect(field.Index(i)), v)
314
			}
315
			return field.Len() == m.Len()
316
		}
317

318
		sf, ok := elem.FieldByName(param)
319
		if !ok {
320
			panic(fmt.Sprintf("Bad field name %s", param))
321
		}
322

323
		sfTyp := sf.Type
324
		if sfTyp.Kind() == reflect.Ptr {
325
			sfTyp = sfTyp.Elem()
326
		}
327

328
		m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type()))
329
		var fieldlen int
330
		for i := 0; i < field.Len(); i++ {
331
			key := reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param))
332
			if key.IsValid() {
333
				fieldlen++
334
				m.SetMapIndex(key, v)
335
			}
336
		}
337
		return fieldlen == m.Len()
338
	case reflect.Map:
339
		var m reflect.Value
340
		if field.Type().Elem().Kind() == reflect.Ptr {
341
			m = reflect.MakeMap(reflect.MapOf(field.Type().Elem().Elem(), v.Type()))
342
		} else {
343
			m = reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
344
		}
345

346
		for _, k := range field.MapKeys() {
347
			m.SetMapIndex(reflect.Indirect(field.MapIndex(k)), v)
348
		}
349

350
		return field.Len() == m.Len()
351
	default:
352
		if parent := fl.Parent(); parent.Kind() == reflect.Struct {
353
			uniqueField := parent.FieldByName(param)
354
			if uniqueField == reflect.ValueOf(nil) {
355
				panic(fmt.Sprintf("Bad field name provided %s", param))
356
			}
357

358
			if uniqueField.Kind() != field.Kind() {
359
				panic(fmt.Sprintf("Bad field type %T:%T", field.Interface(), uniqueField.Interface()))
360
			}
361

362
			return field.Interface() != uniqueField.Interface()
363
		}
364

365
		panic(fmt.Sprintf("Bad field type %T", field.Interface()))
366
	}
367
}
368

369
// isMAC is the validation function for validating if the field's value is a valid MAC address.
370
func isMAC(fl FieldLevel) bool {
371
	_, err := net.ParseMAC(fl.Field().String())
372

373
	return err == nil
374
}
375

376
// isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
377
func isCIDRv4(fl FieldLevel) bool {
378
	ip, net, err := net.ParseCIDR(fl.Field().String())
379

380
	return err == nil && ip.To4() != nil && net.IP.Equal(ip)
381
}
382

383
// isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
384
func isCIDRv6(fl FieldLevel) bool {
385
	ip, _, err := net.ParseCIDR(fl.Field().String())
386

387
	return err == nil && ip.To4() == nil
388
}
389

390
// isCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address.
391
func isCIDR(fl FieldLevel) bool {
392
	_, _, err := net.ParseCIDR(fl.Field().String())
393

394
	return err == nil
395
}
396

397
// isIPv4 is the validation function for validating if a value is a valid v4 IP address.
398
func isIPv4(fl FieldLevel) bool {
399
	ip := net.ParseIP(fl.Field().String())
400

401
	return ip != nil && ip.To4() != nil
402
}
403

404
// isIPv6 is the validation function for validating if the field's value is a valid v6 IP address.
405
func isIPv6(fl FieldLevel) bool {
406
	ip := net.ParseIP(fl.Field().String())
407

408
	return ip != nil && ip.To4() == nil
409
}
410

411
// isIP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
412
func isIP(fl FieldLevel) bool {
413
	ip := net.ParseIP(fl.Field().String())
414

415
	return ip != nil
416
}
417

418
// isSSN is the validation function for validating if the field's value is a valid SSN.
419
func isSSN(fl FieldLevel) bool {
420
	field := fl.Field()
421

422
	if field.Len() != 11 {
423
		return false
424
	}
425

426
	return sSNRegex.MatchString(field.String())
427
}
428

429
// isLongitude is the validation function for validating if the field's value is a valid longitude coordinate.
430
func isLongitude(fl FieldLevel) bool {
431
	field := fl.Field()
432

433
	var v string
434
	switch field.Kind() {
435
	case reflect.String:
436
		v = field.String()
437
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
438
		v = strconv.FormatInt(field.Int(), 10)
439
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
440
		v = strconv.FormatUint(field.Uint(), 10)
441
	case reflect.Float32:
442
		v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
443
	case reflect.Float64:
444
		v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
445
	default:
446
		panic(fmt.Sprintf("Bad field type %T", field.Interface()))
447
	}
448

449
	return longitudeRegex.MatchString(v)
450
}
451

452
// isLatitude is the validation function for validating if the field's value is a valid latitude coordinate.
453
func isLatitude(fl FieldLevel) bool {
454
	field := fl.Field()
455

456
	var v string
457
	switch field.Kind() {
458
	case reflect.String:
459
		v = field.String()
460
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
461
		v = strconv.FormatInt(field.Int(), 10)
462
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
463
		v = strconv.FormatUint(field.Uint(), 10)
464
	case reflect.Float32:
465
		v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
466
	case reflect.Float64:
467
		v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
468
	default:
469
		panic(fmt.Sprintf("Bad field type %T", field.Interface()))
470
	}
471

472
	return latitudeRegex.MatchString(v)
473
}
474

475
// isDataURI is the validation function for validating if the field's value is a valid data URI.
476
func isDataURI(fl FieldLevel) bool {
477
	uri := strings.SplitN(fl.Field().String(), ",", 2)
478

479
	if len(uri) != 2 {
480
		return false
481
	}
482

483
	if !dataURIRegex.MatchString(uri[0]) {
484
		return false
485
	}
486

487
	return base64Regex.MatchString(uri[1])
488
}
489

490
// hasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character.
491
func hasMultiByteCharacter(fl FieldLevel) bool {
492
	field := fl.Field()
493

494
	if field.Len() == 0 {
495
		return true
496
	}
497

498
	return multibyteRegex.MatchString(field.String())
499
}
500

501
// isPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character.
502
func isPrintableASCII(fl FieldLevel) bool {
503
	return printableASCIIRegex.MatchString(fl.Field().String())
504
}
505

506
// isASCII is the validation function for validating if the field's value is a valid ASCII character.
507
func isASCII(fl FieldLevel) bool {
508
	return aSCIIRegex.MatchString(fl.Field().String())
509
}
510

511
// isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
512
func isUUID5(fl FieldLevel) bool {
513
	return fieldMatchesRegexByStringerValOrString(uUID5Regex, fl)
514
}
515

516
// isUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
517
func isUUID4(fl FieldLevel) bool {
518
	return fieldMatchesRegexByStringerValOrString(uUID4Regex, fl)
519
}
520

521
// isUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
522
func isUUID3(fl FieldLevel) bool {
523
	return fieldMatchesRegexByStringerValOrString(uUID3Regex, fl)
524
}
525

526
// isUUID is the validation function for validating if the field's value is a valid UUID of any version.
527
func isUUID(fl FieldLevel) bool {
528
	return fieldMatchesRegexByStringerValOrString(uUIDRegex, fl)
529
}
530

531
// isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
532
func isUUID5RFC4122(fl FieldLevel) bool {
533
	return fieldMatchesRegexByStringerValOrString(uUID5RFC4122Regex, fl)
534
}
535

536
// isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
537
func isUUID4RFC4122(fl FieldLevel) bool {
538
	return fieldMatchesRegexByStringerValOrString(uUID4RFC4122Regex, fl)
539
}
540

541
// isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
542
func isUUID3RFC4122(fl FieldLevel) bool {
543
	return fieldMatchesRegexByStringerValOrString(uUID3RFC4122Regex, fl)
544
}
545

546
// isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
547
func isUUIDRFC4122(fl FieldLevel) bool {
548
	return fieldMatchesRegexByStringerValOrString(uUIDRFC4122Regex, fl)
549
}
550

551
// isULID is the validation function for validating if the field's value is a valid ULID.
552
func isULID(fl FieldLevel) bool {
553
	return fieldMatchesRegexByStringerValOrString(uLIDRegex, fl)
554
}
555

556
// isMD4 is the validation function for validating if the field's value is a valid MD4.
557
func isMD4(fl FieldLevel) bool {
558
	return md4Regex.MatchString(fl.Field().String())
559
}
560

561
// isMD5 is the validation function for validating if the field's value is a valid MD5.
562
func isMD5(fl FieldLevel) bool {
563
	return md5Regex.MatchString(fl.Field().String())
564
}
565

566
// isSHA256 is the validation function for validating if the field's value is a valid SHA256.
567
func isSHA256(fl FieldLevel) bool {
568
	return sha256Regex.MatchString(fl.Field().String())
569
}
570

571
// isSHA384 is the validation function for validating if the field's value is a valid SHA384.
572
func isSHA384(fl FieldLevel) bool {
573
	return sha384Regex.MatchString(fl.Field().String())
574
}
575

576
// isSHA512 is the validation function for validating if the field's value is a valid SHA512.
577
func isSHA512(fl FieldLevel) bool {
578
	return sha512Regex.MatchString(fl.Field().String())
579
}
580

581
// isRIPEMD128 is the validation function for validating if the field's value is a valid PIPEMD128.
582
func isRIPEMD128(fl FieldLevel) bool {
583
	return ripemd128Regex.MatchString(fl.Field().String())
584
}
585

586
// isRIPEMD160 is the validation function for validating if the field's value is a valid PIPEMD160.
587
func isRIPEMD160(fl FieldLevel) bool {
588
	return ripemd160Regex.MatchString(fl.Field().String())
589
}
590

591
// isTIGER128 is the validation function for validating if the field's value is a valid TIGER128.
592
func isTIGER128(fl FieldLevel) bool {
593
	return tiger128Regex.MatchString(fl.Field().String())
594
}
595

596
// isTIGER160 is the validation function for validating if the field's value is a valid TIGER160.
597
func isTIGER160(fl FieldLevel) bool {
598
	return tiger160Regex.MatchString(fl.Field().String())
599
}
600

601
// isTIGER192 is the validation function for validating if the field's value is a valid isTIGER192.
602
func isTIGER192(fl FieldLevel) bool {
603
	return tiger192Regex.MatchString(fl.Field().String())
604
}
605

606
// isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
607
func isISBN(fl FieldLevel) bool {
608
	return isISBN10(fl) || isISBN13(fl)
609
}
610

611
// isISBN13 is the validation function for validating if the field's value is a valid v13 ISBN.
612
func isISBN13(fl FieldLevel) bool {
613
	s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4)
614

615
	if !iSBN13Regex.MatchString(s) {
616
		return false
617
	}
618

619
	var checksum int32
620
	var i int32
621

622
	factor := []int32{1, 3}
623

624
	for i = 0; i < 12; i++ {
625
		checksum += factor[i%2] * int32(s[i]-'0')
626
	}
627

628
	return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0
629
}
630

631
// isISBN10 is the validation function for validating if the field's value is a valid v10 ISBN.
632
func isISBN10(fl FieldLevel) bool {
633
	s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3)
634

635
	if !iSBN10Regex.MatchString(s) {
636
		return false
637
	}
638

639
	var checksum int32
640
	var i int32
641

642
	for i = 0; i < 9; i++ {
643
		checksum += (i + 1) * int32(s[i]-'0')
644
	}
645

646
	if s[9] == 'X' {
647
		checksum += 10 * 10
648
	} else {
649
		checksum += 10 * int32(s[9]-'0')
650
	}
651

652
	return checksum%11 == 0
653
}
654

655
// isISSN is the validation function for validating if the field's value is a valid ISSN.
656
func isISSN(fl FieldLevel) bool {
657
	s := fl.Field().String()
658

659
	if !iSSNRegex.MatchString(s) {
660
		return false
661
	}
662
	s = strings.ReplaceAll(s, "-", "")
663

664
	pos := 8
665
	checksum := 0
666

667
	for i := 0; i < 7; i++ {
668
		checksum += pos * int(s[i]-'0')
669
		pos--
670
	}
671

672
	if s[7] == 'X' {
673
		checksum += 10
674
	} else {
675
		checksum += int(s[7] - '0')
676
	}
677

678
	return checksum%11 == 0
679
}
680

681
// isEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
682
func isEthereumAddress(fl FieldLevel) bool {
683
	address := fl.Field().String()
684

685
	return ethAddressRegex.MatchString(address)
686
}
687

688
// isEthereumAddressChecksum is the validation function for validating if the field's value is a valid checksumed Ethereum address.
689
func isEthereumAddressChecksum(fl FieldLevel) bool {
690
	address := fl.Field().String()
691

692
	if !ethAddressRegex.MatchString(address) {
693
		return false
694
	}
695
	// Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
696
	address = address[2:] // Skip "0x" prefix.
697
	h := sha3.NewLegacyKeccak256()
698
	// hash.Hash's io.Writer implementation says it never returns an error. https://golang.org/pkg/hash/#Hash
699
	_, _ = h.Write([]byte(strings.ToLower(address)))
700
	hash := hex.EncodeToString(h.Sum(nil))
701

702
	for i := 0; i < len(address); i++ {
703
		if address[i] <= '9' { // Skip 0-9 digits: they don't have upper/lower-case.
704
			continue
705
		}
706
		if hash[i] > '7' && address[i] >= 'a' || hash[i] <= '7' && address[i] <= 'F' {
707
			return false
708
		}
709
	}
710

711
	return true
712
}
713

714
// isBitcoinAddress is the validation function for validating if the field's value is a valid btc address
715
func isBitcoinAddress(fl FieldLevel) bool {
716
	address := fl.Field().String()
717

718
	if !btcAddressRegex.MatchString(address) {
719
		return false
720
	}
721

722
	alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
723

724
	decode := [25]byte{}
725

726
	for _, n := range []byte(address) {
727
		d := bytes.IndexByte(alphabet, n)
728

729
		for i := 24; i >= 0; i-- {
730
			d += 58 * int(decode[i])
731
			decode[i] = byte(d % 256)
732
			d /= 256
733
		}
734
	}
735

736
	h := sha256.New()
737
	_, _ = h.Write(decode[:21])
738
	d := h.Sum([]byte{})
739
	h = sha256.New()
740
	_, _ = h.Write(d)
741

742
	validchecksum := [4]byte{}
743
	computedchecksum := [4]byte{}
744

745
	copy(computedchecksum[:], h.Sum(d[:0]))
746
	copy(validchecksum[:], decode[21:])
747

748
	return validchecksum == computedchecksum
749
}
750

751
// isBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address
752
func isBitcoinBech32Address(fl FieldLevel) bool {
753
	address := fl.Field().String()
754

755
	if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) {
756
		return false
757
	}
758

759
	am := len(address) % 8
760

761
	if am == 0 || am == 3 || am == 5 {
762
		return false
763
	}
764

765
	address = strings.ToLower(address)
766

767
	alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
768

769
	hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc
770
	addr := address[3:]
771
	dp := make([]int, 0, len(addr))
772

773
	for _, c := range addr {
774
		dp = append(dp, strings.IndexRune(alphabet, c))
775
	}
776

777
	ver := dp[0]
778

779
	if ver < 0 || ver > 16 {
780
		return false
781
	}
782

783
	if ver == 0 {
784
		if len(address) != 42 && len(address) != 62 {
785
			return false
786
		}
787
	}
788

789
	values := append(hr, dp...)
790

791
	GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
792

793
	p := 1
794

795
	for _, v := range values {
796
		b := p >> 25
797
		p = (p&0x1ffffff)<<5 ^ v
798

799
		for i := 0; i < 5; i++ {
800
			if (b>>uint(i))&1 == 1 {
801
				p ^= GEN[i]
802
			}
803
		}
804
	}
805

806
	if p != 1 {
807
		return false
808
	}
809

810
	b := uint(0)
811
	acc := 0
812
	mv := (1 << 5) - 1
813
	var sw []int
814

815
	for _, v := range dp[1 : len(dp)-6] {
816
		acc = (acc << 5) | v
817
		b += 5
818
		for b >= 8 {
819
			b -= 8
820
			sw = append(sw, (acc>>b)&mv)
821
		}
822
	}
823

824
	if len(sw) < 2 || len(sw) > 40 {
825
		return false
826
	}
827

828
	return true
829
}
830

831
// excludesRune is the validation function for validating that the field's value does not contain the rune specified within the param.
832
func excludesRune(fl FieldLevel) bool {
833
	return !containsRune(fl)
834
}
835

836
// excludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param.
837
func excludesAll(fl FieldLevel) bool {
838
	return !containsAny(fl)
839
}
840

841
// excludes is the validation function for validating that the field's value does not contain the text specified within the param.
842
func excludes(fl FieldLevel) bool {
843
	return !contains(fl)
844
}
845

846
// containsRune is the validation function for validating that the field's value contains the rune specified within the param.
847
func containsRune(fl FieldLevel) bool {
848
	r, _ := utf8.DecodeRuneInString(fl.Param())
849

850
	return strings.ContainsRune(fl.Field().String(), r)
851
}
852

853
// containsAny is the validation function for validating that the field's value contains any of the characters specified within the param.
854
func containsAny(fl FieldLevel) bool {
855
	return strings.ContainsAny(fl.Field().String(), fl.Param())
856
}
857

858
// contains is the validation function for validating that the field's value contains the text specified within the param.
859
func contains(fl FieldLevel) bool {
860
	return strings.Contains(fl.Field().String(), fl.Param())
861
}
862

863
// startsWith is the validation function for validating that the field's value starts with the text specified within the param.
864
func startsWith(fl FieldLevel) bool {
865
	return strings.HasPrefix(fl.Field().String(), fl.Param())
866
}
867

868
// endsWith is the validation function for validating that the field's value ends with the text specified within the param.
869
func endsWith(fl FieldLevel) bool {
870
	return strings.HasSuffix(fl.Field().String(), fl.Param())
871
}
872

873
// startsNotWith is the validation function for validating that the field's value does not start with the text specified within the param.
874
func startsNotWith(fl FieldLevel) bool {
875
	return !startsWith(fl)
876
}
877

878
// endsNotWith is the validation function for validating that the field's value does not end with the text specified within the param.
879
func endsNotWith(fl FieldLevel) bool {
880
	return !endsWith(fl)
881
}
882

883
// fieldContains is the validation function for validating if the current field's value contains the field specified by the param's value.
884
func fieldContains(fl FieldLevel) bool {
885
	field := fl.Field()
886

887
	currentField, _, ok := fl.GetStructFieldOK()
888

889
	if !ok {
890
		return false
891
	}
892

893
	return strings.Contains(field.String(), currentField.String())
894
}
895

896
// fieldExcludes is the validation function for validating if the current field's value excludes the field specified by the param's value.
897
func fieldExcludes(fl FieldLevel) bool {
898
	field := fl.Field()
899

900
	currentField, _, ok := fl.GetStructFieldOK()
901
	if !ok {
902
		return true
903
	}
904

905
	return !strings.Contains(field.String(), currentField.String())
906
}
907

908
// isNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value.
909
func isNeField(fl FieldLevel) bool {
910
	field := fl.Field()
911
	kind := field.Kind()
912

913
	currentField, currentKind, ok := fl.GetStructFieldOK()
914

915
	if !ok || currentKind != kind {
916
		return true
917
	}
918

919
	switch kind {
920

921
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
922
		return field.Int() != currentField.Int()
923

924
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
925
		return field.Uint() != currentField.Uint()
926

927
	case reflect.Float32, reflect.Float64:
928
		return field.Float() != currentField.Float()
929

930
	case reflect.Slice, reflect.Map, reflect.Array:
931
		return int64(field.Len()) != int64(currentField.Len())
932

933
	case reflect.Bool:
934
		return field.Bool() != currentField.Bool()
935

936
	case reflect.Struct:
937

938
		fieldType := field.Type()
939

940
		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
941

942
			t := currentField.Interface().(time.Time)
943
			fieldTime := field.Interface().(time.Time)
944

945
			return !fieldTime.Equal(t)
946
		}
947

948
		// Not Same underlying type i.e. struct and time
949
		if fieldType != currentField.Type() {
950
			return true
951
		}
952
	}
953

954
	// default reflect.String:
955
	return field.String() != currentField.String()
956
}
957

958
// isNe is the validation function for validating that the field's value does not equal the provided param value.
959
func isNe(fl FieldLevel) bool {
960
	return !isEq(fl)
961
}
962

963
// isNeIgnoreCase is the validation function for validating that the field's string value does not equal the
964
// provided param value. The comparison is case-insensitive
965
func isNeIgnoreCase(fl FieldLevel) bool {
966
	return !isEqIgnoreCase(fl)
967
}
968

969
// isLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value.
970
func isLteCrossStructField(fl FieldLevel) bool {
971
	field := fl.Field()
972
	kind := field.Kind()
973

974
	topField, topKind, ok := fl.GetStructFieldOK()
975
	if !ok || topKind != kind {
976
		return false
977
	}
978

979
	switch kind {
980

981
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
982
		return field.Int() <= topField.Int()
983

984
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
985
		return field.Uint() <= topField.Uint()
986

987
	case reflect.Float32, reflect.Float64:
988
		return field.Float() <= topField.Float()
989

990
	case reflect.Slice, reflect.Map, reflect.Array:
991
		return int64(field.Len()) <= int64(topField.Len())
992

993
	case reflect.Struct:
994

995
		fieldType := field.Type()
996

997
		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
998

999
			fieldTime := field.Convert(timeType).Interface().(time.Time)
1000
			topTime := topField.Convert(timeType).Interface().(time.Time)
1001

1002
			return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
1003
		}
1004

1005
		// Not Same underlying type i.e. struct and time
1006
		if fieldType != topField.Type() {
1007
			return false
1008
		}
1009
	}
1010

1011
	// default reflect.String:
1012
	return field.String() <= topField.String()
1013
}
1014

1015
// isLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value.
1016
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
1017
func isLtCrossStructField(fl FieldLevel) bool {
1018
	field := fl.Field()
1019
	kind := field.Kind()
1020

1021
	topField, topKind, ok := fl.GetStructFieldOK()
1022
	if !ok || topKind != kind {
1023
		return false
1024
	}
1025

1026
	switch kind {
1027

1028
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1029
		return field.Int() < topField.Int()
1030

1031
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1032
		return field.Uint() < topField.Uint()
1033

1034
	case reflect.Float32, reflect.Float64:
1035
		return field.Float() < topField.Float()
1036

1037
	case reflect.Slice, reflect.Map, reflect.Array:
1038
		return int64(field.Len()) < int64(topField.Len())
1039

1040
	case reflect.Struct:
1041

1042
		fieldType := field.Type()
1043

1044
		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1045

1046
			fieldTime := field.Convert(timeType).Interface().(time.Time)
1047
			topTime := topField.Convert(timeType).Interface().(time.Time)
1048

1049
			return fieldTime.Before(topTime)
1050
		}
1051

1052
		// Not Same underlying type i.e. struct and time
1053
		if fieldType != topField.Type() {
1054
			return false
1055
		}
1056
	}
1057

1058
	// default reflect.String:
1059
	return field.String() < topField.String()
1060
}
1061

1062
// isGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value.
1063
func isGteCrossStructField(fl FieldLevel) bool {
1064
	field := fl.Field()
1065
	kind := field.Kind()
1066

1067
	topField, topKind, ok := fl.GetStructFieldOK()
1068
	if !ok || topKind != kind {
1069
		return false
1070
	}
1071

1072
	switch kind {
1073

1074
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1075
		return field.Int() >= topField.Int()
1076

1077
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1078
		return field.Uint() >= topField.Uint()
1079

1080
	case reflect.Float32, reflect.Float64:
1081
		return field.Float() >= topField.Float()
1082

1083
	case reflect.Slice, reflect.Map, reflect.Array:
1084
		return int64(field.Len()) >= int64(topField.Len())
1085

1086
	case reflect.Struct:
1087

1088
		fieldType := field.Type()
1089

1090
		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1091

1092
			fieldTime := field.Convert(timeType).Interface().(time.Time)
1093
			topTime := topField.Convert(timeType).Interface().(time.Time)
1094

1095
			return fieldTime.After(topTime) || fieldTime.Equal(topTime)
1096
		}
1097

1098
		// Not Same underlying type i.e. struct and time
1099
		if fieldType != topField.Type() {
1100
			return false
1101
		}
1102
	}
1103

1104
	// default reflect.String:
1105
	return field.String() >= topField.String()
1106
}
1107

1108
// isGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value.
1109
func isGtCrossStructField(fl FieldLevel) bool {
1110
	field := fl.Field()
1111
	kind := field.Kind()
1112

1113
	topField, topKind, ok := fl.GetStructFieldOK()
1114
	if !ok || topKind != kind {
1115
		return false
1116
	}
1117

1118
	switch kind {
1119

1120
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1121
		return field.Int() > topField.Int()
1122

1123
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1124
		return field.Uint() > topField.Uint()
1125

1126
	case reflect.Float32, reflect.Float64:
1127
		return field.Float() > topField.Float()
1128

1129
	case reflect.Slice, reflect.Map, reflect.Array:
1130
		return int64(field.Len()) > int64(topField.Len())
1131

1132
	case reflect.Struct:
1133

1134
		fieldType := field.Type()
1135

1136
		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1137

1138
			fieldTime := field.Convert(timeType).Interface().(time.Time)
1139
			topTime := topField.Convert(timeType).Interface().(time.Time)
1140

1141
			return fieldTime.After(topTime)
1142
		}
1143

1144
		// Not Same underlying type i.e. struct and time
1145
		if fieldType != topField.Type() {
1146
			return false
1147
		}
1148
	}
1149

1150
	// default reflect.String:
1151
	return field.String() > topField.String()
1152
}
1153

1154
// isNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value.
1155
func isNeCrossStructField(fl FieldLevel) bool {
1156
	field := fl.Field()
1157
	kind := field.Kind()
1158

1159
	topField, currentKind, ok := fl.GetStructFieldOK()
1160
	if !ok || currentKind != kind {
1161
		return true
1162
	}
1163

1164
	switch kind {
1165

1166
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1167
		return topField.Int() != field.Int()
1168

1169
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1170
		return topField.Uint() != field.Uint()
1171

1172
	case reflect.Float32, reflect.Float64:
1173
		return topField.Float() != field.Float()
1174

1175
	case reflect.Slice, reflect.Map, reflect.Array:
1176
		return int64(topField.Len()) != int64(field.Len())
1177

1178
	case reflect.Bool:
1179
		return topField.Bool() != field.Bool()
1180

1181
	case reflect.Struct:
1182

1183
		fieldType := field.Type()
1184

1185
		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1186

1187
			t := field.Convert(timeType).Interface().(time.Time)
1188
			fieldTime := topField.Convert(timeType).Interface().(time.Time)
1189

1190
			return !fieldTime.Equal(t)
1191
		}
1192

1193
		// Not Same underlying type i.e. struct and time
1194
		if fieldType != topField.Type() {
1195
			return true
1196
		}
1197
	}
1198

1199
	// default reflect.String:
1200
	return topField.String() != field.String()
1201
}
1202

1203
// isEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value.
1204
func isEqCrossStructField(fl FieldLevel) bool {
1205
	field := fl.Field()
1206
	kind := field.Kind()
1207

1208
	topField, topKind, ok := fl.GetStructFieldOK()
1209
	if !ok || topKind != kind {
1210
		return false
1211
	}
1212

1213
	switch kind {
1214

1215
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1216
		return topField.Int() == field.Int()
1217

1218
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1219
		return topField.Uint() == field.Uint()
1220

1221
	case reflect.Float32, reflect.Float64:
1222
		return topField.Float() == field.Float()
1223

1224
	case reflect.Slice, reflect.Map, reflect.Array:
1225
		return int64(topField.Len()) == int64(field.Len())
1226

1227
	case reflect.Bool:
1228
		return topField.Bool() == field.Bool()
1229

1230
	case reflect.Struct:
1231

1232
		fieldType := field.Type()
1233

1234
		if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
1235

1236
			t := field.Convert(timeType).Interface().(time.Time)
1237
			fieldTime := topField.Convert(timeType).Interface().(time.Time)
1238

1239
			return fieldTime.Equal(t)
1240
		}
1241

1242
		// Not Same underlying type i.e. struct and time
1243
		if fieldType != topField.Type() {
1244
			return false
1245
		}
1246
	}
1247

1248
	// default reflect.String:
1249
	return topField.String() == field.String()
1250
}
1251

1252
// isEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value.
1253
func isEqField(fl FieldLevel) bool {
1254
	field := fl.Field()
1255
	kind := field.Kind()
1256

1257
	currentField, currentKind, ok := fl.GetStructFieldOK()
1258
	if !ok || currentKind != kind {
1259
		return false
1260
	}
1261

1262
	switch kind {
1263

1264
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1265
		return field.Int() == currentField.Int()
1266

1267
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1268
		return field.Uint() == currentField.Uint()
1269

1270
	case reflect.Float32, reflect.Float64:
1271
		return field.Float() == currentField.Float()
1272

1273
	case reflect.Slice, reflect.Map, reflect.Array:
1274
		return int64(field.Len()) == int64(currentField.Len())
1275

1276
	case reflect.Bool:
1277
		return field.Bool() == currentField.Bool()
1278

1279
	case reflect.Struct:
1280

1281
		fieldType := field.Type()
1282

1283
		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
1284

1285
			t := currentField.Convert(timeType).Interface().(time.Time)
1286
			fieldTime := field.Convert(timeType).Interface().(time.Time)
1287

1288
			return fieldTime.Equal(t)
1289
		}
1290

1291
		// Not Same underlying type i.e. struct and time
1292
		if fieldType != currentField.Type() {
1293
			return false
1294
		}
1295
	}
1296

1297
	// default reflect.String:
1298
	return field.String() == currentField.String()
1299
}
1300

1301
// isEq is the validation function for validating if the current field's value is equal to the param's value.
1302
func isEq(fl FieldLevel) bool {
1303
	field := fl.Field()
1304
	param := fl.Param()
1305

1306
	switch field.Kind() {
1307

1308
	case reflect.String:
1309
		return field.String() == param
1310

1311
	case reflect.Slice, reflect.Map, reflect.Array:
1312
		p := asInt(param)
1313

1314
		return int64(field.Len()) == p
1315

1316
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1317
		p := asIntFromType(field.Type(), param)
1318

1319
		return field.Int() == p
1320

1321
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1322
		p := asUint(param)
1323

1324
		return field.Uint() == p
1325

1326
	case reflect.Float32:
1327
		p := asFloat32(param)
1328

1329
		return field.Float() == p
1330

1331
	case reflect.Float64:
1332
		p := asFloat64(param)
1333

1334
		return field.Float() == p
1335

1336
	case reflect.Bool:
1337
		p := asBool(param)
1338

1339
		return field.Bool() == p
1340
	}
1341

1342
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1343
}
1344

1345
// isEqIgnoreCase is the validation function for validating if the current field's string value is
1346
// equal to the param's value.
1347
// The comparison is case-insensitive.
1348
func isEqIgnoreCase(fl FieldLevel) bool {
1349
	field := fl.Field()
1350
	param := fl.Param()
1351

1352
	switch field.Kind() {
1353

1354
	case reflect.String:
1355
		return strings.EqualFold(field.String(), param)
1356
	}
1357

1358
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1359
}
1360

1361
// isPostcodeByIso3166Alpha2 validates by value which is country code in iso 3166 alpha 2
1362
// example: `postcode_iso3166_alpha2=US`
1363
func isPostcodeByIso3166Alpha2(fl FieldLevel) bool {
1364
	field := fl.Field()
1365
	param := fl.Param()
1366

1367
	reg, found := postCodeRegexDict[param]
1368
	if !found {
1369
		return false
1370
	}
1371

1372
	return reg.MatchString(field.String())
1373
}
1374

1375
// isPostcodeByIso3166Alpha2Field validates by field which represents for a value of country code in iso 3166 alpha 2
1376
// example: `postcode_iso3166_alpha2_field=CountryCode`
1377
func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
1378
	field := fl.Field()
1379
	params := parseOneOfParam2(fl.Param())
1380

1381
	if len(params) != 1 {
1382
		return false
1383
	}
1384

1385
	currentField, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), params[0])
1386
	if !found {
1387
		return false
1388
	}
1389

1390
	if kind != reflect.String {
1391
		panic(fmt.Sprintf("Bad field type %T", currentField.Interface()))
1392
	}
1393

1394
	reg, found := postCodeRegexDict[currentField.String()]
1395
	if !found {
1396
		return false
1397
	}
1398

1399
	return reg.MatchString(field.String())
1400
}
1401

1402
// isBase64 is the validation function for validating if the current field's value is a valid base 64.
1403
func isBase64(fl FieldLevel) bool {
1404
	return base64Regex.MatchString(fl.Field().String())
1405
}
1406

1407
// isBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string.
1408
func isBase64URL(fl FieldLevel) bool {
1409
	return base64URLRegex.MatchString(fl.Field().String())
1410
}
1411

1412
// isBase64RawURL is the validation function for validating if the current field's value is a valid base64 URL safe string without '=' padding.
1413
func isBase64RawURL(fl FieldLevel) bool {
1414
	return base64RawURLRegex.MatchString(fl.Field().String())
1415
}
1416

1417
// isURI is the validation function for validating if the current field's value is a valid URI.
1418
func isURI(fl FieldLevel) bool {
1419
	field := fl.Field()
1420

1421
	switch field.Kind() {
1422
	case reflect.String:
1423

1424
		s := field.String()
1425

1426
		// checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
1427
		// emulate browser and strip the '#' suffix prior to validation. see issue-#237
1428
		if i := strings.Index(s, "#"); i > -1 {
1429
			s = s[:i]
1430
		}
1431

1432
		if len(s) == 0 {
1433
			return false
1434
		}
1435

1436
		_, err := url.ParseRequestURI(s)
1437

1438
		return err == nil
1439
	}
1440

1441
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1442
}
1443

1444
// isFileURL is the helper function for validating if the `path` valid file URL as per RFC8089
1445
func isFileURL(path string) bool {
1446
	if !strings.HasPrefix(path, "file:/") {
1447
		return false
1448
	}
1449
	_, err := url.ParseRequestURI(path)
1450
	return err == nil
1451
}
1452

1453
// isURL is the validation function for validating if the current field's value is a valid URL.
1454
func isURL(fl FieldLevel) bool {
1455
	field := fl.Field()
1456

1457
	switch field.Kind() {
1458
	case reflect.String:
1459

1460
		s := strings.ToLower(field.String())
1461

1462
		if len(s) == 0 {
1463
			return false
1464
		}
1465

1466
		if isFileURL(s) {
1467
			return true
1468
		}
1469

1470
		url, err := url.Parse(s)
1471
		if err != nil || url.Scheme == "" {
1472
			return false
1473
		}
1474

1475
		if url.Host == "" && url.Fragment == "" && url.Opaque == "" {
1476
			return false
1477
		}
1478

1479
		return true
1480
	}
1481

1482
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1483
}
1484

1485
// isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL.
1486
func isHttpURL(fl FieldLevel) bool {
1487
	if !isURL(fl) {
1488
		return false
1489
	}
1490

1491
	field := fl.Field()
1492
	switch field.Kind() {
1493
	case reflect.String:
1494

1495
		s := strings.ToLower(field.String())
1496

1497
		url, err := url.Parse(s)
1498
		if err != nil || url.Host == "" {
1499
			return false
1500
		}
1501

1502
		return url.Scheme == "http" || url.Scheme == "https"
1503
	}
1504

1505
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1506
}
1507

1508
// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141.
1509
func isUrnRFC2141(fl FieldLevel) bool {
1510
	field := fl.Field()
1511

1512
	switch field.Kind() {
1513
	case reflect.String:
1514

1515
		str := field.String()
1516

1517
		_, match := urn.Parse([]byte(str))
1518

1519
		return match
1520
	}
1521

1522
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1523
}
1524

1525
// isFile is the validation function for validating if the current field's value is a valid existing file path.
1526
func isFile(fl FieldLevel) bool {
1527
	field := fl.Field()
1528

1529
	switch field.Kind() {
1530
	case reflect.String:
1531
		fileInfo, err := os.Stat(field.String())
1532
		if err != nil {
1533
			return false
1534
		}
1535

1536
		return !fileInfo.IsDir()
1537
	}
1538

1539
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1540
}
1541

1542
// isImage is the validation function for validating if the current field's value contains the path to a valid image file
1543
func isImage(fl FieldLevel) bool {
1544
	mimetypes := map[string]bool{
1545
		"image/bmp":                true,
1546
		"image/cis-cod":            true,
1547
		"image/gif":                true,
1548
		"image/ief":                true,
1549
		"image/jpeg":               true,
1550
		"image/jp2":                true,
1551
		"image/jpx":                true,
1552
		"image/jpm":                true,
1553
		"image/pipeg":              true,
1554
		"image/png":                true,
1555
		"image/svg+xml":            true,
1556
		"image/tiff":               true,
1557
		"image/webp":               true,
1558
		"image/x-cmu-raster":       true,
1559
		"image/x-cmx":              true,
1560
		"image/x-icon":             true,
1561
		"image/x-portable-anymap":  true,
1562
		"image/x-portable-bitmap":  true,
1563
		"image/x-portable-graymap": true,
1564
		"image/x-portable-pixmap":  true,
1565
		"image/x-rgb":              true,
1566
		"image/x-xbitmap":          true,
1567
		"image/x-xpixmap":          true,
1568
		"image/x-xwindowdump":      true,
1569
	}
1570
	field := fl.Field()
1571

1572
	switch field.Kind() {
1573
	case reflect.String:
1574
		filePath := field.String()
1575
		fileInfo, err := os.Stat(filePath)
1576

1577
		if err != nil {
1578
			return false
1579
		}
1580

1581
		if fileInfo.IsDir() {
1582
			return false
1583
		}
1584

1585
		file, err := os.Open(filePath)
1586
		if err != nil {
1587
			return false
1588
		}
1589
		defer file.Close()
1590

1591
		mime, err := mimetype.DetectReader(file)
1592
		if err != nil {
1593
			return false
1594
		}
1595

1596
		if _, ok := mimetypes[mime.String()]; ok {
1597
			return true
1598
		}
1599
	}
1600
	return false
1601
}
1602

1603
// isFilePath is the validation function for validating if the current field's value is a valid file path.
1604
func isFilePath(fl FieldLevel) bool {
1605

1606
	var exists bool
1607
	var err error
1608

1609
	field := fl.Field()
1610

1611
	// Not valid if it is a directory.
1612
	if isDir(fl) {
1613
		return false
1614
	}
1615
	// If it exists, it obviously is valid.
1616
	// This is done first to avoid code duplication and unnecessary additional logic.
1617
	if exists = isFile(fl); exists {
1618
		return true
1619
	}
1620

1621
	// It does not exist but may still be a valid filepath.
1622
	switch field.Kind() {
1623
	case reflect.String:
1624
		// Every OS allows for whitespace, but none
1625
		// let you use a file with no filename (to my knowledge).
1626
		// Unless you're dealing with raw inodes, but I digress.
1627
		if strings.TrimSpace(field.String()) == "" {
1628
			return false
1629
		}
1630
		// We make sure it isn't a directory.
1631
		if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
1632
			return false
1633
		}
1634
		if _, err = os.Stat(field.String()); err != nil {
1635
			switch t := err.(type) {
1636
			case *fs.PathError:
1637
				if t.Err == syscall.EINVAL {
1638
					// It's definitely an invalid character in the filepath.
1639
					return false
1640
				}
1641
				// It could be a permission error, a does-not-exist error, etc.
1642
				// Out-of-scope for this validation, though.
1643
				return true
1644
			default:
1645
				// Something went *seriously* wrong.
1646
				/*
1647
					Per https://pkg.go.dev/os#Stat:
1648
						"If there is an error, it will be of type *PathError."
1649
				*/
1650
				panic(err)
1651
			}
1652
		}
1653
	}
1654

1655
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
1656
}
1657

1658
// isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
1659
func isE164(fl FieldLevel) bool {
1660
	return e164Regex.MatchString(fl.Field().String())
1661
}
1662

1663
// isEmail is the validation function for validating if the current field's value is a valid email address.
1664
func isEmail(fl FieldLevel) bool {
1665
	return emailRegex.MatchString(fl.Field().String())
1666
}
1667

1668
// isHSLA is the validation function for validating if the current field's value is a valid HSLA color.
1669
func isHSLA(fl FieldLevel) bool {
1670
	return hslaRegex.MatchString(fl.Field().String())
1671
}
1672

1673
// isHSL is the validation function for validating if the current field's value is a valid HSL color.
1674
func isHSL(fl FieldLevel) bool {
1675
	return hslRegex.MatchString(fl.Field().String())
1676
}
1677

1678
// isRGBA is the validation function for validating if the current field's value is a valid RGBA color.
1679
func isRGBA(fl FieldLevel) bool {
1680
	return rgbaRegex.MatchString(fl.Field().String())
1681
}
1682

1683
// isRGB is the validation function for validating if the current field's value is a valid RGB color.
1684
func isRGB(fl FieldLevel) bool {
1685
	return rgbRegex.MatchString(fl.Field().String())
1686
}
1687

1688
// isHEXColor is the validation function for validating if the current field's value is a valid HEX color.
1689
func isHEXColor(fl FieldLevel) bool {
1690
	return hexColorRegex.MatchString(fl.Field().String())
1691
}
1692

1693
// isHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal.
1694
func isHexadecimal(fl FieldLevel) bool {
1695
	return hexadecimalRegex.MatchString(fl.Field().String())
1696
}
1697

1698
// isNumber is the validation function for validating if the current field's value is a valid number.
1699
func isNumber(fl FieldLevel) bool {
1700
	switch fl.Field().Kind() {
1701
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
1702
		return true
1703
	default:
1704
		return numberRegex.MatchString(fl.Field().String())
1705
	}
1706
}
1707

1708
// isNumeric is the validation function for validating if the current field's value is a valid numeric value.
1709
func isNumeric(fl FieldLevel) bool {
1710
	switch fl.Field().Kind() {
1711
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
1712
		return true
1713
	default:
1714
		return numericRegex.MatchString(fl.Field().String())
1715
	}
1716
}
1717

1718
// isAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value.
1719
func isAlphanum(fl FieldLevel) bool {
1720
	return alphaNumericRegex.MatchString(fl.Field().String())
1721
}
1722

1723
// isAlpha is the validation function for validating if the current field's value is a valid alpha value.
1724
func isAlpha(fl FieldLevel) bool {
1725
	return alphaRegex.MatchString(fl.Field().String())
1726
}
1727

1728
// isAlphanumUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value.
1729
func isAlphanumUnicode(fl FieldLevel) bool {
1730
	return alphaUnicodeNumericRegex.MatchString(fl.Field().String())
1731
}
1732

1733
// isAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value.
1734
func isAlphaUnicode(fl FieldLevel) bool {
1735
	return alphaUnicodeRegex.MatchString(fl.Field().String())
1736
}
1737

1738
// isBoolean is the validation function for validating if the current field's value is a valid boolean value or can be safely converted to a boolean value.
1739
func isBoolean(fl FieldLevel) bool {
1740
	switch fl.Field().Kind() {
1741
	case reflect.Bool:
1742
		return true
1743
	default:
1744
		_, err := strconv.ParseBool(fl.Field().String())
1745
		return err == nil
1746
	}
1747
}
1748

1749
// isDefault is the opposite of required aka hasValue
1750
func isDefault(fl FieldLevel) bool {
1751
	return !hasValue(fl)
1752
}
1753

1754
// hasValue is the validation function for validating if the current field's value is not the default static value.
1755
func hasValue(fl FieldLevel) bool {
1756
	field := fl.Field()
1757
	switch field.Kind() {
1758
	case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
1759
		return !field.IsNil()
1760
	default:
1761
		if fl.(*validate).fldIsPointer && field.Interface() != nil {
1762
			return true
1763
		}
1764
		return field.IsValid() && !field.IsZero()
1765
	}
1766
}
1767

1768
// requireCheckFieldKind is a func for check field kind
1769
func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
1770
	field := fl.Field()
1771
	kind := field.Kind()
1772
	var nullable, found bool
1773
	if len(param) > 0 {
1774
		field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
1775
		if !found {
1776
			return defaultNotFoundValue
1777
		}
1778
	}
1779
	switch kind {
1780
	case reflect.Invalid:
1781
		return defaultNotFoundValue
1782
	case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
1783
		return field.IsNil()
1784
	default:
1785
		if nullable && field.Interface() != nil {
1786
			return false
1787
		}
1788
		return field.IsValid() && field.IsZero()
1789
	}
1790
}
1791

1792
// requireCheckFieldValue is a func for check field value
1793
func requireCheckFieldValue(
1794
	fl FieldLevel, param string, value string, defaultNotFoundValue bool,
1795
) bool {
1796
	field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
1797
	if !found {
1798
		return defaultNotFoundValue
1799
	}
1800

1801
	switch kind {
1802

1803
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1804
		return field.Int() == asInt(value)
1805

1806
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1807
		return field.Uint() == asUint(value)
1808

1809
	case reflect.Float32:
1810
		return field.Float() == asFloat32(value)
1811

1812
	case reflect.Float64:
1813
		return field.Float() == asFloat64(value)
1814

1815
	case reflect.Slice, reflect.Map, reflect.Array:
1816
		return int64(field.Len()) == asInt(value)
1817

1818
	case reflect.Bool:
1819
		return field.Bool() == asBool(value)
1820
	}
1821

1822
	// default reflect.String:
1823
	return field.String() == value
1824
}
1825

1826
// requiredIf is the validation function
1827
// The field under validation must be present and not empty only if all the other specified fields are equal to the value following with the specified field.
1828
func requiredIf(fl FieldLevel) bool {
1829
	params := parseOneOfParam2(fl.Param())
1830
	if len(params)%2 != 0 {
1831
		panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName()))
1832
	}
1833
	for i := 0; i < len(params); i += 2 {
1834
		if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1835
			return true
1836
		}
1837
	}
1838
	return hasValue(fl)
1839
}
1840

1841
// excludedIf is the validation function
1842
// The field under validation must not be present or is empty only if all the other specified fields are equal to the value following with the specified field.
1843
func excludedIf(fl FieldLevel) bool {
1844
	params := parseOneOfParam2(fl.Param())
1845
	if len(params)%2 != 0 {
1846
		panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName()))
1847
	}
1848

1849
	for i := 0; i < len(params); i += 2 {
1850
		if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1851
			return true
1852
		}
1853
	}
1854
	return !hasValue(fl)
1855
}
1856

1857
// requiredUnless is the validation function
1858
// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
1859
func requiredUnless(fl FieldLevel) bool {
1860
	params := parseOneOfParam2(fl.Param())
1861
	if len(params)%2 != 0 {
1862
		panic(fmt.Sprintf("Bad param number for required_unless %s", fl.FieldName()))
1863
	}
1864

1865
	for i := 0; i < len(params); i += 2 {
1866
		if requireCheckFieldValue(fl, params[i], params[i+1], false) {
1867
			return true
1868
		}
1869
	}
1870
	return hasValue(fl)
1871
}
1872

1873
// skipUnless is the validation function
1874
// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
1875
func skipUnless(fl FieldLevel) bool {
1876
	params := parseOneOfParam2(fl.Param())
1877
	if len(params)%2 != 0 {
1878
		panic(fmt.Sprintf("Bad param number for skip_unless %s", fl.FieldName()))
1879
	}
1880
	for i := 0; i < len(params); i += 2 {
1881
		if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1882
			return true
1883
		}
1884
	}
1885
	return hasValue(fl)
1886
}
1887

1888
// excludedUnless is the validation function
1889
// The field under validation must not be present or is empty unless all the other specified fields are equal to the value following with the specified field.
1890
func excludedUnless(fl FieldLevel) bool {
1891
	params := parseOneOfParam2(fl.Param())
1892
	if len(params)%2 != 0 {
1893
		panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName()))
1894
	}
1895
	for i := 0; i < len(params); i += 2 {
1896
		if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
1897
			return !hasValue(fl)
1898
		}
1899
	}
1900
	return true
1901
}
1902

1903
// excludedWith is the validation function
1904
// The field under validation must not be present or is empty if any of the other specified fields are present.
1905
func excludedWith(fl FieldLevel) bool {
1906
	params := parseOneOfParam2(fl.Param())
1907
	for _, param := range params {
1908
		if !requireCheckFieldKind(fl, param, true) {
1909
			return !hasValue(fl)
1910
		}
1911
	}
1912
	return true
1913
}
1914

1915
// requiredWith is the validation function
1916
// The field under validation must be present and not empty only if any of the other specified fields are present.
1917
func requiredWith(fl FieldLevel) bool {
1918
	params := parseOneOfParam2(fl.Param())
1919
	for _, param := range params {
1920
		if !requireCheckFieldKind(fl, param, true) {
1921
			return hasValue(fl)
1922
		}
1923
	}
1924
	return true
1925
}
1926

1927
// excludedWithAll is the validation function
1928
// The field under validation must not be present or is empty if all of the other specified fields are present.
1929
func excludedWithAll(fl FieldLevel) bool {
1930
	params := parseOneOfParam2(fl.Param())
1931
	for _, param := range params {
1932
		if requireCheckFieldKind(fl, param, true) {
1933
			return true
1934
		}
1935
	}
1936
	return !hasValue(fl)
1937
}
1938

1939
// requiredWithAll is the validation function
1940
// The field under validation must be present and not empty only if all of the other specified fields are present.
1941
func requiredWithAll(fl FieldLevel) bool {
1942
	params := parseOneOfParam2(fl.Param())
1943
	for _, param := range params {
1944
		if requireCheckFieldKind(fl, param, true) {
1945
			return true
1946
		}
1947
	}
1948
	return hasValue(fl)
1949
}
1950

1951
// excludedWithout is the validation function
1952
// The field under validation must not be present or is empty when any of the other specified fields are not present.
1953
func excludedWithout(fl FieldLevel) bool {
1954
	if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
1955
		return !hasValue(fl)
1956
	}
1957
	return true
1958
}
1959

1960
// requiredWithout is the validation function
1961
// The field under validation must be present and not empty only when any of the other specified fields are not present.
1962
func requiredWithout(fl FieldLevel) bool {
1963
	if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
1964
		return hasValue(fl)
1965
	}
1966
	return true
1967
}
1968

1969
// excludedWithoutAll is the validation function
1970
// The field under validation must not be present or is empty when all of the other specified fields are not present.
1971
func excludedWithoutAll(fl FieldLevel) bool {
1972
	params := parseOneOfParam2(fl.Param())
1973
	for _, param := range params {
1974
		if !requireCheckFieldKind(fl, param, true) {
1975
			return true
1976
		}
1977
	}
1978
	return !hasValue(fl)
1979
}
1980

1981
// requiredWithoutAll is the validation function
1982
// The field under validation must be present and not empty only when all of the other specified fields are not present.
1983
func requiredWithoutAll(fl FieldLevel) bool {
1984
	params := parseOneOfParam2(fl.Param())
1985
	for _, param := range params {
1986
		if !requireCheckFieldKind(fl, param, true) {
1987
			return true
1988
		}
1989
	}
1990
	return hasValue(fl)
1991
}
1992

1993
// isGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value.
1994
func isGteField(fl FieldLevel) bool {
1995
	field := fl.Field()
1996
	kind := field.Kind()
1997

1998
	currentField, currentKind, ok := fl.GetStructFieldOK()
1999
	if !ok || currentKind != kind {
2000
		return false
2001
	}
2002

2003
	switch kind {
2004

2005
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2006

2007
		return field.Int() >= currentField.Int()
2008

2009
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2010

2011
		return field.Uint() >= currentField.Uint()
2012

2013
	case reflect.Float32, reflect.Float64:
2014

2015
		return field.Float() >= currentField.Float()
2016

2017
	case reflect.Struct:
2018

2019
		fieldType := field.Type()
2020

2021
		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2022

2023
			t := currentField.Convert(timeType).Interface().(time.Time)
2024
			fieldTime := field.Convert(timeType).Interface().(time.Time)
2025

2026
			return fieldTime.After(t) || fieldTime.Equal(t)
2027
		}
2028

2029
		// Not Same underlying type i.e. struct and time
2030
		if fieldType != currentField.Type() {
2031
			return false
2032
		}
2033
	}
2034

2035
	// default reflect.String
2036
	return len(field.String()) >= len(currentField.String())
2037
}
2038

2039
// isGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value.
2040
func isGtField(fl FieldLevel) bool {
2041
	field := fl.Field()
2042
	kind := field.Kind()
2043

2044
	currentField, currentKind, ok := fl.GetStructFieldOK()
2045
	if !ok || currentKind != kind {
2046
		return false
2047
	}
2048

2049
	switch kind {
2050

2051
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2052

2053
		return field.Int() > currentField.Int()
2054

2055
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2056

2057
		return field.Uint() > currentField.Uint()
2058

2059
	case reflect.Float32, reflect.Float64:
2060

2061
		return field.Float() > currentField.Float()
2062

2063
	case reflect.Struct:
2064

2065
		fieldType := field.Type()
2066

2067
		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2068

2069
			t := currentField.Convert(timeType).Interface().(time.Time)
2070
			fieldTime := field.Convert(timeType).Interface().(time.Time)
2071

2072
			return fieldTime.After(t)
2073
		}
2074

2075
		// Not Same underlying type i.e. struct and time
2076
		if fieldType != currentField.Type() {
2077
			return false
2078
		}
2079
	}
2080

2081
	// default reflect.String
2082
	return len(field.String()) > len(currentField.String())
2083
}
2084

2085
// isGte is the validation function for validating if the current field's value is greater than or equal to the param's value.
2086
func isGte(fl FieldLevel) bool {
2087
	field := fl.Field()
2088
	param := fl.Param()
2089

2090
	switch field.Kind() {
2091

2092
	case reflect.String:
2093
		p := asInt(param)
2094

2095
		return int64(utf8.RuneCountInString(field.String())) >= p
2096

2097
	case reflect.Slice, reflect.Map, reflect.Array:
2098
		p := asInt(param)
2099

2100
		return int64(field.Len()) >= p
2101

2102
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2103
		p := asIntFromType(field.Type(), param)
2104

2105
		return field.Int() >= p
2106

2107
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2108
		p := asUint(param)
2109

2110
		return field.Uint() >= p
2111

2112
	case reflect.Float32:
2113
		p := asFloat32(param)
2114

2115
		return field.Float() >= p
2116

2117
	case reflect.Float64:
2118
		p := asFloat64(param)
2119

2120
		return field.Float() >= p
2121

2122
	case reflect.Struct:
2123

2124
		if field.Type().ConvertibleTo(timeType) {
2125

2126
			now := time.Now().UTC()
2127
			t := field.Convert(timeType).Interface().(time.Time)
2128

2129
			return t.After(now) || t.Equal(now)
2130
		}
2131
	}
2132

2133
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2134
}
2135

2136
// isGt is the validation function for validating if the current field's value is greater than the param's value.
2137
func isGt(fl FieldLevel) bool {
2138
	field := fl.Field()
2139
	param := fl.Param()
2140

2141
	switch field.Kind() {
2142

2143
	case reflect.String:
2144
		p := asInt(param)
2145

2146
		return int64(utf8.RuneCountInString(field.String())) > p
2147

2148
	case reflect.Slice, reflect.Map, reflect.Array:
2149
		p := asInt(param)
2150

2151
		return int64(field.Len()) > p
2152

2153
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2154
		p := asIntFromType(field.Type(), param)
2155

2156
		return field.Int() > p
2157

2158
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2159
		p := asUint(param)
2160

2161
		return field.Uint() > p
2162

2163
	case reflect.Float32:
2164
		p := asFloat32(param)
2165

2166
		return field.Float() > p
2167

2168
	case reflect.Float64:
2169
		p := asFloat64(param)
2170

2171
		return field.Float() > p
2172

2173
	case reflect.Struct:
2174

2175
		if field.Type().ConvertibleTo(timeType) {
2176

2177
			return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
2178
		}
2179
	}
2180

2181
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2182
}
2183

2184
// hasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
2185
func hasLengthOf(fl FieldLevel) bool {
2186
	field := fl.Field()
2187
	param := fl.Param()
2188

2189
	switch field.Kind() {
2190

2191
	case reflect.String:
2192
		p := asInt(param)
2193

2194
		return int64(utf8.RuneCountInString(field.String())) == p
2195

2196
	case reflect.Slice, reflect.Map, reflect.Array:
2197
		p := asInt(param)
2198

2199
		return int64(field.Len()) == p
2200

2201
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2202
		p := asIntFromType(field.Type(), param)
2203

2204
		return field.Int() == p
2205

2206
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2207
		p := asUint(param)
2208

2209
		return field.Uint() == p
2210

2211
	case reflect.Float32:
2212
		p := asFloat32(param)
2213

2214
		return field.Float() == p
2215

2216
	case reflect.Float64:
2217
		p := asFloat64(param)
2218

2219
		return field.Float() == p
2220
	}
2221

2222
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2223
}
2224

2225
// hasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value.
2226
func hasMinOf(fl FieldLevel) bool {
2227
	return isGte(fl)
2228
}
2229

2230
// isLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value.
2231
func isLteField(fl FieldLevel) bool {
2232
	field := fl.Field()
2233
	kind := field.Kind()
2234

2235
	currentField, currentKind, ok := fl.GetStructFieldOK()
2236
	if !ok || currentKind != kind {
2237
		return false
2238
	}
2239

2240
	switch kind {
2241

2242
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2243

2244
		return field.Int() <= currentField.Int()
2245

2246
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2247

2248
		return field.Uint() <= currentField.Uint()
2249

2250
	case reflect.Float32, reflect.Float64:
2251

2252
		return field.Float() <= currentField.Float()
2253

2254
	case reflect.Struct:
2255

2256
		fieldType := field.Type()
2257

2258
		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2259

2260
			t := currentField.Convert(timeType).Interface().(time.Time)
2261
			fieldTime := field.Convert(timeType).Interface().(time.Time)
2262

2263
			return fieldTime.Before(t) || fieldTime.Equal(t)
2264
		}
2265

2266
		// Not Same underlying type i.e. struct and time
2267
		if fieldType != currentField.Type() {
2268
			return false
2269
		}
2270
	}
2271

2272
	// default reflect.String
2273
	return len(field.String()) <= len(currentField.String())
2274
}
2275

2276
// isLtField is the validation function for validating if the current field's value is less than the field specified by the param's value.
2277
func isLtField(fl FieldLevel) bool {
2278
	field := fl.Field()
2279
	kind := field.Kind()
2280

2281
	currentField, currentKind, ok := fl.GetStructFieldOK()
2282
	if !ok || currentKind != kind {
2283
		return false
2284
	}
2285

2286
	switch kind {
2287

2288
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2289

2290
		return field.Int() < currentField.Int()
2291

2292
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2293

2294
		return field.Uint() < currentField.Uint()
2295

2296
	case reflect.Float32, reflect.Float64:
2297

2298
		return field.Float() < currentField.Float()
2299

2300
	case reflect.Struct:
2301

2302
		fieldType := field.Type()
2303

2304
		if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
2305

2306
			t := currentField.Convert(timeType).Interface().(time.Time)
2307
			fieldTime := field.Convert(timeType).Interface().(time.Time)
2308

2309
			return fieldTime.Before(t)
2310
		}
2311

2312
		// Not Same underlying type i.e. struct and time
2313
		if fieldType != currentField.Type() {
2314
			return false
2315
		}
2316
	}
2317

2318
	// default reflect.String
2319
	return len(field.String()) < len(currentField.String())
2320
}
2321

2322
// isLte is the validation function for validating if the current field's value is less than or equal to the param's value.
2323
func isLte(fl FieldLevel) bool {
2324
	field := fl.Field()
2325
	param := fl.Param()
2326

2327
	switch field.Kind() {
2328

2329
	case reflect.String:
2330
		p := asInt(param)
2331

2332
		return int64(utf8.RuneCountInString(field.String())) <= p
2333

2334
	case reflect.Slice, reflect.Map, reflect.Array:
2335
		p := asInt(param)
2336

2337
		return int64(field.Len()) <= p
2338

2339
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2340
		p := asIntFromType(field.Type(), param)
2341

2342
		return field.Int() <= p
2343

2344
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2345
		p := asUint(param)
2346

2347
		return field.Uint() <= p
2348

2349
	case reflect.Float32:
2350
		p := asFloat32(param)
2351

2352
		return field.Float() <= p
2353

2354
	case reflect.Float64:
2355
		p := asFloat64(param)
2356

2357
		return field.Float() <= p
2358

2359
	case reflect.Struct:
2360

2361
		if field.Type().ConvertibleTo(timeType) {
2362

2363
			now := time.Now().UTC()
2364
			t := field.Convert(timeType).Interface().(time.Time)
2365

2366
			return t.Before(now) || t.Equal(now)
2367
		}
2368
	}
2369

2370
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2371
}
2372

2373
// isLt is the validation function for validating if the current field's value is less than the param's value.
2374
func isLt(fl FieldLevel) bool {
2375
	field := fl.Field()
2376
	param := fl.Param()
2377

2378
	switch field.Kind() {
2379

2380
	case reflect.String:
2381
		p := asInt(param)
2382

2383
		return int64(utf8.RuneCountInString(field.String())) < p
2384

2385
	case reflect.Slice, reflect.Map, reflect.Array:
2386
		p := asInt(param)
2387

2388
		return int64(field.Len()) < p
2389

2390
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2391
		p := asIntFromType(field.Type(), param)
2392

2393
		return field.Int() < p
2394

2395
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2396
		p := asUint(param)
2397

2398
		return field.Uint() < p
2399

2400
	case reflect.Float32:
2401
		p := asFloat32(param)
2402

2403
		return field.Float() < p
2404

2405
	case reflect.Float64:
2406
		p := asFloat64(param)
2407

2408
		return field.Float() < p
2409

2410
	case reflect.Struct:
2411

2412
		if field.Type().ConvertibleTo(timeType) {
2413

2414
			return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
2415
		}
2416
	}
2417

2418
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2419
}
2420

2421
// hasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value.
2422
func hasMaxOf(fl FieldLevel) bool {
2423
	return isLte(fl)
2424
}
2425

2426
// isTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address.
2427
func isTCP4AddrResolvable(fl FieldLevel) bool {
2428
	if !isIP4Addr(fl) {
2429
		return false
2430
	}
2431

2432
	_, err := net.ResolveTCPAddr("tcp4", fl.Field().String())
2433
	return err == nil
2434
}
2435

2436
// isTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address.
2437
func isTCP6AddrResolvable(fl FieldLevel) bool {
2438
	if !isIP6Addr(fl) {
2439
		return false
2440
	}
2441

2442
	_, err := net.ResolveTCPAddr("tcp6", fl.Field().String())
2443

2444
	return err == nil
2445
}
2446

2447
// isTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address.
2448
func isTCPAddrResolvable(fl FieldLevel) bool {
2449
	if !isIP4Addr(fl) && !isIP6Addr(fl) {
2450
		return false
2451
	}
2452

2453
	_, err := net.ResolveTCPAddr("tcp", fl.Field().String())
2454

2455
	return err == nil
2456
}
2457

2458
// isUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address.
2459
func isUDP4AddrResolvable(fl FieldLevel) bool {
2460
	if !isIP4Addr(fl) {
2461
		return false
2462
	}
2463

2464
	_, err := net.ResolveUDPAddr("udp4", fl.Field().String())
2465

2466
	return err == nil
2467
}
2468

2469
// isUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address.
2470
func isUDP6AddrResolvable(fl FieldLevel) bool {
2471
	if !isIP6Addr(fl) {
2472
		return false
2473
	}
2474

2475
	_, err := net.ResolveUDPAddr("udp6", fl.Field().String())
2476

2477
	return err == nil
2478
}
2479

2480
// isUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address.
2481
func isUDPAddrResolvable(fl FieldLevel) bool {
2482
	if !isIP4Addr(fl) && !isIP6Addr(fl) {
2483
		return false
2484
	}
2485

2486
	_, err := net.ResolveUDPAddr("udp", fl.Field().String())
2487

2488
	return err == nil
2489
}
2490

2491
// isIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address.
2492
func isIP4AddrResolvable(fl FieldLevel) bool {
2493
	if !isIPv4(fl) {
2494
		return false
2495
	}
2496

2497
	_, err := net.ResolveIPAddr("ip4", fl.Field().String())
2498

2499
	return err == nil
2500
}
2501

2502
// isIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address.
2503
func isIP6AddrResolvable(fl FieldLevel) bool {
2504
	if !isIPv6(fl) {
2505
		return false
2506
	}
2507

2508
	_, err := net.ResolveIPAddr("ip6", fl.Field().String())
2509

2510
	return err == nil
2511
}
2512

2513
// isIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address.
2514
func isIPAddrResolvable(fl FieldLevel) bool {
2515
	if !isIP(fl) {
2516
		return false
2517
	}
2518

2519
	_, err := net.ResolveIPAddr("ip", fl.Field().String())
2520

2521
	return err == nil
2522
}
2523

2524
// isUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address.
2525
func isUnixAddrResolvable(fl FieldLevel) bool {
2526
	_, err := net.ResolveUnixAddr("unix", fl.Field().String())
2527

2528
	return err == nil
2529
}
2530

2531
func isIP4Addr(fl FieldLevel) bool {
2532
	val := fl.Field().String()
2533

2534
	if idx := strings.LastIndex(val, ":"); idx != -1 {
2535
		val = val[0:idx]
2536
	}
2537

2538
	ip := net.ParseIP(val)
2539

2540
	return ip != nil && ip.To4() != nil
2541
}
2542

2543
func isIP6Addr(fl FieldLevel) bool {
2544
	val := fl.Field().String()
2545

2546
	if idx := strings.LastIndex(val, ":"); idx != -1 {
2547
		if idx != 0 && val[idx-1:idx] == "]" {
2548
			val = val[1 : idx-1]
2549
		}
2550
	}
2551

2552
	ip := net.ParseIP(val)
2553

2554
	return ip != nil && ip.To4() == nil
2555
}
2556

2557
func isHostnameRFC952(fl FieldLevel) bool {
2558
	return hostnameRegexRFC952.MatchString(fl.Field().String())
2559
}
2560

2561
func isHostnameRFC1123(fl FieldLevel) bool {
2562
	return hostnameRegexRFC1123.MatchString(fl.Field().String())
2563
}
2564

2565
func isFQDN(fl FieldLevel) bool {
2566
	val := fl.Field().String()
2567

2568
	if val == "" {
2569
		return false
2570
	}
2571

2572
	return fqdnRegexRFC1123.MatchString(val)
2573
}
2574

2575
// isDir is the validation function for validating if the current field's value is a valid existing directory.
2576
func isDir(fl FieldLevel) bool {
2577
	field := fl.Field()
2578

2579
	if field.Kind() == reflect.String {
2580
		fileInfo, err := os.Stat(field.String())
2581
		if err != nil {
2582
			return false
2583
		}
2584

2585
		return fileInfo.IsDir()
2586
	}
2587

2588
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2589
}
2590

2591
// isDirPath is the validation function for validating if the current field's value is a valid directory.
2592
func isDirPath(fl FieldLevel) bool {
2593

2594
	var exists bool
2595
	var err error
2596

2597
	field := fl.Field()
2598

2599
	// If it exists, it obviously is valid.
2600
	// This is done first to avoid code duplication and unnecessary additional logic.
2601
	if exists = isDir(fl); exists {
2602
		return true
2603
	}
2604

2605
	// It does not exist but may still be a valid path.
2606
	switch field.Kind() {
2607
	case reflect.String:
2608
		// Every OS allows for whitespace, but none
2609
		// let you use a dir with no name (to my knowledge).
2610
		// Unless you're dealing with raw inodes, but I digress.
2611
		if strings.TrimSpace(field.String()) == "" {
2612
			return false
2613
		}
2614
		if _, err = os.Stat(field.String()); err != nil {
2615
			switch t := err.(type) {
2616
			case *fs.PathError:
2617
				if t.Err == syscall.EINVAL {
2618
					// It's definitely an invalid character in the path.
2619
					return false
2620
				}
2621
				// It could be a permission error, a does-not-exist error, etc.
2622
				// Out-of-scope for this validation, though.
2623
				// Lastly, we make sure it is a directory.
2624
				if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
2625
					return true
2626
				} else {
2627
					return false
2628
				}
2629
			default:
2630
				// Something went *seriously* wrong.
2631
				/*
2632
					Per https://pkg.go.dev/os#Stat:
2633
						"If there is an error, it will be of type *PathError."
2634
				*/
2635
				panic(err)
2636
			}
2637
		}
2638
		// We repeat the check here to make sure it is an explicit directory in case the above os.Stat didn't trigger an error.
2639
		if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
2640
			return true
2641
		} else {
2642
			return false
2643
		}
2644
	}
2645

2646
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2647
}
2648

2649
// isJSON is the validation function for validating if the current field's value is a valid json string.
2650
func isJSON(fl FieldLevel) bool {
2651
	field := fl.Field()
2652

2653
	switch field.Kind() {
2654
	case reflect.String:
2655
		val := field.String()
2656
		return json.Valid([]byte(val))
2657
	case reflect.Slice:
2658
		fieldType := field.Type()
2659

2660
		if fieldType.ConvertibleTo(byteSliceType) {
2661
			b := field.Convert(byteSliceType).Interface().([]byte)
2662
			return json.Valid(b)
2663
		}
2664
	}
2665

2666
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2667
}
2668

2669
// isJWT is the validation function for validating if the current field's value is a valid JWT string.
2670
func isJWT(fl FieldLevel) bool {
2671
	return jWTRegex.MatchString(fl.Field().String())
2672
}
2673

2674
// isHostnamePort validates a <dns>:<port> combination for fields typically used for socket address.
2675
func isHostnamePort(fl FieldLevel) bool {
2676
	val := fl.Field().String()
2677
	host, port, err := net.SplitHostPort(val)
2678
	if err != nil {
2679
		return false
2680
	}
2681
	// Port must be a iny <= 65535.
2682
	if portNum, err := strconv.ParseInt(
2683
		port, 10, 32,
2684
	); err != nil || portNum > 65535 || portNum < 1 {
2685
		return false
2686
	}
2687

2688
	// If host is specified, it should match a DNS name
2689
	if host != "" {
2690
		return hostnameRegexRFC1123.MatchString(host)
2691
	}
2692
	return true
2693
}
2694

2695
// isLowercase is the validation function for validating if the current field's value is a lowercase string.
2696
func isLowercase(fl FieldLevel) bool {
2697
	field := fl.Field()
2698

2699
	if field.Kind() == reflect.String {
2700
		if field.String() == "" {
2701
			return false
2702
		}
2703
		return field.String() == strings.ToLower(field.String())
2704
	}
2705

2706
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2707
}
2708

2709
// isUppercase is the validation function for validating if the current field's value is an uppercase string.
2710
func isUppercase(fl FieldLevel) bool {
2711
	field := fl.Field()
2712

2713
	if field.Kind() == reflect.String {
2714
		if field.String() == "" {
2715
			return false
2716
		}
2717
		return field.String() == strings.ToUpper(field.String())
2718
	}
2719

2720
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2721
}
2722

2723
// isDatetime is the validation function for validating if the current field's value is a valid datetime string.
2724
func isDatetime(fl FieldLevel) bool {
2725
	field := fl.Field()
2726
	param := fl.Param()
2727

2728
	if field.Kind() == reflect.String {
2729
		_, err := time.Parse(param, field.String())
2730

2731
		return err == nil
2732
	}
2733

2734
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2735
}
2736

2737
// isTimeZone is the validation function for validating if the current field's value is a valid time zone string.
2738
func isTimeZone(fl FieldLevel) bool {
2739
	field := fl.Field()
2740

2741
	if field.Kind() == reflect.String {
2742
		// empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name
2743
		if field.String() == "" {
2744
			return false
2745
		}
2746

2747
		// Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name
2748
		if strings.ToLower(field.String()) == "local" {
2749
			return false
2750
		}
2751

2752
		_, err := time.LoadLocation(field.String())
2753
		return err == nil
2754
	}
2755

2756
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2757
}
2758

2759
// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code.
2760
func isIso3166Alpha2(fl FieldLevel) bool {
2761
	val := fl.Field().String()
2762
	return iso3166_1_alpha2[val]
2763
}
2764

2765
// isIso3166Alpha3 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code.
2766
func isIso3166Alpha3(fl FieldLevel) bool {
2767
	val := fl.Field().String()
2768
	return iso3166_1_alpha3[val]
2769
}
2770

2771
// isIso3166AlphaNumeric is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code.
2772
func isIso3166AlphaNumeric(fl FieldLevel) bool {
2773
	field := fl.Field()
2774

2775
	var code int
2776
	switch field.Kind() {
2777
	case reflect.String:
2778
		i, err := strconv.Atoi(field.String())
2779
		if err != nil {
2780
			return false
2781
		}
2782
		code = i % 1000
2783
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2784
		code = int(field.Int() % 1000)
2785
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
2786
		code = int(field.Uint() % 1000)
2787
	default:
2788
		panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2789
	}
2790
	return iso3166_1_alpha_numeric[code]
2791
}
2792

2793
// isIso31662 is the validation function for validating if the current field's value is a valid iso3166-2 code.
2794
func isIso31662(fl FieldLevel) bool {
2795
	val := fl.Field().String()
2796
	return iso3166_2[val]
2797
}
2798

2799
// isIso4217 is the validation function for validating if the current field's value is a valid iso4217 currency code.
2800
func isIso4217(fl FieldLevel) bool {
2801
	val := fl.Field().String()
2802
	return iso4217[val]
2803
}
2804

2805
// isIso4217Numeric is the validation function for validating if the current field's value is a valid iso4217 numeric currency code.
2806
func isIso4217Numeric(fl FieldLevel) bool {
2807
	field := fl.Field()
2808

2809
	var code int
2810
	switch field.Kind() {
2811
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2812
		code = int(field.Int())
2813
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
2814
		code = int(field.Uint())
2815
	default:
2816
		panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2817
	}
2818
	return iso4217_numeric[code]
2819
}
2820

2821
// isBCP47LanguageTag is the validation function for validating if the current field's value is a valid BCP 47 language tag, as parsed by language.Parse
2822
func isBCP47LanguageTag(fl FieldLevel) bool {
2823
	field := fl.Field()
2824

2825
	if field.Kind() == reflect.String {
2826
		_, err := language.Parse(field.String())
2827
		return err == nil
2828
	}
2829

2830
	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2831
}
2832

2833
// isIsoBicFormat is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362
2834
func isIsoBicFormat(fl FieldLevel) bool {
2835
	bicString := fl.Field().String()
2836

2837
	return bicRegex.MatchString(bicString)
2838
}
2839

2840
// isSemverFormat is the validation function for validating if the current field's value is a valid semver version, defined in Semantic Versioning 2.0.0
2841
func isSemverFormat(fl FieldLevel) bool {
2842
	semverString := fl.Field().String()
2843

2844
	return semverRegex.MatchString(semverString)
2845
}
2846

2847
// isCveFormat is the validation function for validating if the current field's value is a valid cve id, defined in CVE mitre org
2848
func isCveFormat(fl FieldLevel) bool {
2849
	cveString := fl.Field().String()
2850

2851
	return cveRegex.MatchString(cveString)
2852
}
2853

2854
// isDnsRFC1035LabelFormat is the validation function
2855
// for validating if the current field's value is
2856
// a valid dns RFC 1035 label, defined in RFC 1035.
2857
func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
2858
	val := fl.Field().String()
2859
	return dnsRegexRFC1035Label.MatchString(val)
2860
}
2861

2862
// digitsHaveLuhnChecksum returns true if and only if the last element of the given digits slice is the Luhn checksum of the previous elements
2863
func digitsHaveLuhnChecksum(digits []string) bool {
2864
	size := len(digits)
2865
	sum := 0
2866
	for i, digit := range digits {
2867
		value, err := strconv.Atoi(digit)
2868
		if err != nil {
2869
			return false
2870
		}
2871
		if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
2872
			v := value * 2
2873
			if v >= 10 {
2874
				sum += 1 + (v % 10)
2875
			} else {
2876
				sum += v
2877
			}
2878
		} else {
2879
			sum += value
2880
		}
2881
	}
2882
	return (sum % 10) == 0
2883
}
2884

2885
// isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID
2886
func isMongoDB(fl FieldLevel) bool {
2887
	val := fl.Field().String()
2888
	return mongodbRegex.MatchString(val)
2889
}
2890

2891
// isSpiceDB is the validation function for validating if the current field's value is valid for use with Authzed SpiceDB in the indicated way
2892
func isSpiceDB(fl FieldLevel) bool {
2893
	val := fl.Field().String()
2894
	param := fl.Param()
2895

2896
	switch param {
2897
	case "permission":
2898
		return spicedbPermissionRegex.MatchString(val)
2899
	case "type":
2900
		return spicedbTypeRegex.MatchString(val)
2901
	case "id", "":
2902
		return spicedbIDRegex.MatchString(val)
2903
	}
2904

2905
	panic("Unrecognized parameter: " + param)
2906
}
2907

2908
// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
2909
func isCreditCard(fl FieldLevel) bool {
2910
	val := fl.Field().String()
2911
	var creditCard bytes.Buffer
2912
	segments := strings.Split(val, " ")
2913
	for _, segment := range segments {
2914
		if len(segment) < 3 {
2915
			return false
2916
		}
2917
		creditCard.WriteString(segment)
2918
	}
2919

2920
	ccDigits := strings.Split(creditCard.String(), "")
2921
	size := len(ccDigits)
2922
	if size < 12 || size > 19 {
2923
		return false
2924
	}
2925

2926
	return digitsHaveLuhnChecksum(ccDigits)
2927
}
2928

2929
// hasLuhnChecksum is the validation for validating if the current field's value has a valid Luhn checksum
2930
func hasLuhnChecksum(fl FieldLevel) bool {
2931
	field := fl.Field()
2932
	var str string // convert to a string which will then be split into single digits; easier and more readable than shifting/extracting single digits from a number
2933
	switch field.Kind() {
2934
	case reflect.String:
2935
		str = field.String()
2936
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2937
		str = strconv.FormatInt(field.Int(), 10)
2938
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
2939
		str = strconv.FormatUint(field.Uint(), 10)
2940
	default:
2941
		panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2942
	}
2943
	size := len(str)
2944
	if size < 2 { // there has to be at least one digit that carries a meaning + the checksum
2945
		return false
2946
	}
2947
	digits := strings.Split(str, "")
2948
	return digitsHaveLuhnChecksum(digits)
2949
}
2950

2951
// isCron is the validation function for validating if the current field's value is a valid cron expression
2952
func isCron(fl FieldLevel) bool {
2953
	cronString := fl.Field().String()
2954
	return cronRegex.MatchString(cronString)
2955
}
2956

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

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

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

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