baked_in.go (84767B)
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 "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 skipValidationTag: {}, 55 utf8HexComma: {}, 56 utf8Pipe: {}, 57 noStructLevelTag: {}, 58 requiredTag: {}, 59 isdefault: {}, 60 } 61 62 // bakedInAliases is a default mapping of a single validation tag that 63 // defines a common or complex set of validation(s) to simplify 64 // adding validation to structs. 65 bakedInAliases = map[string]string{ 66 "iscolor": "hexcolor|rgb|rgba|hsl|hsla", 67 "country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric", 68 } 69 70 // bakedInValidators is the default map of ValidationFunc 71 // you can add, remove or even replace items to suite your needs, 72 // or even disregard and use your own map if so desired. 73 bakedInValidators = map[string]Func{ 74 "required": hasValue, 75 "required_if": requiredIf, 76 "required_unless": requiredUnless, 77 "skip_unless": skipUnless, 78 "required_with": requiredWith, 79 "required_with_all": requiredWithAll, 80 "required_without": requiredWithout, 81 "required_without_all": requiredWithoutAll, 82 "excluded_if": excludedIf, 83 "excluded_unless": excludedUnless, 84 "excluded_with": excludedWith, 85 "excluded_with_all": excludedWithAll, 86 "excluded_without": excludedWithout, 87 "excluded_without_all": excludedWithoutAll, 88 "isdefault": isDefault, 89 "len": hasLengthOf, 90 "min": hasMinOf, 91 "max": hasMaxOf, 92 "eq": isEq, 93 "eq_ignore_case": isEqIgnoreCase, 94 "ne": isNe, 95 "ne_ignore_case": isNeIgnoreCase, 96 "lt": isLt, 97 "lte": isLte, 98 "gt": isGt, 99 "gte": isGte, 100 "eqfield": isEqField, 101 "eqcsfield": isEqCrossStructField, 102 "necsfield": isNeCrossStructField, 103 "gtcsfield": isGtCrossStructField, 104 "gtecsfield": isGteCrossStructField, 105 "ltcsfield": isLtCrossStructField, 106 "ltecsfield": isLteCrossStructField, 107 "nefield": isNeField, 108 "gtefield": isGteField, 109 "gtfield": isGtField, 110 "ltefield": isLteField, 111 "ltfield": isLtField, 112 "fieldcontains": fieldContains, 113 "fieldexcludes": fieldExcludes, 114 "alpha": isAlpha, 115 "alphanum": isAlphanum, 116 "alphaunicode": isAlphaUnicode, 117 "alphanumunicode": isAlphanumUnicode, 118 "boolean": isBoolean, 119 "numeric": isNumeric, 120 "number": isNumber, 121 "hexadecimal": isHexadecimal, 122 "hexcolor": isHEXColor, 123 "rgb": isRGB, 124 "rgba": isRGBA, 125 "hsl": isHSL, 126 "hsla": isHSLA, 127 "e164": isE164, 128 "email": isEmail, 129 "url": isURL, 130 "http_url": isHttpURL, 131 "uri": isURI, 132 "urn_rfc2141": isUrnRFC2141, // RFC 2141 133 "file": isFile, 134 "filepath": isFilePath, 135 "base64": isBase64, 136 "base64url": isBase64URL, 137 "base64rawurl": isBase64RawURL, 138 "contains": contains, 139 "containsany": containsAny, 140 "containsrune": containsRune, 141 "excludes": excludes, 142 "excludesall": excludesAll, 143 "excludesrune": excludesRune, 144 "startswith": startsWith, 145 "endswith": endsWith, 146 "startsnotwith": startsNotWith, 147 "endsnotwith": endsNotWith, 148 "image": isImage, 149 "isbn": isISBN, 150 "isbn10": isISBN10, 151 "isbn13": isISBN13, 152 "eth_addr": isEthereumAddress, 153 "eth_addr_checksum": isEthereumAddressChecksum, 154 "btc_addr": isBitcoinAddress, 155 "btc_addr_bech32": isBitcoinBech32Address, 156 "uuid": isUUID, 157 "uuid3": isUUID3, 158 "uuid4": isUUID4, 159 "uuid5": isUUID5, 160 "uuid_rfc4122": isUUIDRFC4122, 161 "uuid3_rfc4122": isUUID3RFC4122, 162 "uuid4_rfc4122": isUUID4RFC4122, 163 "uuid5_rfc4122": isUUID5RFC4122, 164 "ulid": isULID, 165 "md4": isMD4, 166 "md5": isMD5, 167 "sha256": isSHA256, 168 "sha384": isSHA384, 169 "sha512": isSHA512, 170 "ripemd128": isRIPEMD128, 171 "ripemd160": isRIPEMD160, 172 "tiger128": isTIGER128, 173 "tiger160": isTIGER160, 174 "tiger192": isTIGER192, 175 "ascii": isASCII, 176 "printascii": isPrintableASCII, 177 "multibyte": hasMultiByteCharacter, 178 "datauri": isDataURI, 179 "latitude": isLatitude, 180 "longitude": isLongitude, 181 "ssn": isSSN, 182 "ipv4": isIPv4, 183 "ipv6": isIPv6, 184 "ip": isIP, 185 "cidrv4": isCIDRv4, 186 "cidrv6": isCIDRv6, 187 "cidr": isCIDR, 188 "tcp4_addr": isTCP4AddrResolvable, 189 "tcp6_addr": isTCP6AddrResolvable, 190 "tcp_addr": isTCPAddrResolvable, 191 "udp4_addr": isUDP4AddrResolvable, 192 "udp6_addr": isUDP6AddrResolvable, 193 "udp_addr": isUDPAddrResolvable, 194 "ip4_addr": isIP4AddrResolvable, 195 "ip6_addr": isIP6AddrResolvable, 196 "ip_addr": isIPAddrResolvable, 197 "unix_addr": isUnixAddrResolvable, 198 "mac": isMAC, 199 "hostname": isHostnameRFC952, // RFC 952 200 "hostname_rfc1123": isHostnameRFC1123, // RFC 1123 201 "fqdn": isFQDN, 202 "unique": isUnique, 203 "oneof": isOneOf, 204 "html": isHTML, 205 "html_encoded": isHTMLEncoded, 206 "url_encoded": isURLEncoded, 207 "dir": isDir, 208 "dirpath": isDirPath, 209 "json": isJSON, 210 "jwt": isJWT, 211 "hostname_port": isHostnamePort, 212 "lowercase": isLowercase, 213 "uppercase": isUppercase, 214 "datetime": isDatetime, 215 "timezone": isTimeZone, 216 "iso3166_1_alpha2": isIso3166Alpha2, 217 "iso3166_1_alpha3": isIso3166Alpha3, 218 "iso3166_1_alpha_numeric": isIso3166AlphaNumeric, 219 "iso3166_2": isIso31662, 220 "iso4217": isIso4217, 221 "iso4217_numeric": isIso4217Numeric, 222 "bcp47_language_tag": isBCP47LanguageTag, 223 "postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2, 224 "postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field, 225 "bic": isIsoBicFormat, 226 "semver": isSemverFormat, 227 "dns_rfc1035_label": isDnsRFC1035LabelFormat, 228 "credit_card": isCreditCard, 229 "cve": isCveFormat, 230 "luhn_checksum": hasLuhnChecksum, 231 "mongodb": isMongoDB, 232 "cron": isCron, 233 } 234 ) 235 236 var ( 237 oneofValsCache = map[string][]string{} 238 oneofValsCacheRWLock = sync.RWMutex{} 239 ) 240 241 func parseOneOfParam2(s string) []string { 242 oneofValsCacheRWLock.RLock() 243 vals, ok := oneofValsCache[s] 244 oneofValsCacheRWLock.RUnlock() 245 if !ok { 246 oneofValsCacheRWLock.Lock() 247 vals = splitParamsRegex.FindAllString(s, -1) 248 for i := 0; i < len(vals); i++ { 249 vals[i] = strings.Replace(vals[i], "'", "", -1) 250 } 251 oneofValsCache[s] = vals 252 oneofValsCacheRWLock.Unlock() 253 } 254 return vals 255 } 256 257 func isURLEncoded(fl FieldLevel) bool { 258 return uRLEncodedRegex.MatchString(fl.Field().String()) 259 } 260 261 func isHTMLEncoded(fl FieldLevel) bool { 262 return hTMLEncodedRegex.MatchString(fl.Field().String()) 263 } 264 265 func isHTML(fl FieldLevel) bool { 266 return hTMLRegex.MatchString(fl.Field().String()) 267 } 268 269 func isOneOf(fl FieldLevel) bool { 270 vals := parseOneOfParam2(fl.Param()) 271 272 field := fl.Field() 273 274 var v string 275 switch field.Kind() { 276 case reflect.String: 277 v = field.String() 278 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 279 v = strconv.FormatInt(field.Int(), 10) 280 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 281 v = strconv.FormatUint(field.Uint(), 10) 282 default: 283 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 284 } 285 for i := 0; i < len(vals); i++ { 286 if vals[i] == v { 287 return true 288 } 289 } 290 return false 291 } 292 293 // isUnique is the validation function for validating if each array|slice|map value is unique 294 func isUnique(fl FieldLevel) bool { 295 field := fl.Field() 296 param := fl.Param() 297 v := reflect.ValueOf(struct{}{}) 298 299 switch field.Kind() { 300 case reflect.Slice, reflect.Array: 301 elem := field.Type().Elem() 302 if elem.Kind() == reflect.Ptr { 303 elem = elem.Elem() 304 } 305 306 if param == "" { 307 m := reflect.MakeMap(reflect.MapOf(elem, v.Type())) 308 309 for i := 0; i < field.Len(); i++ { 310 m.SetMapIndex(reflect.Indirect(field.Index(i)), v) 311 } 312 return field.Len() == m.Len() 313 } 314 315 sf, ok := elem.FieldByName(param) 316 if !ok { 317 panic(fmt.Sprintf("Bad field name %s", param)) 318 } 319 320 sfTyp := sf.Type 321 if sfTyp.Kind() == reflect.Ptr { 322 sfTyp = sfTyp.Elem() 323 } 324 325 m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type())) 326 var fieldlen int 327 for i := 0; i < field.Len(); i++ { 328 key := reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param)) 329 if key.IsValid() { 330 fieldlen++ 331 m.SetMapIndex(key, v) 332 } 333 } 334 return fieldlen == m.Len() 335 case reflect.Map: 336 var m reflect.Value 337 if field.Type().Elem().Kind() == reflect.Ptr { 338 m = reflect.MakeMap(reflect.MapOf(field.Type().Elem().Elem(), v.Type())) 339 } else { 340 m = reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type())) 341 } 342 343 for _, k := range field.MapKeys() { 344 m.SetMapIndex(reflect.Indirect(field.MapIndex(k)), v) 345 } 346 347 return field.Len() == m.Len() 348 default: 349 if parent := fl.Parent(); parent.Kind() == reflect.Struct { 350 uniqueField := parent.FieldByName(param) 351 if uniqueField == reflect.ValueOf(nil) { 352 panic(fmt.Sprintf("Bad field name provided %s", param)) 353 } 354 355 if uniqueField.Kind() != field.Kind() { 356 panic(fmt.Sprintf("Bad field type %T:%T", field.Interface(), uniqueField.Interface())) 357 } 358 359 return field.Interface() != uniqueField.Interface() 360 } 361 362 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 363 } 364 } 365 366 // isMAC is the validation function for validating if the field's value is a valid MAC address. 367 func isMAC(fl FieldLevel) bool { 368 _, err := net.ParseMAC(fl.Field().String()) 369 370 return err == nil 371 } 372 373 // isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address. 374 func isCIDRv4(fl FieldLevel) bool { 375 ip, _, err := net.ParseCIDR(fl.Field().String()) 376 377 return err == nil && ip.To4() != nil 378 } 379 380 // isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address. 381 func isCIDRv6(fl FieldLevel) bool { 382 ip, _, err := net.ParseCIDR(fl.Field().String()) 383 384 return err == nil && ip.To4() == nil 385 } 386 387 // isCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address. 388 func isCIDR(fl FieldLevel) bool { 389 _, _, err := net.ParseCIDR(fl.Field().String()) 390 391 return err == nil 392 } 393 394 // isIPv4 is the validation function for validating if a value is a valid v4 IP address. 395 func isIPv4(fl FieldLevel) bool { 396 ip := net.ParseIP(fl.Field().String()) 397 398 return ip != nil && ip.To4() != nil 399 } 400 401 // isIPv6 is the validation function for validating if the field's value is a valid v6 IP address. 402 func isIPv6(fl FieldLevel) bool { 403 ip := net.ParseIP(fl.Field().String()) 404 405 return ip != nil && ip.To4() == nil 406 } 407 408 // isIP is the validation function for validating if the field's value is a valid v4 or v6 IP address. 409 func isIP(fl FieldLevel) bool { 410 ip := net.ParseIP(fl.Field().String()) 411 412 return ip != nil 413 } 414 415 // isSSN is the validation function for validating if the field's value is a valid SSN. 416 func isSSN(fl FieldLevel) bool { 417 field := fl.Field() 418 419 if field.Len() != 11 { 420 return false 421 } 422 423 return sSNRegex.MatchString(field.String()) 424 } 425 426 // isLongitude is the validation function for validating if the field's value is a valid longitude coordinate. 427 func isLongitude(fl FieldLevel) bool { 428 field := fl.Field() 429 430 var v string 431 switch field.Kind() { 432 case reflect.String: 433 v = field.String() 434 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 435 v = strconv.FormatInt(field.Int(), 10) 436 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 437 v = strconv.FormatUint(field.Uint(), 10) 438 case reflect.Float32: 439 v = strconv.FormatFloat(field.Float(), 'f', -1, 32) 440 case reflect.Float64: 441 v = strconv.FormatFloat(field.Float(), 'f', -1, 64) 442 default: 443 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 444 } 445 446 return longitudeRegex.MatchString(v) 447 } 448 449 // isLatitude is the validation function for validating if the field's value is a valid latitude coordinate. 450 func isLatitude(fl FieldLevel) bool { 451 field := fl.Field() 452 453 var v string 454 switch field.Kind() { 455 case reflect.String: 456 v = field.String() 457 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 458 v = strconv.FormatInt(field.Int(), 10) 459 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 460 v = strconv.FormatUint(field.Uint(), 10) 461 case reflect.Float32: 462 v = strconv.FormatFloat(field.Float(), 'f', -1, 32) 463 case reflect.Float64: 464 v = strconv.FormatFloat(field.Float(), 'f', -1, 64) 465 default: 466 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 467 } 468 469 return latitudeRegex.MatchString(v) 470 } 471 472 // isDataURI is the validation function for validating if the field's value is a valid data URI. 473 func isDataURI(fl FieldLevel) bool { 474 uri := strings.SplitN(fl.Field().String(), ",", 2) 475 476 if len(uri) != 2 { 477 return false 478 } 479 480 if !dataURIRegex.MatchString(uri[0]) { 481 return false 482 } 483 484 return base64Regex.MatchString(uri[1]) 485 } 486 487 // hasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character. 488 func hasMultiByteCharacter(fl FieldLevel) bool { 489 field := fl.Field() 490 491 if field.Len() == 0 { 492 return true 493 } 494 495 return multibyteRegex.MatchString(field.String()) 496 } 497 498 // isPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character. 499 func isPrintableASCII(fl FieldLevel) bool { 500 return printableASCIIRegex.MatchString(fl.Field().String()) 501 } 502 503 // isASCII is the validation function for validating if the field's value is a valid ASCII character. 504 func isASCII(fl FieldLevel) bool { 505 return aSCIIRegex.MatchString(fl.Field().String()) 506 } 507 508 // isUUID5 is the validation function for validating if the field's value is a valid v5 UUID. 509 func isUUID5(fl FieldLevel) bool { 510 return uUID5Regex.MatchString(fl.Field().String()) 511 } 512 513 // isUUID4 is the validation function for validating if the field's value is a valid v4 UUID. 514 func isUUID4(fl FieldLevel) bool { 515 return uUID4Regex.MatchString(fl.Field().String()) 516 } 517 518 // isUUID3 is the validation function for validating if the field's value is a valid v3 UUID. 519 func isUUID3(fl FieldLevel) bool { 520 return uUID3Regex.MatchString(fl.Field().String()) 521 } 522 523 // isUUID is the validation function for validating if the field's value is a valid UUID of any version. 524 func isUUID(fl FieldLevel) bool { 525 return uUIDRegex.MatchString(fl.Field().String()) 526 } 527 528 // isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID. 529 func isUUID5RFC4122(fl FieldLevel) bool { 530 return uUID5RFC4122Regex.MatchString(fl.Field().String()) 531 } 532 533 // isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID. 534 func isUUID4RFC4122(fl FieldLevel) bool { 535 return uUID4RFC4122Regex.MatchString(fl.Field().String()) 536 } 537 538 // isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID. 539 func isUUID3RFC4122(fl FieldLevel) bool { 540 return uUID3RFC4122Regex.MatchString(fl.Field().String()) 541 } 542 543 // isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version. 544 func isUUIDRFC4122(fl FieldLevel) bool { 545 return uUIDRFC4122Regex.MatchString(fl.Field().String()) 546 } 547 548 // isULID is the validation function for validating if the field's value is a valid ULID. 549 func isULID(fl FieldLevel) bool { 550 return uLIDRegex.MatchString(fl.Field().String()) 551 } 552 553 // isMD4 is the validation function for validating if the field's value is a valid MD4. 554 func isMD4(fl FieldLevel) bool { 555 return md4Regex.MatchString(fl.Field().String()) 556 } 557 558 // isMD5 is the validation function for validating if the field's value is a valid MD5. 559 func isMD5(fl FieldLevel) bool { 560 return md5Regex.MatchString(fl.Field().String()) 561 } 562 563 // isSHA256 is the validation function for validating if the field's value is a valid SHA256. 564 func isSHA256(fl FieldLevel) bool { 565 return sha256Regex.MatchString(fl.Field().String()) 566 } 567 568 // isSHA384 is the validation function for validating if the field's value is a valid SHA384. 569 func isSHA384(fl FieldLevel) bool { 570 return sha384Regex.MatchString(fl.Field().String()) 571 } 572 573 // isSHA512 is the validation function for validating if the field's value is a valid SHA512. 574 func isSHA512(fl FieldLevel) bool { 575 return sha512Regex.MatchString(fl.Field().String()) 576 } 577 578 // isRIPEMD128 is the validation function for validating if the field's value is a valid PIPEMD128. 579 func isRIPEMD128(fl FieldLevel) bool { 580 return ripemd128Regex.MatchString(fl.Field().String()) 581 } 582 583 // isRIPEMD160 is the validation function for validating if the field's value is a valid PIPEMD160. 584 func isRIPEMD160(fl FieldLevel) bool { 585 return ripemd160Regex.MatchString(fl.Field().String()) 586 } 587 588 // isTIGER128 is the validation function for validating if the field's value is a valid TIGER128. 589 func isTIGER128(fl FieldLevel) bool { 590 return tiger128Regex.MatchString(fl.Field().String()) 591 } 592 593 // isTIGER160 is the validation function for validating if the field's value is a valid TIGER160. 594 func isTIGER160(fl FieldLevel) bool { 595 return tiger160Regex.MatchString(fl.Field().String()) 596 } 597 598 // isTIGER192 is the validation function for validating if the field's value is a valid isTIGER192. 599 func isTIGER192(fl FieldLevel) bool { 600 return tiger192Regex.MatchString(fl.Field().String()) 601 } 602 603 // isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. 604 func isISBN(fl FieldLevel) bool { 605 return isISBN10(fl) || isISBN13(fl) 606 } 607 608 // isISBN13 is the validation function for validating if the field's value is a valid v13 ISBN. 609 func isISBN13(fl FieldLevel) bool { 610 s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4) 611 612 if !iSBN13Regex.MatchString(s) { 613 return false 614 } 615 616 var checksum int32 617 var i int32 618 619 factor := []int32{1, 3} 620 621 for i = 0; i < 12; i++ { 622 checksum += factor[i%2] * int32(s[i]-'0') 623 } 624 625 return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 626 } 627 628 // isISBN10 is the validation function for validating if the field's value is a valid v10 ISBN. 629 func isISBN10(fl FieldLevel) bool { 630 s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3) 631 632 if !iSBN10Regex.MatchString(s) { 633 return false 634 } 635 636 var checksum int32 637 var i int32 638 639 for i = 0; i < 9; i++ { 640 checksum += (i + 1) * int32(s[i]-'0') 641 } 642 643 if s[9] == 'X' { 644 checksum += 10 * 10 645 } else { 646 checksum += 10 * int32(s[9]-'0') 647 } 648 649 return checksum%11 == 0 650 } 651 652 // isEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address. 653 func isEthereumAddress(fl FieldLevel) bool { 654 address := fl.Field().String() 655 656 return ethAddressRegex.MatchString(address) 657 } 658 659 // isEthereumAddressChecksum is the validation function for validating if the field's value is a valid checksumed Ethereum address. 660 func isEthereumAddressChecksum(fl FieldLevel) bool { 661 address := fl.Field().String() 662 663 if !ethAddressRegex.MatchString(address) { 664 return false 665 } 666 // Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md 667 address = address[2:] // Skip "0x" prefix. 668 h := sha3.NewLegacyKeccak256() 669 // hash.Hash's io.Writer implementation says it never returns an error. https://golang.org/pkg/hash/#Hash 670 _, _ = h.Write([]byte(strings.ToLower(address))) 671 hash := hex.EncodeToString(h.Sum(nil)) 672 673 for i := 0; i < len(address); i++ { 674 if address[i] <= '9' { // Skip 0-9 digits: they don't have upper/lower-case. 675 continue 676 } 677 if hash[i] > '7' && address[i] >= 'a' || hash[i] <= '7' && address[i] <= 'F' { 678 return false 679 } 680 } 681 682 return true 683 } 684 685 // isBitcoinAddress is the validation function for validating if the field's value is a valid btc address 686 func isBitcoinAddress(fl FieldLevel) bool { 687 address := fl.Field().String() 688 689 if !btcAddressRegex.MatchString(address) { 690 return false 691 } 692 693 alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 694 695 decode := [25]byte{} 696 697 for _, n := range []byte(address) { 698 d := bytes.IndexByte(alphabet, n) 699 700 for i := 24; i >= 0; i-- { 701 d += 58 * int(decode[i]) 702 decode[i] = byte(d % 256) 703 d /= 256 704 } 705 } 706 707 h := sha256.New() 708 _, _ = h.Write(decode[:21]) 709 d := h.Sum([]byte{}) 710 h = sha256.New() 711 _, _ = h.Write(d) 712 713 validchecksum := [4]byte{} 714 computedchecksum := [4]byte{} 715 716 copy(computedchecksum[:], h.Sum(d[:0])) 717 copy(validchecksum[:], decode[21:]) 718 719 return validchecksum == computedchecksum 720 } 721 722 // isBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address 723 func isBitcoinBech32Address(fl FieldLevel) bool { 724 address := fl.Field().String() 725 726 if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) { 727 return false 728 } 729 730 am := len(address) % 8 731 732 if am == 0 || am == 3 || am == 5 { 733 return false 734 } 735 736 address = strings.ToLower(address) 737 738 alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 739 740 hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc 741 addr := address[3:] 742 dp := make([]int, 0, len(addr)) 743 744 for _, c := range addr { 745 dp = append(dp, strings.IndexRune(alphabet, c)) 746 } 747 748 ver := dp[0] 749 750 if ver < 0 || ver > 16 { 751 return false 752 } 753 754 if ver == 0 { 755 if len(address) != 42 && len(address) != 62 { 756 return false 757 } 758 } 759 760 values := append(hr, dp...) 761 762 GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} 763 764 p := 1 765 766 for _, v := range values { 767 b := p >> 25 768 p = (p&0x1ffffff)<<5 ^ v 769 770 for i := 0; i < 5; i++ { 771 if (b>>uint(i))&1 == 1 { 772 p ^= GEN[i] 773 } 774 } 775 } 776 777 if p != 1 { 778 return false 779 } 780 781 b := uint(0) 782 acc := 0 783 mv := (1 << 5) - 1 784 var sw []int 785 786 for _, v := range dp[1 : len(dp)-6] { 787 acc = (acc << 5) | v 788 b += 5 789 for b >= 8 { 790 b -= 8 791 sw = append(sw, (acc>>b)&mv) 792 } 793 } 794 795 if len(sw) < 2 || len(sw) > 40 { 796 return false 797 } 798 799 return true 800 } 801 802 // excludesRune is the validation function for validating that the field's value does not contain the rune specified within the param. 803 func excludesRune(fl FieldLevel) bool { 804 return !containsRune(fl) 805 } 806 807 // excludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param. 808 func excludesAll(fl FieldLevel) bool { 809 return !containsAny(fl) 810 } 811 812 // excludes is the validation function for validating that the field's value does not contain the text specified within the param. 813 func excludes(fl FieldLevel) bool { 814 return !contains(fl) 815 } 816 817 // containsRune is the validation function for validating that the field's value contains the rune specified within the param. 818 func containsRune(fl FieldLevel) bool { 819 r, _ := utf8.DecodeRuneInString(fl.Param()) 820 821 return strings.ContainsRune(fl.Field().String(), r) 822 } 823 824 // containsAny is the validation function for validating that the field's value contains any of the characters specified within the param. 825 func containsAny(fl FieldLevel) bool { 826 return strings.ContainsAny(fl.Field().String(), fl.Param()) 827 } 828 829 // contains is the validation function for validating that the field's value contains the text specified within the param. 830 func contains(fl FieldLevel) bool { 831 return strings.Contains(fl.Field().String(), fl.Param()) 832 } 833 834 // startsWith is the validation function for validating that the field's value starts with the text specified within the param. 835 func startsWith(fl FieldLevel) bool { 836 return strings.HasPrefix(fl.Field().String(), fl.Param()) 837 } 838 839 // endsWith is the validation function for validating that the field's value ends with the text specified within the param. 840 func endsWith(fl FieldLevel) bool { 841 return strings.HasSuffix(fl.Field().String(), fl.Param()) 842 } 843 844 // startsNotWith is the validation function for validating that the field's value does not start with the text specified within the param. 845 func startsNotWith(fl FieldLevel) bool { 846 return !startsWith(fl) 847 } 848 849 // endsNotWith is the validation function for validating that the field's value does not end with the text specified within the param. 850 func endsNotWith(fl FieldLevel) bool { 851 return !endsWith(fl) 852 } 853 854 // fieldContains is the validation function for validating if the current field's value contains the field specified by the param's value. 855 func fieldContains(fl FieldLevel) bool { 856 field := fl.Field() 857 858 currentField, _, ok := fl.GetStructFieldOK() 859 860 if !ok { 861 return false 862 } 863 864 return strings.Contains(field.String(), currentField.String()) 865 } 866 867 // fieldExcludes is the validation function for validating if the current field's value excludes the field specified by the param's value. 868 func fieldExcludes(fl FieldLevel) bool { 869 field := fl.Field() 870 871 currentField, _, ok := fl.GetStructFieldOK() 872 if !ok { 873 return true 874 } 875 876 return !strings.Contains(field.String(), currentField.String()) 877 } 878 879 // 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. 880 func isNeField(fl FieldLevel) bool { 881 field := fl.Field() 882 kind := field.Kind() 883 884 currentField, currentKind, ok := fl.GetStructFieldOK() 885 886 if !ok || currentKind != kind { 887 return true 888 } 889 890 switch kind { 891 892 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 893 return field.Int() != currentField.Int() 894 895 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 896 return field.Uint() != currentField.Uint() 897 898 case reflect.Float32, reflect.Float64: 899 return field.Float() != currentField.Float() 900 901 case reflect.Slice, reflect.Map, reflect.Array: 902 return int64(field.Len()) != int64(currentField.Len()) 903 904 case reflect.Bool: 905 return field.Bool() != currentField.Bool() 906 907 case reflect.Struct: 908 909 fieldType := field.Type() 910 911 if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { 912 913 t := currentField.Interface().(time.Time) 914 fieldTime := field.Interface().(time.Time) 915 916 return !fieldTime.Equal(t) 917 } 918 919 // Not Same underlying type i.e. struct and time 920 if fieldType != currentField.Type() { 921 return true 922 } 923 } 924 925 // default reflect.String: 926 return field.String() != currentField.String() 927 } 928 929 // isNe is the validation function for validating that the field's value does not equal the provided param value. 930 func isNe(fl FieldLevel) bool { 931 return !isEq(fl) 932 } 933 934 // isNeIgnoreCase is the validation function for validating that the field's string value does not equal the 935 // provided param value. The comparison is case-insensitive 936 func isNeIgnoreCase(fl FieldLevel) bool { 937 return !isEqIgnoreCase(fl) 938 } 939 940 // 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. 941 func isLteCrossStructField(fl FieldLevel) bool { 942 field := fl.Field() 943 kind := field.Kind() 944 945 topField, topKind, ok := fl.GetStructFieldOK() 946 if !ok || topKind != kind { 947 return false 948 } 949 950 switch kind { 951 952 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 953 return field.Int() <= topField.Int() 954 955 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 956 return field.Uint() <= topField.Uint() 957 958 case reflect.Float32, reflect.Float64: 959 return field.Float() <= topField.Float() 960 961 case reflect.Slice, reflect.Map, reflect.Array: 962 return int64(field.Len()) <= int64(topField.Len()) 963 964 case reflect.Struct: 965 966 fieldType := field.Type() 967 968 if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { 969 970 fieldTime := field.Convert(timeType).Interface().(time.Time) 971 topTime := topField.Convert(timeType).Interface().(time.Time) 972 973 return fieldTime.Before(topTime) || fieldTime.Equal(topTime) 974 } 975 976 // Not Same underlying type i.e. struct and time 977 if fieldType != topField.Type() { 978 return false 979 } 980 } 981 982 // default reflect.String: 983 return field.String() <= topField.String() 984 } 985 986 // 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. 987 // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. 988 func isLtCrossStructField(fl FieldLevel) bool { 989 field := fl.Field() 990 kind := field.Kind() 991 992 topField, topKind, ok := fl.GetStructFieldOK() 993 if !ok || topKind != kind { 994 return false 995 } 996 997 switch kind { 998 999 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1000 return field.Int() < topField.Int() 1001 1002 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1003 return field.Uint() < topField.Uint() 1004 1005 case reflect.Float32, reflect.Float64: 1006 return field.Float() < topField.Float() 1007 1008 case reflect.Slice, reflect.Map, reflect.Array: 1009 return int64(field.Len()) < int64(topField.Len()) 1010 1011 case reflect.Struct: 1012 1013 fieldType := field.Type() 1014 1015 if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { 1016 1017 fieldTime := field.Convert(timeType).Interface().(time.Time) 1018 topTime := topField.Convert(timeType).Interface().(time.Time) 1019 1020 return fieldTime.Before(topTime) 1021 } 1022 1023 // Not Same underlying type i.e. struct and time 1024 if fieldType != topField.Type() { 1025 return false 1026 } 1027 } 1028 1029 // default reflect.String: 1030 return field.String() < topField.String() 1031 } 1032 1033 // 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. 1034 func isGteCrossStructField(fl FieldLevel) bool { 1035 field := fl.Field() 1036 kind := field.Kind() 1037 1038 topField, topKind, ok := fl.GetStructFieldOK() 1039 if !ok || topKind != kind { 1040 return false 1041 } 1042 1043 switch kind { 1044 1045 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1046 return field.Int() >= topField.Int() 1047 1048 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1049 return field.Uint() >= topField.Uint() 1050 1051 case reflect.Float32, reflect.Float64: 1052 return field.Float() >= topField.Float() 1053 1054 case reflect.Slice, reflect.Map, reflect.Array: 1055 return int64(field.Len()) >= int64(topField.Len()) 1056 1057 case reflect.Struct: 1058 1059 fieldType := field.Type() 1060 1061 if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { 1062 1063 fieldTime := field.Convert(timeType).Interface().(time.Time) 1064 topTime := topField.Convert(timeType).Interface().(time.Time) 1065 1066 return fieldTime.After(topTime) || fieldTime.Equal(topTime) 1067 } 1068 1069 // Not Same underlying type i.e. struct and time 1070 if fieldType != topField.Type() { 1071 return false 1072 } 1073 } 1074 1075 // default reflect.String: 1076 return field.String() >= topField.String() 1077 } 1078 1079 // 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. 1080 func isGtCrossStructField(fl FieldLevel) bool { 1081 field := fl.Field() 1082 kind := field.Kind() 1083 1084 topField, topKind, ok := fl.GetStructFieldOK() 1085 if !ok || topKind != kind { 1086 return false 1087 } 1088 1089 switch kind { 1090 1091 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1092 return field.Int() > topField.Int() 1093 1094 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1095 return field.Uint() > topField.Uint() 1096 1097 case reflect.Float32, reflect.Float64: 1098 return field.Float() > topField.Float() 1099 1100 case reflect.Slice, reflect.Map, reflect.Array: 1101 return int64(field.Len()) > int64(topField.Len()) 1102 1103 case reflect.Struct: 1104 1105 fieldType := field.Type() 1106 1107 if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { 1108 1109 fieldTime := field.Convert(timeType).Interface().(time.Time) 1110 topTime := topField.Convert(timeType).Interface().(time.Time) 1111 1112 return fieldTime.After(topTime) 1113 } 1114 1115 // Not Same underlying type i.e. struct and time 1116 if fieldType != topField.Type() { 1117 return false 1118 } 1119 } 1120 1121 // default reflect.String: 1122 return field.String() > topField.String() 1123 } 1124 1125 // 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. 1126 func isNeCrossStructField(fl FieldLevel) bool { 1127 field := fl.Field() 1128 kind := field.Kind() 1129 1130 topField, currentKind, ok := fl.GetStructFieldOK() 1131 if !ok || currentKind != kind { 1132 return true 1133 } 1134 1135 switch kind { 1136 1137 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1138 return topField.Int() != field.Int() 1139 1140 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1141 return topField.Uint() != field.Uint() 1142 1143 case reflect.Float32, reflect.Float64: 1144 return topField.Float() != field.Float() 1145 1146 case reflect.Slice, reflect.Map, reflect.Array: 1147 return int64(topField.Len()) != int64(field.Len()) 1148 1149 case reflect.Bool: 1150 return topField.Bool() != field.Bool() 1151 1152 case reflect.Struct: 1153 1154 fieldType := field.Type() 1155 1156 if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { 1157 1158 t := field.Convert(timeType).Interface().(time.Time) 1159 fieldTime := topField.Convert(timeType).Interface().(time.Time) 1160 1161 return !fieldTime.Equal(t) 1162 } 1163 1164 // Not Same underlying type i.e. struct and time 1165 if fieldType != topField.Type() { 1166 return true 1167 } 1168 } 1169 1170 // default reflect.String: 1171 return topField.String() != field.String() 1172 } 1173 1174 // 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. 1175 func isEqCrossStructField(fl FieldLevel) bool { 1176 field := fl.Field() 1177 kind := field.Kind() 1178 1179 topField, topKind, ok := fl.GetStructFieldOK() 1180 if !ok || topKind != kind { 1181 return false 1182 } 1183 1184 switch kind { 1185 1186 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1187 return topField.Int() == field.Int() 1188 1189 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1190 return topField.Uint() == field.Uint() 1191 1192 case reflect.Float32, reflect.Float64: 1193 return topField.Float() == field.Float() 1194 1195 case reflect.Slice, reflect.Map, reflect.Array: 1196 return int64(topField.Len()) == int64(field.Len()) 1197 1198 case reflect.Bool: 1199 return topField.Bool() == field.Bool() 1200 1201 case reflect.Struct: 1202 1203 fieldType := field.Type() 1204 1205 if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) { 1206 1207 t := field.Convert(timeType).Interface().(time.Time) 1208 fieldTime := topField.Convert(timeType).Interface().(time.Time) 1209 1210 return fieldTime.Equal(t) 1211 } 1212 1213 // Not Same underlying type i.e. struct and time 1214 if fieldType != topField.Type() { 1215 return false 1216 } 1217 } 1218 1219 // default reflect.String: 1220 return topField.String() == field.String() 1221 } 1222 1223 // isEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value. 1224 func isEqField(fl FieldLevel) bool { 1225 field := fl.Field() 1226 kind := field.Kind() 1227 1228 currentField, currentKind, ok := fl.GetStructFieldOK() 1229 if !ok || currentKind != kind { 1230 return false 1231 } 1232 1233 switch kind { 1234 1235 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1236 return field.Int() == currentField.Int() 1237 1238 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1239 return field.Uint() == currentField.Uint() 1240 1241 case reflect.Float32, reflect.Float64: 1242 return field.Float() == currentField.Float() 1243 1244 case reflect.Slice, reflect.Map, reflect.Array: 1245 return int64(field.Len()) == int64(currentField.Len()) 1246 1247 case reflect.Bool: 1248 return field.Bool() == currentField.Bool() 1249 1250 case reflect.Struct: 1251 1252 fieldType := field.Type() 1253 1254 if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { 1255 1256 t := currentField.Convert(timeType).Interface().(time.Time) 1257 fieldTime := field.Convert(timeType).Interface().(time.Time) 1258 1259 return fieldTime.Equal(t) 1260 } 1261 1262 // Not Same underlying type i.e. struct and time 1263 if fieldType != currentField.Type() { 1264 return false 1265 } 1266 } 1267 1268 // default reflect.String: 1269 return field.String() == currentField.String() 1270 } 1271 1272 // isEq is the validation function for validating if the current field's value is equal to the param's value. 1273 func isEq(fl FieldLevel) bool { 1274 field := fl.Field() 1275 param := fl.Param() 1276 1277 switch field.Kind() { 1278 1279 case reflect.String: 1280 return field.String() == param 1281 1282 case reflect.Slice, reflect.Map, reflect.Array: 1283 p := asInt(param) 1284 1285 return int64(field.Len()) == p 1286 1287 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1288 p := asIntFromType(field.Type(), param) 1289 1290 return field.Int() == p 1291 1292 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1293 p := asUint(param) 1294 1295 return field.Uint() == p 1296 1297 case reflect.Float32, reflect.Float64: 1298 p := asFloat(param) 1299 1300 return field.Float() == p 1301 1302 case reflect.Bool: 1303 p := asBool(param) 1304 1305 return field.Bool() == p 1306 } 1307 1308 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1309 } 1310 1311 // isEqIgnoreCase is the validation function for validating if the current field's string value is 1312 // equal to the param's value. 1313 // The comparison is case-insensitive. 1314 func isEqIgnoreCase(fl FieldLevel) bool { 1315 field := fl.Field() 1316 param := fl.Param() 1317 1318 switch field.Kind() { 1319 1320 case reflect.String: 1321 return strings.EqualFold(field.String(), param) 1322 } 1323 1324 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1325 } 1326 1327 // isPostcodeByIso3166Alpha2 validates by value which is country code in iso 3166 alpha 2 1328 // example: `postcode_iso3166_alpha2=US` 1329 func isPostcodeByIso3166Alpha2(fl FieldLevel) bool { 1330 field := fl.Field() 1331 param := fl.Param() 1332 1333 reg, found := postCodeRegexDict[param] 1334 if !found { 1335 return false 1336 } 1337 1338 return reg.MatchString(field.String()) 1339 } 1340 1341 // isPostcodeByIso3166Alpha2Field validates by field which represents for a value of country code in iso 3166 alpha 2 1342 // example: `postcode_iso3166_alpha2_field=CountryCode` 1343 func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool { 1344 field := fl.Field() 1345 params := parseOneOfParam2(fl.Param()) 1346 1347 if len(params) != 1 { 1348 return false 1349 } 1350 1351 currentField, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), params[0]) 1352 if !found { 1353 return false 1354 } 1355 1356 if kind != reflect.String { 1357 panic(fmt.Sprintf("Bad field type %T", currentField.Interface())) 1358 } 1359 1360 reg, found := postCodeRegexDict[currentField.String()] 1361 if !found { 1362 return false 1363 } 1364 1365 return reg.MatchString(field.String()) 1366 } 1367 1368 // isBase64 is the validation function for validating if the current field's value is a valid base 64. 1369 func isBase64(fl FieldLevel) bool { 1370 return base64Regex.MatchString(fl.Field().String()) 1371 } 1372 1373 // isBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string. 1374 func isBase64URL(fl FieldLevel) bool { 1375 return base64URLRegex.MatchString(fl.Field().String()) 1376 } 1377 1378 // isBase64RawURL is the validation function for validating if the current field's value is a valid base64 URL safe string without '=' padding. 1379 func isBase64RawURL(fl FieldLevel) bool { 1380 return base64RawURLRegex.MatchString(fl.Field().String()) 1381 } 1382 1383 // isURI is the validation function for validating if the current field's value is a valid URI. 1384 func isURI(fl FieldLevel) bool { 1385 field := fl.Field() 1386 1387 switch field.Kind() { 1388 case reflect.String: 1389 1390 s := field.String() 1391 1392 // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 1393 // emulate browser and strip the '#' suffix prior to validation. see issue-#237 1394 if i := strings.Index(s, "#"); i > -1 { 1395 s = s[:i] 1396 } 1397 1398 if len(s) == 0 { 1399 return false 1400 } 1401 1402 _, err := url.ParseRequestURI(s) 1403 1404 return err == nil 1405 } 1406 1407 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1408 } 1409 1410 // isURL is the validation function for validating if the current field's value is a valid URL. 1411 func isURL(fl FieldLevel) bool { 1412 field := fl.Field() 1413 1414 switch field.Kind() { 1415 case reflect.String: 1416 1417 s := field.String() 1418 1419 if len(s) == 0 { 1420 return false 1421 } 1422 1423 url, err := url.Parse(s) 1424 if err != nil || url.Scheme == "" { 1425 return false 1426 } 1427 1428 if url.Host == "" && url.Fragment == "" && url.Opaque == "" { 1429 return false 1430 } 1431 1432 return true 1433 } 1434 1435 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1436 } 1437 1438 // isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL. 1439 func isHttpURL(fl FieldLevel) bool { 1440 if !isURL(fl) { 1441 return false 1442 } 1443 1444 field := fl.Field() 1445 switch field.Kind() { 1446 case reflect.String: 1447 1448 s := strings.ToLower(field.String()) 1449 1450 url, err := url.Parse(s) 1451 if err != nil || url.Host == "" { 1452 return false 1453 } 1454 1455 return url.Scheme == "http" || url.Scheme == "https" 1456 } 1457 1458 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1459 } 1460 1461 // isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141. 1462 func isUrnRFC2141(fl FieldLevel) bool { 1463 field := fl.Field() 1464 1465 switch field.Kind() { 1466 case reflect.String: 1467 1468 str := field.String() 1469 1470 _, match := urn.Parse([]byte(str)) 1471 1472 return match 1473 } 1474 1475 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1476 } 1477 1478 // isFile is the validation function for validating if the current field's value is a valid existing file path. 1479 func isFile(fl FieldLevel) bool { 1480 field := fl.Field() 1481 1482 switch field.Kind() { 1483 case reflect.String: 1484 fileInfo, err := os.Stat(field.String()) 1485 if err != nil { 1486 return false 1487 } 1488 1489 return !fileInfo.IsDir() 1490 } 1491 1492 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1493 } 1494 1495 // isImage is the validation function for validating if the current field's value contains the path to a valid image file 1496 func isImage(fl FieldLevel) bool { 1497 mimetypes := map[string]bool{ 1498 "image/bmp": true, 1499 "image/cis-cod": true, 1500 "image/gif": true, 1501 "image/ief": true, 1502 "image/jpeg": true, 1503 "image/jp2": true, 1504 "image/jpx": true, 1505 "image/jpm": true, 1506 "image/pipeg": true, 1507 "image/png": true, 1508 "image/svg+xml": true, 1509 "image/tiff": true, 1510 "image/webp": true, 1511 "image/x-cmu-raster": true, 1512 "image/x-cmx": true, 1513 "image/x-icon": true, 1514 "image/x-portable-anymap": true, 1515 "image/x-portable-bitmap": true, 1516 "image/x-portable-graymap": true, 1517 "image/x-portable-pixmap": true, 1518 "image/x-rgb": true, 1519 "image/x-xbitmap": true, 1520 "image/x-xpixmap": true, 1521 "image/x-xwindowdump": true, 1522 } 1523 field := fl.Field() 1524 1525 switch field.Kind() { 1526 case reflect.String: 1527 filePath := field.String() 1528 fileInfo, err := os.Stat(filePath) 1529 1530 if err != nil { 1531 return false 1532 } 1533 1534 if fileInfo.IsDir() { 1535 return false 1536 } 1537 1538 file, err := os.Open(filePath) 1539 if err != nil { 1540 return false 1541 } 1542 defer file.Close() 1543 1544 mime, err := mimetype.DetectReader(file) 1545 if err != nil { 1546 return false 1547 } 1548 1549 if _, ok := mimetypes[mime.String()]; ok { 1550 return true 1551 } 1552 } 1553 return false 1554 } 1555 1556 // isFilePath is the validation function for validating if the current field's value is a valid file path. 1557 func isFilePath(fl FieldLevel) bool { 1558 1559 var exists bool 1560 var err error 1561 1562 field := fl.Field() 1563 1564 // If it exists, it obviously is valid. 1565 // This is done first to avoid code duplication and unnecessary additional logic. 1566 if exists = isFile(fl); exists { 1567 return true 1568 } 1569 1570 // It does not exist but may still be a valid filepath. 1571 switch field.Kind() { 1572 case reflect.String: 1573 // Every OS allows for whitespace, but none 1574 // let you use a file with no filename (to my knowledge). 1575 // Unless you're dealing with raw inodes, but I digress. 1576 if strings.TrimSpace(field.String()) == "" { 1577 return false 1578 } 1579 // We make sure it isn't a directory. 1580 if strings.HasSuffix(field.String(), string(os.PathSeparator)) { 1581 return false 1582 } 1583 if _, err = os.Stat(field.String()); err != nil { 1584 switch t := err.(type) { 1585 case *fs.PathError: 1586 if t.Err == syscall.EINVAL { 1587 // It's definitely an invalid character in the filepath. 1588 return false 1589 } 1590 // It could be a permission error, a does-not-exist error, etc. 1591 // Out-of-scope for this validation, though. 1592 return true 1593 default: 1594 // Something went *seriously* wrong. 1595 /* 1596 Per https://pkg.go.dev/os#Stat: 1597 "If there is an error, it will be of type *PathError." 1598 */ 1599 panic(err) 1600 } 1601 } 1602 } 1603 1604 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 1605 } 1606 1607 // isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number. 1608 func isE164(fl FieldLevel) bool { 1609 return e164Regex.MatchString(fl.Field().String()) 1610 } 1611 1612 // isEmail is the validation function for validating if the current field's value is a valid email address. 1613 func isEmail(fl FieldLevel) bool { 1614 return emailRegex.MatchString(fl.Field().String()) 1615 } 1616 1617 // isHSLA is the validation function for validating if the current field's value is a valid HSLA color. 1618 func isHSLA(fl FieldLevel) bool { 1619 return hslaRegex.MatchString(fl.Field().String()) 1620 } 1621 1622 // isHSL is the validation function for validating if the current field's value is a valid HSL color. 1623 func isHSL(fl FieldLevel) bool { 1624 return hslRegex.MatchString(fl.Field().String()) 1625 } 1626 1627 // isRGBA is the validation function for validating if the current field's value is a valid RGBA color. 1628 func isRGBA(fl FieldLevel) bool { 1629 return rgbaRegex.MatchString(fl.Field().String()) 1630 } 1631 1632 // isRGB is the validation function for validating if the current field's value is a valid RGB color. 1633 func isRGB(fl FieldLevel) bool { 1634 return rgbRegex.MatchString(fl.Field().String()) 1635 } 1636 1637 // isHEXColor is the validation function for validating if the current field's value is a valid HEX color. 1638 func isHEXColor(fl FieldLevel) bool { 1639 return hexColorRegex.MatchString(fl.Field().String()) 1640 } 1641 1642 // isHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal. 1643 func isHexadecimal(fl FieldLevel) bool { 1644 return hexadecimalRegex.MatchString(fl.Field().String()) 1645 } 1646 1647 // isNumber is the validation function for validating if the current field's value is a valid number. 1648 func isNumber(fl FieldLevel) bool { 1649 switch fl.Field().Kind() { 1650 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: 1651 return true 1652 default: 1653 return numberRegex.MatchString(fl.Field().String()) 1654 } 1655 } 1656 1657 // isNumeric is the validation function for validating if the current field's value is a valid numeric value. 1658 func isNumeric(fl FieldLevel) bool { 1659 switch fl.Field().Kind() { 1660 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: 1661 return true 1662 default: 1663 return numericRegex.MatchString(fl.Field().String()) 1664 } 1665 } 1666 1667 // isAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value. 1668 func isAlphanum(fl FieldLevel) bool { 1669 return alphaNumericRegex.MatchString(fl.Field().String()) 1670 } 1671 1672 // isAlpha is the validation function for validating if the current field's value is a valid alpha value. 1673 func isAlpha(fl FieldLevel) bool { 1674 return alphaRegex.MatchString(fl.Field().String()) 1675 } 1676 1677 // isAlphanumUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value. 1678 func isAlphanumUnicode(fl FieldLevel) bool { 1679 return alphaUnicodeNumericRegex.MatchString(fl.Field().String()) 1680 } 1681 1682 // isAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value. 1683 func isAlphaUnicode(fl FieldLevel) bool { 1684 return alphaUnicodeRegex.MatchString(fl.Field().String()) 1685 } 1686 1687 // 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. 1688 func isBoolean(fl FieldLevel) bool { 1689 switch fl.Field().Kind() { 1690 case reflect.Bool: 1691 return true 1692 default: 1693 _, err := strconv.ParseBool(fl.Field().String()) 1694 return err == nil 1695 } 1696 } 1697 1698 // isDefault is the opposite of required aka hasValue 1699 func isDefault(fl FieldLevel) bool { 1700 return !hasValue(fl) 1701 } 1702 1703 // hasValue is the validation function for validating if the current field's value is not the default static value. 1704 func hasValue(fl FieldLevel) bool { 1705 field := fl.Field() 1706 switch field.Kind() { 1707 case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: 1708 return !field.IsNil() 1709 default: 1710 if fl.(*validate).fldIsPointer && field.Interface() != nil { 1711 return true 1712 } 1713 return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() 1714 } 1715 } 1716 1717 // requireCheckFieldKind is a func for check field kind 1718 func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool { 1719 field := fl.Field() 1720 kind := field.Kind() 1721 var nullable, found bool 1722 if len(param) > 0 { 1723 field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param) 1724 if !found { 1725 return defaultNotFoundValue 1726 } 1727 } 1728 switch kind { 1729 case reflect.Invalid: 1730 return defaultNotFoundValue 1731 case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: 1732 return field.IsNil() 1733 default: 1734 if nullable && field.Interface() != nil { 1735 return false 1736 } 1737 return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface() 1738 } 1739 } 1740 1741 // requireCheckFieldValue is a func for check field value 1742 func requireCheckFieldValue( 1743 fl FieldLevel, param string, value string, defaultNotFoundValue bool, 1744 ) bool { 1745 field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param) 1746 if !found { 1747 return defaultNotFoundValue 1748 } 1749 1750 switch kind { 1751 1752 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1753 return field.Int() == asInt(value) 1754 1755 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1756 return field.Uint() == asUint(value) 1757 1758 case reflect.Float32, reflect.Float64: 1759 return field.Float() == asFloat(value) 1760 1761 case reflect.Slice, reflect.Map, reflect.Array: 1762 return int64(field.Len()) == asInt(value) 1763 1764 case reflect.Bool: 1765 return field.Bool() == asBool(value) 1766 } 1767 1768 // default reflect.String: 1769 return field.String() == value 1770 } 1771 1772 // requiredIf is the validation function 1773 // 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. 1774 func requiredIf(fl FieldLevel) bool { 1775 params := parseOneOfParam2(fl.Param()) 1776 if len(params)%2 != 0 { 1777 panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName())) 1778 } 1779 for i := 0; i < len(params); i += 2 { 1780 if !requireCheckFieldValue(fl, params[i], params[i+1], false) { 1781 return true 1782 } 1783 } 1784 return hasValue(fl) 1785 } 1786 1787 // excludedIf is the validation function 1788 // 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. 1789 func excludedIf(fl FieldLevel) bool { 1790 params := parseOneOfParam2(fl.Param()) 1791 if len(params)%2 != 0 { 1792 panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName())) 1793 } 1794 1795 for i := 0; i < len(params); i += 2 { 1796 if !requireCheckFieldValue(fl, params[i], params[i+1], false) { 1797 return true 1798 } 1799 } 1800 return !hasValue(fl) 1801 } 1802 1803 // requiredUnless is the validation function 1804 // 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. 1805 func requiredUnless(fl FieldLevel) bool { 1806 params := parseOneOfParam2(fl.Param()) 1807 if len(params)%2 != 0 { 1808 panic(fmt.Sprintf("Bad param number for required_unless %s", fl.FieldName())) 1809 } 1810 1811 for i := 0; i < len(params); i += 2 { 1812 if requireCheckFieldValue(fl, params[i], params[i+1], false) { 1813 return true 1814 } 1815 } 1816 return hasValue(fl) 1817 } 1818 1819 // skipUnless is the validation function 1820 // 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. 1821 func skipUnless(fl FieldLevel) bool { 1822 params := parseOneOfParam2(fl.Param()) 1823 if len(params)%2 != 0 { 1824 panic(fmt.Sprintf("Bad param number for skip_unless %s", fl.FieldName())) 1825 } 1826 for i := 0; i < len(params); i += 2 { 1827 if !requireCheckFieldValue(fl, params[i], params[i+1], false) { 1828 return true 1829 } 1830 } 1831 return hasValue(fl) 1832 } 1833 1834 // excludedUnless is the validation function 1835 // 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. 1836 func excludedUnless(fl FieldLevel) bool { 1837 params := parseOneOfParam2(fl.Param()) 1838 if len(params)%2 != 0 { 1839 panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName())) 1840 } 1841 for i := 0; i < len(params); i += 2 { 1842 if !requireCheckFieldValue(fl, params[i], params[i+1], false) { 1843 return !hasValue(fl) 1844 } 1845 } 1846 return true 1847 } 1848 1849 // excludedWith is the validation function 1850 // The field under validation must not be present or is empty if any of the other specified fields are present. 1851 func excludedWith(fl FieldLevel) bool { 1852 params := parseOneOfParam2(fl.Param()) 1853 for _, param := range params { 1854 if !requireCheckFieldKind(fl, param, true) { 1855 return !hasValue(fl) 1856 } 1857 } 1858 return true 1859 } 1860 1861 // requiredWith is the validation function 1862 // The field under validation must be present and not empty only if any of the other specified fields are present. 1863 func requiredWith(fl FieldLevel) bool { 1864 params := parseOneOfParam2(fl.Param()) 1865 for _, param := range params { 1866 if !requireCheckFieldKind(fl, param, true) { 1867 return hasValue(fl) 1868 } 1869 } 1870 return true 1871 } 1872 1873 // excludedWithAll is the validation function 1874 // The field under validation must not be present or is empty if all of the other specified fields are present. 1875 func excludedWithAll(fl FieldLevel) bool { 1876 params := parseOneOfParam2(fl.Param()) 1877 for _, param := range params { 1878 if requireCheckFieldKind(fl, param, true) { 1879 return true 1880 } 1881 } 1882 return !hasValue(fl) 1883 } 1884 1885 // requiredWithAll is the validation function 1886 // The field under validation must be present and not empty only if all of the other specified fields are present. 1887 func requiredWithAll(fl FieldLevel) bool { 1888 params := parseOneOfParam2(fl.Param()) 1889 for _, param := range params { 1890 if requireCheckFieldKind(fl, param, true) { 1891 return true 1892 } 1893 } 1894 return hasValue(fl) 1895 } 1896 1897 // excludedWithout is the validation function 1898 // The field under validation must not be present or is empty when any of the other specified fields are not present. 1899 func excludedWithout(fl FieldLevel) bool { 1900 if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) { 1901 return !hasValue(fl) 1902 } 1903 return true 1904 } 1905 1906 // requiredWithout is the validation function 1907 // The field under validation must be present and not empty only when any of the other specified fields are not present. 1908 func requiredWithout(fl FieldLevel) bool { 1909 if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) { 1910 return hasValue(fl) 1911 } 1912 return true 1913 } 1914 1915 // excludedWithoutAll is the validation function 1916 // The field under validation must not be present or is empty when all of the other specified fields are not present. 1917 func excludedWithoutAll(fl FieldLevel) bool { 1918 params := parseOneOfParam2(fl.Param()) 1919 for _, param := range params { 1920 if !requireCheckFieldKind(fl, param, true) { 1921 return true 1922 } 1923 } 1924 return !hasValue(fl) 1925 } 1926 1927 // requiredWithoutAll is the validation function 1928 // The field under validation must be present and not empty only when all of the other specified fields are not present. 1929 func requiredWithoutAll(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 // 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. 1940 func isGteField(fl FieldLevel) bool { 1941 field := fl.Field() 1942 kind := field.Kind() 1943 1944 currentField, currentKind, ok := fl.GetStructFieldOK() 1945 if !ok || currentKind != kind { 1946 return false 1947 } 1948 1949 switch kind { 1950 1951 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1952 1953 return field.Int() >= currentField.Int() 1954 1955 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 1956 1957 return field.Uint() >= currentField.Uint() 1958 1959 case reflect.Float32, reflect.Float64: 1960 1961 return field.Float() >= currentField.Float() 1962 1963 case reflect.Struct: 1964 1965 fieldType := field.Type() 1966 1967 if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { 1968 1969 t := currentField.Convert(timeType).Interface().(time.Time) 1970 fieldTime := field.Convert(timeType).Interface().(time.Time) 1971 1972 return fieldTime.After(t) || fieldTime.Equal(t) 1973 } 1974 1975 // Not Same underlying type i.e. struct and time 1976 if fieldType != currentField.Type() { 1977 return false 1978 } 1979 } 1980 1981 // default reflect.String 1982 return len(field.String()) >= len(currentField.String()) 1983 } 1984 1985 // isGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value. 1986 func isGtField(fl FieldLevel) bool { 1987 field := fl.Field() 1988 kind := field.Kind() 1989 1990 currentField, currentKind, ok := fl.GetStructFieldOK() 1991 if !ok || currentKind != kind { 1992 return false 1993 } 1994 1995 switch kind { 1996 1997 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 1998 1999 return field.Int() > currentField.Int() 2000 2001 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2002 2003 return field.Uint() > currentField.Uint() 2004 2005 case reflect.Float32, reflect.Float64: 2006 2007 return field.Float() > currentField.Float() 2008 2009 case reflect.Struct: 2010 2011 fieldType := field.Type() 2012 2013 if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { 2014 2015 t := currentField.Convert(timeType).Interface().(time.Time) 2016 fieldTime := field.Convert(timeType).Interface().(time.Time) 2017 2018 return fieldTime.After(t) 2019 } 2020 2021 // Not Same underlying type i.e. struct and time 2022 if fieldType != currentField.Type() { 2023 return false 2024 } 2025 } 2026 2027 // default reflect.String 2028 return len(field.String()) > len(currentField.String()) 2029 } 2030 2031 // isGte is the validation function for validating if the current field's value is greater than or equal to the param's value. 2032 func isGte(fl FieldLevel) bool { 2033 field := fl.Field() 2034 param := fl.Param() 2035 2036 switch field.Kind() { 2037 2038 case reflect.String: 2039 p := asInt(param) 2040 2041 return int64(utf8.RuneCountInString(field.String())) >= p 2042 2043 case reflect.Slice, reflect.Map, reflect.Array: 2044 p := asInt(param) 2045 2046 return int64(field.Len()) >= p 2047 2048 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2049 p := asIntFromType(field.Type(), param) 2050 2051 return field.Int() >= p 2052 2053 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2054 p := asUint(param) 2055 2056 return field.Uint() >= p 2057 2058 case reflect.Float32, reflect.Float64: 2059 p := asFloat(param) 2060 2061 return field.Float() >= p 2062 2063 case reflect.Struct: 2064 2065 if field.Type().ConvertibleTo(timeType) { 2066 2067 now := time.Now().UTC() 2068 t := field.Convert(timeType).Interface().(time.Time) 2069 2070 return t.After(now) || t.Equal(now) 2071 } 2072 } 2073 2074 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2075 } 2076 2077 // isGt is the validation function for validating if the current field's value is greater than the param's value. 2078 func isGt(fl FieldLevel) bool { 2079 field := fl.Field() 2080 param := fl.Param() 2081 2082 switch field.Kind() { 2083 2084 case reflect.String: 2085 p := asInt(param) 2086 2087 return int64(utf8.RuneCountInString(field.String())) > p 2088 2089 case reflect.Slice, reflect.Map, reflect.Array: 2090 p := asInt(param) 2091 2092 return int64(field.Len()) > p 2093 2094 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2095 p := asIntFromType(field.Type(), param) 2096 2097 return field.Int() > p 2098 2099 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2100 p := asUint(param) 2101 2102 return field.Uint() > p 2103 2104 case reflect.Float32, reflect.Float64: 2105 p := asFloat(param) 2106 2107 return field.Float() > p 2108 case reflect.Struct: 2109 2110 if field.Type().ConvertibleTo(timeType) { 2111 2112 return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC()) 2113 } 2114 } 2115 2116 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2117 } 2118 2119 // hasLengthOf is the validation function for validating if the current field's value is equal to the param's value. 2120 func hasLengthOf(fl FieldLevel) bool { 2121 field := fl.Field() 2122 param := fl.Param() 2123 2124 switch field.Kind() { 2125 2126 case reflect.String: 2127 p := asInt(param) 2128 2129 return int64(utf8.RuneCountInString(field.String())) == p 2130 2131 case reflect.Slice, reflect.Map, reflect.Array: 2132 p := asInt(param) 2133 2134 return int64(field.Len()) == p 2135 2136 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2137 p := asIntFromType(field.Type(), param) 2138 2139 return field.Int() == p 2140 2141 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2142 p := asUint(param) 2143 2144 return field.Uint() == p 2145 2146 case reflect.Float32, reflect.Float64: 2147 p := asFloat(param) 2148 2149 return field.Float() == p 2150 } 2151 2152 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2153 } 2154 2155 // hasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value. 2156 func hasMinOf(fl FieldLevel) bool { 2157 return isGte(fl) 2158 } 2159 2160 // 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. 2161 func isLteField(fl FieldLevel) bool { 2162 field := fl.Field() 2163 kind := field.Kind() 2164 2165 currentField, currentKind, ok := fl.GetStructFieldOK() 2166 if !ok || currentKind != kind { 2167 return false 2168 } 2169 2170 switch kind { 2171 2172 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2173 2174 return field.Int() <= currentField.Int() 2175 2176 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2177 2178 return field.Uint() <= currentField.Uint() 2179 2180 case reflect.Float32, reflect.Float64: 2181 2182 return field.Float() <= currentField.Float() 2183 2184 case reflect.Struct: 2185 2186 fieldType := field.Type() 2187 2188 if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { 2189 2190 t := currentField.Convert(timeType).Interface().(time.Time) 2191 fieldTime := field.Convert(timeType).Interface().(time.Time) 2192 2193 return fieldTime.Before(t) || fieldTime.Equal(t) 2194 } 2195 2196 // Not Same underlying type i.e. struct and time 2197 if fieldType != currentField.Type() { 2198 return false 2199 } 2200 } 2201 2202 // default reflect.String 2203 return len(field.String()) <= len(currentField.String()) 2204 } 2205 2206 // isLtField is the validation function for validating if the current field's value is less than the field specified by the param's value. 2207 func isLtField(fl FieldLevel) bool { 2208 field := fl.Field() 2209 kind := field.Kind() 2210 2211 currentField, currentKind, ok := fl.GetStructFieldOK() 2212 if !ok || currentKind != kind { 2213 return false 2214 } 2215 2216 switch kind { 2217 2218 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2219 2220 return field.Int() < currentField.Int() 2221 2222 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2223 2224 return field.Uint() < currentField.Uint() 2225 2226 case reflect.Float32, reflect.Float64: 2227 2228 return field.Float() < currentField.Float() 2229 2230 case reflect.Struct: 2231 2232 fieldType := field.Type() 2233 2234 if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) { 2235 2236 t := currentField.Convert(timeType).Interface().(time.Time) 2237 fieldTime := field.Convert(timeType).Interface().(time.Time) 2238 2239 return fieldTime.Before(t) 2240 } 2241 2242 // Not Same underlying type i.e. struct and time 2243 if fieldType != currentField.Type() { 2244 return false 2245 } 2246 } 2247 2248 // default reflect.String 2249 return len(field.String()) < len(currentField.String()) 2250 } 2251 2252 // isLte is the validation function for validating if the current field's value is less than or equal to the param's value. 2253 func isLte(fl FieldLevel) bool { 2254 field := fl.Field() 2255 param := fl.Param() 2256 2257 switch field.Kind() { 2258 2259 case reflect.String: 2260 p := asInt(param) 2261 2262 return int64(utf8.RuneCountInString(field.String())) <= p 2263 2264 case reflect.Slice, reflect.Map, reflect.Array: 2265 p := asInt(param) 2266 2267 return int64(field.Len()) <= p 2268 2269 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2270 p := asIntFromType(field.Type(), param) 2271 2272 return field.Int() <= p 2273 2274 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2275 p := asUint(param) 2276 2277 return field.Uint() <= p 2278 2279 case reflect.Float32, reflect.Float64: 2280 p := asFloat(param) 2281 2282 return field.Float() <= p 2283 2284 case reflect.Struct: 2285 2286 if field.Type().ConvertibleTo(timeType) { 2287 2288 now := time.Now().UTC() 2289 t := field.Convert(timeType).Interface().(time.Time) 2290 2291 return t.Before(now) || t.Equal(now) 2292 } 2293 } 2294 2295 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2296 } 2297 2298 // isLt is the validation function for validating if the current field's value is less than the param's value. 2299 func isLt(fl FieldLevel) bool { 2300 field := fl.Field() 2301 param := fl.Param() 2302 2303 switch field.Kind() { 2304 2305 case reflect.String: 2306 p := asInt(param) 2307 2308 return int64(utf8.RuneCountInString(field.String())) < p 2309 2310 case reflect.Slice, reflect.Map, reflect.Array: 2311 p := asInt(param) 2312 2313 return int64(field.Len()) < p 2314 2315 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2316 p := asIntFromType(field.Type(), param) 2317 2318 return field.Int() < p 2319 2320 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2321 p := asUint(param) 2322 2323 return field.Uint() < p 2324 2325 case reflect.Float32, reflect.Float64: 2326 p := asFloat(param) 2327 2328 return field.Float() < p 2329 2330 case reflect.Struct: 2331 2332 if field.Type().ConvertibleTo(timeType) { 2333 2334 return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC()) 2335 } 2336 } 2337 2338 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2339 } 2340 2341 // hasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value. 2342 func hasMaxOf(fl FieldLevel) bool { 2343 return isLte(fl) 2344 } 2345 2346 // isTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address. 2347 func isTCP4AddrResolvable(fl FieldLevel) bool { 2348 if !isIP4Addr(fl) { 2349 return false 2350 } 2351 2352 _, err := net.ResolveTCPAddr("tcp4", fl.Field().String()) 2353 return err == nil 2354 } 2355 2356 // isTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address. 2357 func isTCP6AddrResolvable(fl FieldLevel) bool { 2358 if !isIP6Addr(fl) { 2359 return false 2360 } 2361 2362 _, err := net.ResolveTCPAddr("tcp6", fl.Field().String()) 2363 2364 return err == nil 2365 } 2366 2367 // isTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address. 2368 func isTCPAddrResolvable(fl FieldLevel) bool { 2369 if !isIP4Addr(fl) && !isIP6Addr(fl) { 2370 return false 2371 } 2372 2373 _, err := net.ResolveTCPAddr("tcp", fl.Field().String()) 2374 2375 return err == nil 2376 } 2377 2378 // isUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address. 2379 func isUDP4AddrResolvable(fl FieldLevel) bool { 2380 if !isIP4Addr(fl) { 2381 return false 2382 } 2383 2384 _, err := net.ResolveUDPAddr("udp4", fl.Field().String()) 2385 2386 return err == nil 2387 } 2388 2389 // isUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address. 2390 func isUDP6AddrResolvable(fl FieldLevel) bool { 2391 if !isIP6Addr(fl) { 2392 return false 2393 } 2394 2395 _, err := net.ResolveUDPAddr("udp6", fl.Field().String()) 2396 2397 return err == nil 2398 } 2399 2400 // isUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address. 2401 func isUDPAddrResolvable(fl FieldLevel) bool { 2402 if !isIP4Addr(fl) && !isIP6Addr(fl) { 2403 return false 2404 } 2405 2406 _, err := net.ResolveUDPAddr("udp", fl.Field().String()) 2407 2408 return err == nil 2409 } 2410 2411 // isIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address. 2412 func isIP4AddrResolvable(fl FieldLevel) bool { 2413 if !isIPv4(fl) { 2414 return false 2415 } 2416 2417 _, err := net.ResolveIPAddr("ip4", fl.Field().String()) 2418 2419 return err == nil 2420 } 2421 2422 // isIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address. 2423 func isIP6AddrResolvable(fl FieldLevel) bool { 2424 if !isIPv6(fl) { 2425 return false 2426 } 2427 2428 _, err := net.ResolveIPAddr("ip6", fl.Field().String()) 2429 2430 return err == nil 2431 } 2432 2433 // isIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address. 2434 func isIPAddrResolvable(fl FieldLevel) bool { 2435 if !isIP(fl) { 2436 return false 2437 } 2438 2439 _, err := net.ResolveIPAddr("ip", fl.Field().String()) 2440 2441 return err == nil 2442 } 2443 2444 // isUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address. 2445 func isUnixAddrResolvable(fl FieldLevel) bool { 2446 _, err := net.ResolveUnixAddr("unix", fl.Field().String()) 2447 2448 return err == nil 2449 } 2450 2451 func isIP4Addr(fl FieldLevel) bool { 2452 val := fl.Field().String() 2453 2454 if idx := strings.LastIndex(val, ":"); idx != -1 { 2455 val = val[0:idx] 2456 } 2457 2458 ip := net.ParseIP(val) 2459 2460 return ip != nil && ip.To4() != nil 2461 } 2462 2463 func isIP6Addr(fl FieldLevel) bool { 2464 val := fl.Field().String() 2465 2466 if idx := strings.LastIndex(val, ":"); idx != -1 { 2467 if idx != 0 && val[idx-1:idx] == "]" { 2468 val = val[1 : idx-1] 2469 } 2470 } 2471 2472 ip := net.ParseIP(val) 2473 2474 return ip != nil && ip.To4() == nil 2475 } 2476 2477 func isHostnameRFC952(fl FieldLevel) bool { 2478 return hostnameRegexRFC952.MatchString(fl.Field().String()) 2479 } 2480 2481 func isHostnameRFC1123(fl FieldLevel) bool { 2482 return hostnameRegexRFC1123.MatchString(fl.Field().String()) 2483 } 2484 2485 func isFQDN(fl FieldLevel) bool { 2486 val := fl.Field().String() 2487 2488 if val == "" { 2489 return false 2490 } 2491 2492 return fqdnRegexRFC1123.MatchString(val) 2493 } 2494 2495 // isDir is the validation function for validating if the current field's value is a valid existing directory. 2496 func isDir(fl FieldLevel) bool { 2497 field := fl.Field() 2498 2499 if field.Kind() == reflect.String { 2500 fileInfo, err := os.Stat(field.String()) 2501 if err != nil { 2502 return false 2503 } 2504 2505 return fileInfo.IsDir() 2506 } 2507 2508 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2509 } 2510 2511 // isDirPath is the validation function for validating if the current field's value is a valid directory. 2512 func isDirPath(fl FieldLevel) bool { 2513 2514 var exists bool 2515 var err error 2516 2517 field := fl.Field() 2518 2519 // If it exists, it obviously is valid. 2520 // This is done first to avoid code duplication and unnecessary additional logic. 2521 if exists = isDir(fl); exists { 2522 return true 2523 } 2524 2525 // It does not exist but may still be a valid path. 2526 switch field.Kind() { 2527 case reflect.String: 2528 // Every OS allows for whitespace, but none 2529 // let you use a dir with no name (to my knowledge). 2530 // Unless you're dealing with raw inodes, but I digress. 2531 if strings.TrimSpace(field.String()) == "" { 2532 return false 2533 } 2534 if _, err = os.Stat(field.String()); err != nil { 2535 switch t := err.(type) { 2536 case *fs.PathError: 2537 if t.Err == syscall.EINVAL { 2538 // It's definitely an invalid character in the path. 2539 return false 2540 } 2541 // It could be a permission error, a does-not-exist error, etc. 2542 // Out-of-scope for this validation, though. 2543 // Lastly, we make sure it is a directory. 2544 if strings.HasSuffix(field.String(), string(os.PathSeparator)) { 2545 return true 2546 } else { 2547 return false 2548 } 2549 default: 2550 // Something went *seriously* wrong. 2551 /* 2552 Per https://pkg.go.dev/os#Stat: 2553 "If there is an error, it will be of type *PathError." 2554 */ 2555 panic(err) 2556 } 2557 } 2558 // We repeat the check here to make sure it is an explicit directory in case the above os.Stat didn't trigger an error. 2559 if strings.HasSuffix(field.String(), string(os.PathSeparator)) { 2560 return true 2561 } else { 2562 return false 2563 } 2564 } 2565 2566 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2567 } 2568 2569 // isJSON is the validation function for validating if the current field's value is a valid json string. 2570 func isJSON(fl FieldLevel) bool { 2571 field := fl.Field() 2572 2573 switch field.Kind() { 2574 case reflect.String: 2575 val := field.String() 2576 return json.Valid([]byte(val)) 2577 case reflect.Slice: 2578 fieldType := field.Type() 2579 2580 if fieldType.ConvertibleTo(byteSliceType) { 2581 b := field.Convert(byteSliceType).Interface().([]byte) 2582 return json.Valid(b) 2583 } 2584 } 2585 2586 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2587 } 2588 2589 // isJWT is the validation function for validating if the current field's value is a valid JWT string. 2590 func isJWT(fl FieldLevel) bool { 2591 return jWTRegex.MatchString(fl.Field().String()) 2592 } 2593 2594 // isHostnamePort validates a <dns>:<port> combination for fields typically used for socket address. 2595 func isHostnamePort(fl FieldLevel) bool { 2596 val := fl.Field().String() 2597 host, port, err := net.SplitHostPort(val) 2598 if err != nil { 2599 return false 2600 } 2601 // Port must be a iny <= 65535. 2602 if portNum, err := strconv.ParseInt( 2603 port, 10, 32, 2604 ); err != nil || portNum > 65535 || portNum < 1 { 2605 return false 2606 } 2607 2608 // If host is specified, it should match a DNS name 2609 if host != "" { 2610 return hostnameRegexRFC1123.MatchString(host) 2611 } 2612 return true 2613 } 2614 2615 // isLowercase is the validation function for validating if the current field's value is a lowercase string. 2616 func isLowercase(fl FieldLevel) bool { 2617 field := fl.Field() 2618 2619 if field.Kind() == reflect.String { 2620 if field.String() == "" { 2621 return false 2622 } 2623 return field.String() == strings.ToLower(field.String()) 2624 } 2625 2626 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2627 } 2628 2629 // isUppercase is the validation function for validating if the current field's value is an uppercase string. 2630 func isUppercase(fl FieldLevel) bool { 2631 field := fl.Field() 2632 2633 if field.Kind() == reflect.String { 2634 if field.String() == "" { 2635 return false 2636 } 2637 return field.String() == strings.ToUpper(field.String()) 2638 } 2639 2640 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2641 } 2642 2643 // isDatetime is the validation function for validating if the current field's value is a valid datetime string. 2644 func isDatetime(fl FieldLevel) bool { 2645 field := fl.Field() 2646 param := fl.Param() 2647 2648 if field.Kind() == reflect.String { 2649 _, err := time.Parse(param, field.String()) 2650 2651 return err == nil 2652 } 2653 2654 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2655 } 2656 2657 // isTimeZone is the validation function for validating if the current field's value is a valid time zone string. 2658 func isTimeZone(fl FieldLevel) bool { 2659 field := fl.Field() 2660 2661 if field.Kind() == reflect.String { 2662 // empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name 2663 if field.String() == "" { 2664 return false 2665 } 2666 2667 // 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 2668 if strings.ToLower(field.String()) == "local" { 2669 return false 2670 } 2671 2672 _, err := time.LoadLocation(field.String()) 2673 return err == nil 2674 } 2675 2676 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2677 } 2678 2679 // isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code. 2680 func isIso3166Alpha2(fl FieldLevel) bool { 2681 val := fl.Field().String() 2682 return iso3166_1_alpha2[val] 2683 } 2684 2685 // isIso3166Alpha3 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code. 2686 func isIso3166Alpha3(fl FieldLevel) bool { 2687 val := fl.Field().String() 2688 return iso3166_1_alpha3[val] 2689 } 2690 2691 // isIso3166AlphaNumeric is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code. 2692 func isIso3166AlphaNumeric(fl FieldLevel) bool { 2693 field := fl.Field() 2694 2695 var code int 2696 switch field.Kind() { 2697 case reflect.String: 2698 i, err := strconv.Atoi(field.String()) 2699 if err != nil { 2700 return false 2701 } 2702 code = i % 1000 2703 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2704 code = int(field.Int() % 1000) 2705 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 2706 code = int(field.Uint() % 1000) 2707 default: 2708 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2709 } 2710 return iso3166_1_alpha_numeric[code] 2711 } 2712 2713 // isIso31662 is the validation function for validating if the current field's value is a valid iso3166-2 code. 2714 func isIso31662(fl FieldLevel) bool { 2715 val := fl.Field().String() 2716 return iso3166_2[val] 2717 } 2718 2719 // isIso4217 is the validation function for validating if the current field's value is a valid iso4217 currency code. 2720 func isIso4217(fl FieldLevel) bool { 2721 val := fl.Field().String() 2722 return iso4217[val] 2723 } 2724 2725 // isIso4217Numeric is the validation function for validating if the current field's value is a valid iso4217 numeric currency code. 2726 func isIso4217Numeric(fl FieldLevel) bool { 2727 field := fl.Field() 2728 2729 var code int 2730 switch field.Kind() { 2731 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2732 code = int(field.Int()) 2733 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 2734 code = int(field.Uint()) 2735 default: 2736 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2737 } 2738 return iso4217_numeric[code] 2739 } 2740 2741 // 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 2742 func isBCP47LanguageTag(fl FieldLevel) bool { 2743 field := fl.Field() 2744 2745 if field.Kind() == reflect.String { 2746 _, err := language.Parse(field.String()) 2747 return err == nil 2748 } 2749 2750 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2751 } 2752 2753 // 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 2754 func isIsoBicFormat(fl FieldLevel) bool { 2755 bicString := fl.Field().String() 2756 2757 return bicRegex.MatchString(bicString) 2758 } 2759 2760 // 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 2761 func isSemverFormat(fl FieldLevel) bool { 2762 semverString := fl.Field().String() 2763 2764 return semverRegex.MatchString(semverString) 2765 } 2766 2767 // isCveFormat is the validation function for validating if the current field's value is a valid cve id, defined in CVE mitre org 2768 func isCveFormat(fl FieldLevel) bool { 2769 cveString := fl.Field().String() 2770 2771 return cveRegex.MatchString(cveString) 2772 } 2773 2774 // isDnsRFC1035LabelFormat is the validation function 2775 // for validating if the current field's value is 2776 // a valid dns RFC 1035 label, defined in RFC 1035. 2777 func isDnsRFC1035LabelFormat(fl FieldLevel) bool { 2778 val := fl.Field().String() 2779 return dnsRegexRFC1035Label.MatchString(val) 2780 } 2781 2782 // digitsHaveLuhnChecksum returns true if and only if the last element of the given digits slice is the Luhn checksum of the previous elements 2783 func digitsHaveLuhnChecksum(digits []string) bool { 2784 size := len(digits) 2785 sum := 0 2786 for i, digit := range digits { 2787 value, err := strconv.Atoi(digit) 2788 if err != nil { 2789 return false 2790 } 2791 if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 { 2792 v := value * 2 2793 if v >= 10 { 2794 sum += 1 + (v % 10) 2795 } else { 2796 sum += v 2797 } 2798 } else { 2799 sum += value 2800 } 2801 } 2802 return (sum % 10) == 0 2803 } 2804 2805 // isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID 2806 func isMongoDB(fl FieldLevel) bool { 2807 val := fl.Field().String() 2808 return mongodbRegex.MatchString(val) 2809 } 2810 2811 // isCreditCard is the validation function for validating if the current field's value is a valid credit card number 2812 func isCreditCard(fl FieldLevel) bool { 2813 val := fl.Field().String() 2814 var creditCard bytes.Buffer 2815 segments := strings.Split(val, " ") 2816 for _, segment := range segments { 2817 if len(segment) < 3 { 2818 return false 2819 } 2820 creditCard.WriteString(segment) 2821 } 2822 2823 ccDigits := strings.Split(creditCard.String(), "") 2824 size := len(ccDigits) 2825 if size < 12 || size > 19 { 2826 return false 2827 } 2828 2829 return digitsHaveLuhnChecksum(ccDigits) 2830 } 2831 2832 // hasLuhnChecksum is the validation for validating if the current field's value has a valid Luhn checksum 2833 func hasLuhnChecksum(fl FieldLevel) bool { 2834 field := fl.Field() 2835 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 2836 switch field.Kind() { 2837 case reflect.String: 2838 str = field.String() 2839 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 2840 str = strconv.FormatInt(field.Int(), 10) 2841 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 2842 str = strconv.FormatUint(field.Uint(), 10) 2843 default: 2844 panic(fmt.Sprintf("Bad field type %T", field.Interface())) 2845 } 2846 size := len(str) 2847 if size < 2 { // there has to be at least one digit that carries a meaning + the checksum 2848 return false 2849 } 2850 digits := strings.Split(str, "") 2851 return digitsHaveLuhnChecksum(digits) 2852 } 2853 2854 // isCron is the validation function for validating if the current field's value is a valid cron expression 2855 func isCron(fl FieldLevel) bool { 2856 cronString := fl.Field().String() 2857 return cronRegex.MatchString(cronString) 2858 }