podman
1768 строк · 49.2 Кб
1// Package govalidator is package of validators and sanitizers for strings, structs and collections.
2package govalidator3
4import (5"bytes"6"crypto/rsa"7"crypto/x509"8"encoding/base64"9"encoding/json"10"encoding/pem"11"fmt"12"io/ioutil"13"net"14"net/url"15"reflect"16"regexp"17"sort"18"strconv"19"strings"20"time"21"unicode"22"unicode/utf8"23)
24
25var (26fieldsRequiredByDefault bool27nilPtrAllowedByRequired = false28notNumberRegexp = regexp.MustCompile("[^0-9]+")29whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`)30paramsRegexp = regexp.MustCompile(`\(.*\)$`)31)
32
33const maxURLRuneCount = 208334const minURLRuneCount = 335const rfc3339WithoutZone = "2006-01-02T15:04:05"36
37// SetFieldsRequiredByDefault causes validation to fail when struct fields
38// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
39// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
40// type exampleStruct struct {
41// Name string ``
42// Email string `valid:"email"`
43// This, however, will only fail when Email is empty or an invalid email address:
44// type exampleStruct2 struct {
45// Name string `valid:"-"`
46// Email string `valid:"email"`
47// Lastly, this will only fail when Email is an invalid email address but not when it's empty:
48// type exampleStruct2 struct {
49// Name string `valid:"-"`
50// Email string `valid:"email,optional"`
51func SetFieldsRequiredByDefault(value bool) {52fieldsRequiredByDefault = value53}
54
55// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required.
56// The validation will still reject ptr fields in their zero value state. Example with this enabled:
57// type exampleStruct struct {
58// Name *string `valid:"required"`
59// With `Name` set to "", this will be considered invalid input and will cause a validation error.
60// With `Name` set to nil, this will be considered valid by validation.
61// By default this is disabled.
62func SetNilPtrAllowedByRequired(value bool) {63nilPtrAllowedByRequired = value64}
65
66// IsEmail checks if the string is an email.
67func IsEmail(str string) bool {68// TODO uppercase letters are not supported69return rxEmail.MatchString(str)70}
71
72// IsExistingEmail checks if the string is an email of existing domain
73func IsExistingEmail(email string) bool {74
75if len(email) < 6 || len(email) > 254 {76return false77}78at := strings.LastIndex(email, "@")79if at <= 0 || at > len(email)-3 {80return false81}82user := email[:at]83host := email[at+1:]84if len(user) > 64 {85return false86}87switch host {88case "localhost", "example.com":89return true90}91if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) {92return false93}94if _, err := net.LookupMX(host); err != nil {95if _, err := net.LookupIP(host); err != nil {96return false97}98}99
100return true101}
102
103// IsURL checks if the string is an URL.
104func IsURL(str string) bool {105if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") {106return false107}108strTemp := str109if strings.Contains(str, ":") && !strings.Contains(str, "://") {110// support no indicated urlscheme but with colon for port number111// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString112strTemp = "http://" + str113}114u, err := url.Parse(strTemp)115if err != nil {116return false117}118if strings.HasPrefix(u.Host, ".") {119return false120}121if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {122return false123}124return rxURL.MatchString(str)125}
126
127// IsRequestURL checks if the string rawurl, assuming
128// it was received in an HTTP request, is a valid
129// URL confirm to RFC 3986
130func IsRequestURL(rawurl string) bool {131url, err := url.ParseRequestURI(rawurl)132if err != nil {133return false //Couldn't even parse the rawurl134}135if len(url.Scheme) == 0 {136return false //No Scheme found137}138return true139}
140
141// IsRequestURI checks if the string rawurl, assuming
142// it was received in an HTTP request, is an
143// absolute URI or an absolute path.
144func IsRequestURI(rawurl string) bool {145_, err := url.ParseRequestURI(rawurl)146return err == nil147}
148
149// IsAlpha checks if the string contains only letters (a-zA-Z). Empty string is valid.
150func IsAlpha(str string) bool {151if IsNull(str) {152return true153}154return rxAlpha.MatchString(str)155}
156
157//IsUTFLetter checks if the string contains only unicode letter characters.
158//Similar to IsAlpha but for all languages. Empty string is valid.
159func IsUTFLetter(str string) bool {160if IsNull(str) {161return true162}163
164for _, c := range str {165if !unicode.IsLetter(c) {166return false167}168}169return true170
171}
172
173// IsAlphanumeric checks if the string contains only letters and numbers. Empty string is valid.
174func IsAlphanumeric(str string) bool {175if IsNull(str) {176return true177}178return rxAlphanumeric.MatchString(str)179}
180
181// IsUTFLetterNumeric checks if the string contains only unicode letters and numbers. Empty string is valid.
182func IsUTFLetterNumeric(str string) bool {183if IsNull(str) {184return true185}186for _, c := range str {187if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok188return false189}190}191return true192
193}
194
195// IsNumeric checks if the string contains only numbers. Empty string is valid.
196func IsNumeric(str string) bool {197if IsNull(str) {198return true199}200return rxNumeric.MatchString(str)201}
202
203// IsUTFNumeric checks if the string contains only unicode numbers of any kind.
204// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
205func IsUTFNumeric(str string) bool {206if IsNull(str) {207return true208}209if strings.IndexAny(str, "+-") > 0 {210return false211}212if len(str) > 1 {213str = strings.TrimPrefix(str, "-")214str = strings.TrimPrefix(str, "+")215}216for _, c := range str {217if !unicode.IsNumber(c) { //numbers && minus sign are ok218return false219}220}221return true222
223}
224
225// IsUTFDigit checks if the string contains only unicode radix-10 decimal digits. Empty string is valid.
226func IsUTFDigit(str string) bool {227if IsNull(str) {228return true229}230if strings.IndexAny(str, "+-") > 0 {231return false232}233if len(str) > 1 {234str = strings.TrimPrefix(str, "-")235str = strings.TrimPrefix(str, "+")236}237for _, c := range str {238if !unicode.IsDigit(c) { //digits && minus sign are ok239return false240}241}242return true243
244}
245
246// IsHexadecimal checks if the string is a hexadecimal number.
247func IsHexadecimal(str string) bool {248return rxHexadecimal.MatchString(str)249}
250
251// IsHexcolor checks if the string is a hexadecimal color.
252func IsHexcolor(str string) bool {253return rxHexcolor.MatchString(str)254}
255
256// IsRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
257func IsRGBcolor(str string) bool {258return rxRGBcolor.MatchString(str)259}
260
261// IsLowerCase checks if the string is lowercase. Empty string is valid.
262func IsLowerCase(str string) bool {263if IsNull(str) {264return true265}266return str == strings.ToLower(str)267}
268
269// IsUpperCase checks if the string is uppercase. Empty string is valid.
270func IsUpperCase(str string) bool {271if IsNull(str) {272return true273}274return str == strings.ToUpper(str)275}
276
277// HasLowerCase checks if the string contains at least 1 lowercase. Empty string is valid.
278func HasLowerCase(str string) bool {279if IsNull(str) {280return true281}282return rxHasLowerCase.MatchString(str)283}
284
285// HasUpperCase checks if the string contains as least 1 uppercase. Empty string is valid.
286func HasUpperCase(str string) bool {287if IsNull(str) {288return true289}290return rxHasUpperCase.MatchString(str)291}
292
293// IsInt checks if the string is an integer. Empty string is valid.
294func IsInt(str string) bool {295if IsNull(str) {296return true297}298return rxInt.MatchString(str)299}
300
301// IsFloat checks if the string is a float.
302func IsFloat(str string) bool {303return str != "" && rxFloat.MatchString(str)304}
305
306// IsDivisibleBy checks if the string is a number that's divisible by another.
307// If second argument is not valid integer or zero, it's return false.
308// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
309func IsDivisibleBy(str, num string) bool {310f, _ := ToFloat(str)311p := int64(f)312q, _ := ToInt(num)313if q == 0 {314return false315}316return (p == 0) || (p%q == 0)317}
318
319// IsNull checks if the string is null.
320func IsNull(str string) bool {321return len(str) == 0322}
323
324// IsNotNull checks if the string is not null.
325func IsNotNull(str string) bool {326return !IsNull(str)327}
328
329// HasWhitespaceOnly checks the string only contains whitespace
330func HasWhitespaceOnly(str string) bool {331return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str)332}
333
334// HasWhitespace checks if the string contains any whitespace
335func HasWhitespace(str string) bool {336return len(str) > 0 && rxHasWhitespace.MatchString(str)337}
338
339// IsByteLength checks if the string's length (in bytes) falls in a range.
340func IsByteLength(str string, min, max int) bool {341return len(str) >= min && len(str) <= max342}
343
344// IsUUIDv3 checks if the string is a UUID version 3.
345func IsUUIDv3(str string) bool {346return rxUUID3.MatchString(str)347}
348
349// IsUUIDv4 checks if the string is a UUID version 4.
350func IsUUIDv4(str string) bool {351return rxUUID4.MatchString(str)352}
353
354// IsUUIDv5 checks if the string is a UUID version 5.
355func IsUUIDv5(str string) bool {356return rxUUID5.MatchString(str)357}
358
359// IsUUID checks if the string is a UUID (version 3, 4 or 5).
360func IsUUID(str string) bool {361return rxUUID.MatchString(str)362}
363
364// Byte to index table for O(1) lookups when unmarshaling.
365// We use 0xFF as sentinel value for invalid indexes.
366var ulidDec = [...]byte{3670xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3680xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3690xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3700xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3710xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,3720x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF,3730xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,3740x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF,3750x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E,3760x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C,3770x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14,3780x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,3790x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3800xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3810xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3820xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3830xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3840xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3850xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3860xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3870xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3880xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3890xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3900xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3910xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,3920xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,393}
394
395// EncodedSize is the length of a text encoded ULID.
396const ulidEncodedSize = 26397
398// IsULID checks if the string is a ULID.
399//
400// Implementation got from:
401// https://github.com/oklog/ulid (Apache-2.0 License)
402//
403func IsULID(str string) bool {404// Check if a base32 encoded ULID is the right length.405if len(str) != ulidEncodedSize {406return false407}408
409// Check if all the characters in a base32 encoded ULID are part of the410// expected base32 character set.411if ulidDec[str[0]] == 0xFF ||412ulidDec[str[1]] == 0xFF ||413ulidDec[str[2]] == 0xFF ||414ulidDec[str[3]] == 0xFF ||415ulidDec[str[4]] == 0xFF ||416ulidDec[str[5]] == 0xFF ||417ulidDec[str[6]] == 0xFF ||418ulidDec[str[7]] == 0xFF ||419ulidDec[str[8]] == 0xFF ||420ulidDec[str[9]] == 0xFF ||421ulidDec[str[10]] == 0xFF ||422ulidDec[str[11]] == 0xFF ||423ulidDec[str[12]] == 0xFF ||424ulidDec[str[13]] == 0xFF ||425ulidDec[str[14]] == 0xFF ||426ulidDec[str[15]] == 0xFF ||427ulidDec[str[16]] == 0xFF ||428ulidDec[str[17]] == 0xFF ||429ulidDec[str[18]] == 0xFF ||430ulidDec[str[19]] == 0xFF ||431ulidDec[str[20]] == 0xFF ||432ulidDec[str[21]] == 0xFF ||433ulidDec[str[22]] == 0xFF ||434ulidDec[str[23]] == 0xFF ||435ulidDec[str[24]] == 0xFF ||436ulidDec[str[25]] == 0xFF {437return false438}439
440// Check if the first character in a base32 encoded ULID will overflow. This441// happens because the base32 representation encodes 130 bits, while the442// ULID is only 128 bits.443//444// See https://github.com/oklog/ulid/issues/9 for details.445if str[0] > '7' {446return false447}448return true449}
450
451// IsCreditCard checks if the string is a credit card.
452func IsCreditCard(str string) bool {453sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")454if !rxCreditCard.MatchString(sanitized) {455return false456}457
458number, _ := ToInt(sanitized)459number, lastDigit := number / 10, number % 10460
461var sum int64462for i:=0; number > 0; i++ {463digit := number % 10464
465if i % 2 == 0 {466digit *= 2467if digit > 9 {468digit -= 9469}470}471
472sum += digit473number = number / 10474}475
476return (sum + lastDigit) % 10 == 0477}
478
479// IsISBN10 checks if the string is an ISBN version 10.
480func IsISBN10(str string) bool {481return IsISBN(str, 10)482}
483
484// IsISBN13 checks if the string is an ISBN version 13.
485func IsISBN13(str string) bool {486return IsISBN(str, 13)487}
488
489// IsISBN checks if the string is an ISBN (version 10 or 13).
490// If version value is not equal to 10 or 13, it will be checks both variants.
491func IsISBN(str string, version int) bool {492sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")493var checksum int32494var i int32495if version == 10 {496if !rxISBN10.MatchString(sanitized) {497return false498}499for i = 0; i < 9; i++ {500checksum += (i + 1) * int32(sanitized[i]-'0')501}502if sanitized[9] == 'X' {503checksum += 10 * 10504} else {505checksum += 10 * int32(sanitized[9]-'0')506}507if checksum%11 == 0 {508return true509}510return false511} else if version == 13 {512if !rxISBN13.MatchString(sanitized) {513return false514}515factor := []int32{1, 3}516for i = 0; i < 12; i++ {517checksum += factor[i%2] * int32(sanitized[i]-'0')518}519return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0520}521return IsISBN(str, 10) || IsISBN(str, 13)522}
523
524// IsJSON checks if the string is valid JSON (note: uses json.Unmarshal).
525func IsJSON(str string) bool {526var js json.RawMessage527return json.Unmarshal([]byte(str), &js) == nil528}
529
530// IsMultibyte checks if the string contains one or more multibyte chars. Empty string is valid.
531func IsMultibyte(str string) bool {532if IsNull(str) {533return true534}535return rxMultibyte.MatchString(str)536}
537
538// IsASCII checks if the string contains ASCII chars only. Empty string is valid.
539func IsASCII(str string) bool {540if IsNull(str) {541return true542}543return rxASCII.MatchString(str)544}
545
546// IsPrintableASCII checks if the string contains printable ASCII chars only. Empty string is valid.
547func IsPrintableASCII(str string) bool {548if IsNull(str) {549return true550}551return rxPrintableASCII.MatchString(str)552}
553
554// IsFullWidth checks if the string contains any full-width chars. Empty string is valid.
555func IsFullWidth(str string) bool {556if IsNull(str) {557return true558}559return rxFullWidth.MatchString(str)560}
561
562// IsHalfWidth checks if the string contains any half-width chars. Empty string is valid.
563func IsHalfWidth(str string) bool {564if IsNull(str) {565return true566}567return rxHalfWidth.MatchString(str)568}
569
570// IsVariableWidth checks if the string contains a mixture of full and half-width chars. Empty string is valid.
571func IsVariableWidth(str string) bool {572if IsNull(str) {573return true574}575return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str)576}
577
578// IsBase64 checks if a string is base64 encoded.
579func IsBase64(str string) bool {580return rxBase64.MatchString(str)581}
582
583// IsFilePath checks is a string is Win or Unix file path and returns it's type.
584func IsFilePath(str string) (bool, int) {585if rxWinPath.MatchString(str) {586//check windows path limit see:587// http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath588if len(str[3:]) > 32767 {589return false, Win590}591return true, Win592} else if rxUnixPath.MatchString(str) {593return true, Unix594}595return false, Unknown596}
597
598//IsWinFilePath checks both relative & absolute paths in Windows
599func IsWinFilePath(str string) bool {600if rxARWinPath.MatchString(str) {601//check windows path limit see:602// http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath603if len(str[3:]) > 32767 {604return false605}606return true607}608return false609}
610
611//IsUnixFilePath checks both relative & absolute paths in Unix
612func IsUnixFilePath(str string) bool {613if rxARUnixPath.MatchString(str) {614return true615}616return false617}
618
619// IsDataURI checks if a string is base64 encoded data URI such as an image
620func IsDataURI(str string) bool {621dataURI := strings.Split(str, ",")622if !rxDataURI.MatchString(dataURI[0]) {623return false624}625return IsBase64(dataURI[1])626}
627
628// IsMagnetURI checks if a string is valid magnet URI
629func IsMagnetURI(str string) bool {630return rxMagnetURI.MatchString(str)631}
632
633// IsISO3166Alpha2 checks if a string is valid two-letter country code
634func IsISO3166Alpha2(str string) bool {635for _, entry := range ISO3166List {636if str == entry.Alpha2Code {637return true638}639}640return false641}
642
643// IsISO3166Alpha3 checks if a string is valid three-letter country code
644func IsISO3166Alpha3(str string) bool {645for _, entry := range ISO3166List {646if str == entry.Alpha3Code {647return true648}649}650return false651}
652
653// IsISO693Alpha2 checks if a string is valid two-letter language code
654func IsISO693Alpha2(str string) bool {655for _, entry := range ISO693List {656if str == entry.Alpha2Code {657return true658}659}660return false661}
662
663// IsISO693Alpha3b checks if a string is valid three-letter language code
664func IsISO693Alpha3b(str string) bool {665for _, entry := range ISO693List {666if str == entry.Alpha3bCode {667return true668}669}670return false671}
672
673// IsDNSName will validate the given string as a DNS name
674func IsDNSName(str string) bool {675if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 {676// constraints already violated677return false678}679return !IsIP(str) && rxDNSName.MatchString(str)680}
681
682// IsHash checks if a string is a hash of type algorithm.
683// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b']
684func IsHash(str string, algorithm string) bool {685var len string686algo := strings.ToLower(algorithm)687
688if algo == "crc32" || algo == "crc32b" {689len = "8"690} else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" {691len = "32"692} else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" {693len = "40"694} else if algo == "tiger192" {695len = "48"696} else if algo == "sha3-224" {697len = "56"698} else if algo == "sha256" || algo == "sha3-256" {699len = "64"700} else if algo == "sha384" || algo == "sha3-384" {701len = "96"702} else if algo == "sha512" || algo == "sha3-512" {703len = "128"704} else {705return false706}707
708return Matches(str, "^[a-f0-9]{"+len+"}$")709}
710
711// IsSHA3224 checks is a string is a SHA3-224 hash. Alias for `IsHash(str, "sha3-224")`
712func IsSHA3224(str string) bool {713return IsHash(str, "sha3-224")714}
715
716// IsSHA3256 checks is a string is a SHA3-256 hash. Alias for `IsHash(str, "sha3-256")`
717func IsSHA3256(str string) bool {718return IsHash(str, "sha3-256")719}
720
721// IsSHA3384 checks is a string is a SHA3-384 hash. Alias for `IsHash(str, "sha3-384")`
722func IsSHA3384(str string) bool {723return IsHash(str, "sha3-384")724}
725
726// IsSHA3512 checks is a string is a SHA3-512 hash. Alias for `IsHash(str, "sha3-512")`
727func IsSHA3512(str string) bool {728return IsHash(str, "sha3-512")729}
730
731// IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")`
732func IsSHA512(str string) bool {733return IsHash(str, "sha512")734}
735
736// IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")`
737func IsSHA384(str string) bool {738return IsHash(str, "sha384")739}
740
741// IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")`
742func IsSHA256(str string) bool {743return IsHash(str, "sha256")744}
745
746// IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")`
747func IsTiger192(str string) bool {748return IsHash(str, "tiger192")749}
750
751// IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")`
752func IsTiger160(str string) bool {753return IsHash(str, "tiger160")754}
755
756// IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")`
757func IsRipeMD160(str string) bool {758return IsHash(str, "ripemd160")759}
760
761// IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")`
762func IsSHA1(str string) bool {763return IsHash(str, "sha1")764}
765
766// IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")`
767func IsTiger128(str string) bool {768return IsHash(str, "tiger128")769}
770
771// IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")`
772func IsRipeMD128(str string) bool {773return IsHash(str, "ripemd128")774}
775
776// IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")`
777func IsCRC32(str string) bool {778return IsHash(str, "crc32")779}
780
781// IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")`
782func IsCRC32b(str string) bool {783return IsHash(str, "crc32b")784}
785
786// IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")`
787func IsMD5(str string) bool {788return IsHash(str, "md5")789}
790
791// IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")`
792func IsMD4(str string) bool {793return IsHash(str, "md4")794}
795
796// IsDialString validates the given string for usage with the various Dial() functions
797func IsDialString(str string) bool {798if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) {799return true800}801
802return false803}
804
805// IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP`
806func IsIP(str string) bool {807return net.ParseIP(str) != nil808}
809
810// IsPort checks if a string represents a valid port
811func IsPort(str string) bool {812if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 {813return true814}815return false816}
817
818// IsIPv4 checks if the string is an IP version 4.
819func IsIPv4(str string) bool {820ip := net.ParseIP(str)821return ip != nil && strings.Contains(str, ".")822}
823
824// IsIPv6 checks if the string is an IP version 6.
825func IsIPv6(str string) bool {826ip := net.ParseIP(str)827return ip != nil && strings.Contains(str, ":")828}
829
830// IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6)
831func IsCIDR(str string) bool {832_, _, err := net.ParseCIDR(str)833return err == nil834}
835
836// IsMAC checks if a string is valid MAC address.
837// Possible MAC formats:
838// 01:23:45:67:89:ab
839// 01:23:45:67:89:ab:cd:ef
840// 01-23-45-67-89-ab
841// 01-23-45-67-89-ab-cd-ef
842// 0123.4567.89ab
843// 0123.4567.89ab.cdef
844func IsMAC(str string) bool {845_, err := net.ParseMAC(str)846return err == nil847}
848
849// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name
850func IsHost(str string) bool {851return IsIP(str) || IsDNSName(str)852}
853
854// IsMongoID checks if the string is a valid hex-encoded representation of a MongoDB ObjectId.
855func IsMongoID(str string) bool {856return rxHexadecimal.MatchString(str) && (len(str) == 24)857}
858
859// IsLatitude checks if a string is valid latitude.
860func IsLatitude(str string) bool {861return rxLatitude.MatchString(str)862}
863
864// IsLongitude checks if a string is valid longitude.
865func IsLongitude(str string) bool {866return rxLongitude.MatchString(str)867}
868
869// IsIMEI checks if a string is valid IMEI
870func IsIMEI(str string) bool {871return rxIMEI.MatchString(str)872}
873
874// IsIMSI checks if a string is valid IMSI
875func IsIMSI(str string) bool {876if !rxIMSI.MatchString(str) {877return false878}879
880mcc, err := strconv.ParseInt(str[0:3], 10, 32)881if err != nil {882return false883}884
885switch mcc {886case 202, 204, 206, 208, 212, 213, 214, 216, 218, 219:887case 220, 221, 222, 226, 228, 230, 231, 232, 234, 235:888case 238, 240, 242, 244, 246, 247, 248, 250, 255, 257:889case 259, 260, 262, 266, 268, 270, 272, 274, 276, 278:890case 280, 282, 283, 284, 286, 288, 289, 290, 292, 293:891case 294, 295, 297, 302, 308, 310, 311, 312, 313, 314:892case 315, 316, 330, 332, 334, 338, 340, 342, 344, 346:893case 348, 350, 352, 354, 356, 358, 360, 362, 363, 364:894case 365, 366, 368, 370, 372, 374, 376, 400, 401, 402:895case 404, 405, 406, 410, 412, 413, 414, 415, 416, 417:896case 418, 419, 420, 421, 422, 424, 425, 426, 427, 428:897case 429, 430, 431, 432, 434, 436, 437, 438, 440, 441:898case 450, 452, 454, 455, 456, 457, 460, 461, 466, 467:899case 470, 472, 502, 505, 510, 514, 515, 520, 525, 528:900case 530, 536, 537, 539, 540, 541, 542, 543, 544, 545:901case 546, 547, 548, 549, 550, 551, 552, 553, 554, 555:902case 602, 603, 604, 605, 606, 607, 608, 609, 610, 611:903case 612, 613, 614, 615, 616, 617, 618, 619, 620, 621:904case 622, 623, 624, 625, 626, 627, 628, 629, 630, 631:905case 632, 633, 634, 635, 636, 637, 638, 639, 640, 641:906case 642, 643, 645, 646, 647, 648, 649, 650, 651, 652:907case 653, 654, 655, 657, 658, 659, 702, 704, 706, 708:908case 710, 712, 714, 716, 722, 724, 730, 732, 734, 736:909case 738, 740, 742, 744, 746, 748, 750, 995:910return true911default:912return false913}914return true915}
916
917// IsRsaPublicKey checks if a string is valid public key with provided length
918func IsRsaPublicKey(str string, keylen int) bool {919bb := bytes.NewBufferString(str)920pemBytes, err := ioutil.ReadAll(bb)921if err != nil {922return false923}924block, _ := pem.Decode(pemBytes)925if block != nil && block.Type != "PUBLIC KEY" {926return false927}928var der []byte929
930if block != nil {931der = block.Bytes932} else {933der, err = base64.StdEncoding.DecodeString(str)934if err != nil {935return false936}937}938
939key, err := x509.ParsePKIXPublicKey(der)940if err != nil {941return false942}943pubkey, ok := key.(*rsa.PublicKey)944if !ok {945return false946}947bitlen := len(pubkey.N.Bytes()) * 8948return bitlen == int(keylen)949}
950
951// IsRegex checks if a give string is a valid regex with RE2 syntax or not
952func IsRegex(str string) bool {953if _, err := regexp.Compile(str); err == nil {954return true955}956return false957}
958
959func toJSONName(tag string) string {960if tag == "" {961return ""962}963
964// JSON name always comes first. If there's no options then split[0] is965// JSON name, if JSON name is not set, then split[0] is an empty string.966split := strings.SplitN(tag, ",", 2)967
968name := split[0]969
970// However it is possible that the field is skipped when971// (de-)serializing from/to JSON, in which case assume that there is no972// tag name to use973if name == "-" {974return ""975}976return name977}
978
979func prependPathToErrors(err error, path string) error {980switch err2 := err.(type) {981case Error:982err2.Path = append([]string{path}, err2.Path...)983return err2984case Errors:985errors := err2.Errors()986for i, err3 := range errors {987errors[i] = prependPathToErrors(err3, path)988}989return err2990}991return err992}
993
994// ValidateArray performs validation according to condition iterator that validates every element of the array
995func ValidateArray(array []interface{}, iterator ConditionIterator) bool {996return Every(array, iterator)997}
998
999// ValidateMap use validation map for fields.
1000// result will be equal to `false` if there are any errors.
1001// s is the map containing the data to be validated.
1002// m is the validation map in the form:
1003// map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}}
1004func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) {1005if s == nil {1006return true, nil1007}1008result := true1009var err error1010var errs Errors1011var index int1012val := reflect.ValueOf(s)1013for key, value := range s {1014presentResult := true1015validator, ok := m[key]1016if !ok {1017presentResult = false1018var err error1019err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key)1020err = prependPathToErrors(err, key)1021errs = append(errs, err)1022}1023valueField := reflect.ValueOf(value)1024mapResult := true1025typeResult := true1026structResult := true1027resultField := true1028switch subValidator := validator.(type) {1029case map[string]interface{}:1030var err error1031if v, ok := value.(map[string]interface{}); !ok {1032mapResult = false1033err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String())1034err = prependPathToErrors(err, key)1035errs = append(errs, err)1036} else {1037mapResult, err = ValidateMap(v, subValidator)1038if err != nil {1039mapResult = false1040err = prependPathToErrors(err, key)1041errs = append(errs, err)1042}1043}1044case string:1045if (valueField.Kind() == reflect.Struct ||1046(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&1047subValidator != "-" {1048var err error1049structResult, err = ValidateStruct(valueField.Interface())1050if err != nil {1051err = prependPathToErrors(err, key)1052errs = append(errs, err)1053}1054}1055resultField, err = typeCheck(valueField, reflect.StructField{1056Name: key,1057PkgPath: "",1058Type: val.Type(),1059Tag: reflect.StructTag(fmt.Sprintf("%s:%q", tagName, subValidator)),1060Offset: 0,1061Index: []int{index},1062Anonymous: false,1063}, val, nil)1064if err != nil {1065errs = append(errs, err)1066}1067case nil:1068// already handlerd when checked before1069default:1070typeResult = false1071err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String())1072err = prependPathToErrors(err, key)1073errs = append(errs, err)1074}1075result = result && presentResult && typeResult && resultField && structResult && mapResult1076index++1077}1078// checks required keys1079requiredResult := true1080for key, value := range m {1081if schema, ok := value.(string); ok {1082tags := parseTagIntoMap(schema)1083if required, ok := tags["required"]; ok {1084if _, ok := s[key]; !ok {1085requiredResult = false1086if required.customErrorMessage != "" {1087err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}}1088} else {1089err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}}1090}1091errs = append(errs, err)1092}1093}1094}1095}1096
1097if len(errs) > 0 {1098err = errs1099}1100return result && requiredResult, err1101}
1102
1103// ValidateStruct use tags for fields.
1104// result will be equal to `false` if there are any errors.
1105// todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail)
1106func ValidateStruct(s interface{}) (bool, error) {1107if s == nil {1108return true, nil1109}1110result := true1111var err error1112val := reflect.ValueOf(s)1113if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {1114val = val.Elem()1115}1116// we only accept structs1117if val.Kind() != reflect.Struct {1118return false, fmt.Errorf("function only accepts structs; got %s", val.Kind())1119}1120var errs Errors1121for i := 0; i < val.NumField(); i++ {1122valueField := val.Field(i)1123typeField := val.Type().Field(i)1124if typeField.PkgPath != "" {1125continue // Private field1126}1127structResult := true1128if valueField.Kind() == reflect.Interface {1129valueField = valueField.Elem()1130}1131if (valueField.Kind() == reflect.Struct ||1132(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&1133typeField.Tag.Get(tagName) != "-" {1134var err error1135structResult, err = ValidateStruct(valueField.Interface())1136if err != nil {1137err = prependPathToErrors(err, typeField.Name)1138errs = append(errs, err)1139}1140}1141resultField, err2 := typeCheck(valueField, typeField, val, nil)1142if err2 != nil {1143
1144// Replace structure name with JSON name if there is a tag on the variable1145jsonTag := toJSONName(typeField.Tag.Get("json"))1146if jsonTag != "" {1147switch jsonError := err2.(type) {1148case Error:1149jsonError.Name = jsonTag1150err2 = jsonError1151case Errors:1152for i2, err3 := range jsonError {1153switch customErr := err3.(type) {1154case Error:1155customErr.Name = jsonTag1156jsonError[i2] = customErr1157}1158}1159
1160err2 = jsonError1161}1162}1163
1164errs = append(errs, err2)1165}1166result = result && resultField && structResult1167}1168if len(errs) > 0 {1169err = errs1170}1171return result, err1172}
1173
1174// ValidateStructAsync performs async validation of the struct and returns results through the channels
1175func ValidateStructAsync(s interface{}) (<-chan bool, <-chan error) {1176res := make(chan bool)1177errors := make(chan error)1178
1179go func() {1180defer close(res)1181defer close(errors)1182
1183isValid, isFailed := ValidateStruct(s)1184
1185res <- isValid1186errors <- isFailed1187}()1188
1189return res, errors1190}
1191
1192// ValidateMapAsync performs async validation of the map and returns results through the channels
1193func ValidateMapAsync(s map[string]interface{}, m map[string]interface{}) (<-chan bool, <-chan error) {1194res := make(chan bool)1195errors := make(chan error)1196
1197go func() {1198defer close(res)1199defer close(errors)1200
1201isValid, isFailed := ValidateMap(s, m)1202
1203res <- isValid1204errors <- isFailed1205}()1206
1207return res, errors1208}
1209
1210// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
1211func parseTagIntoMap(tag string) tagOptionsMap {1212optionsMap := make(tagOptionsMap)1213options := strings.Split(tag, ",")1214
1215for i, option := range options {1216option = strings.TrimSpace(option)1217
1218validationOptions := strings.Split(option, "~")1219if !isValidTag(validationOptions[0]) {1220continue1221}1222if len(validationOptions) == 2 {1223optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i}1224} else {1225optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i}1226}1227}1228return optionsMap1229}
1230
1231func isValidTag(s string) bool {1232if s == "" {1233return false1234}1235for _, c := range s {1236switch {1237case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c):1238// Backslash and quote chars are reserved, but1239// otherwise any punctuation chars are allowed1240// in a tag name.1241default:1242if !unicode.IsLetter(c) && !unicode.IsDigit(c) {1243return false1244}1245}1246}1247return true1248}
1249
1250// IsSSN will validate the given string as a U.S. Social Security Number
1251func IsSSN(str string) bool {1252if str == "" || len(str) != 11 {1253return false1254}1255return rxSSN.MatchString(str)1256}
1257
1258// IsSemver checks if string is valid semantic version
1259func IsSemver(str string) bool {1260return rxSemver.MatchString(str)1261}
1262
1263// IsType checks if interface is of some type
1264func IsType(v interface{}, params ...string) bool {1265if len(params) == 1 {1266typ := params[0]1267return strings.Replace(reflect.TypeOf(v).String(), " ", "", -1) == strings.Replace(typ, " ", "", -1)1268}1269return false1270}
1271
1272// IsTime checks if string is valid according to given format
1273func IsTime(str string, format string) bool {1274_, err := time.Parse(format, str)1275return err == nil1276}
1277
1278// IsUnixTime checks if string is valid unix timestamp value
1279func IsUnixTime(str string) bool {1280if _, err := strconv.Atoi(str); err == nil {1281return true1282}1283return false1284}
1285
1286// IsRFC3339 checks if string is valid timestamp value according to RFC3339
1287func IsRFC3339(str string) bool {1288return IsTime(str, time.RFC3339)1289}
1290
1291// IsRFC3339WithoutZone checks if string is valid timestamp value according to RFC3339 which excludes the timezone.
1292func IsRFC3339WithoutZone(str string) bool {1293return IsTime(str, rfc3339WithoutZone)1294}
1295
1296// IsISO4217 checks if string is valid ISO currency code
1297func IsISO4217(str string) bool {1298for _, currency := range ISO4217List {1299if str == currency {1300return true1301}1302}1303
1304return false1305}
1306
1307// ByteLength checks string's length
1308func ByteLength(str string, params ...string) bool {1309if len(params) == 2 {1310min, _ := ToInt(params[0])1311max, _ := ToInt(params[1])1312return len(str) >= int(min) && len(str) <= int(max)1313}1314
1315return false1316}
1317
1318// RuneLength checks string's length
1319// Alias for StringLength
1320func RuneLength(str string, params ...string) bool {1321return StringLength(str, params...)1322}
1323
1324// IsRsaPub checks whether string is valid RSA key
1325// Alias for IsRsaPublicKey
1326func IsRsaPub(str string, params ...string) bool {1327if len(params) == 1 {1328len, _ := ToInt(params[0])1329return IsRsaPublicKey(str, int(len))1330}1331
1332return false1333}
1334
1335// StringMatches checks if a string matches a given pattern.
1336func StringMatches(s string, params ...string) bool {1337if len(params) == 1 {1338pattern := params[0]1339return Matches(s, pattern)1340}1341return false1342}
1343
1344// StringLength checks string's length (including multi byte strings)
1345func StringLength(str string, params ...string) bool {1346
1347if len(params) == 2 {1348strLength := utf8.RuneCountInString(str)1349min, _ := ToInt(params[0])1350max, _ := ToInt(params[1])1351return strLength >= int(min) && strLength <= int(max)1352}1353
1354return false1355}
1356
1357// MinStringLength checks string's minimum length (including multi byte strings)
1358func MinStringLength(str string, params ...string) bool {1359
1360if len(params) == 1 {1361strLength := utf8.RuneCountInString(str)1362min, _ := ToInt(params[0])1363return strLength >= int(min)1364}1365
1366return false1367}
1368
1369// MaxStringLength checks string's maximum length (including multi byte strings)
1370func MaxStringLength(str string, params ...string) bool {1371
1372if len(params) == 1 {1373strLength := utf8.RuneCountInString(str)1374max, _ := ToInt(params[0])1375return strLength <= int(max)1376}1377
1378return false1379}
1380
1381// Range checks string's length
1382func Range(str string, params ...string) bool {1383if len(params) == 2 {1384value, _ := ToFloat(str)1385min, _ := ToFloat(params[0])1386max, _ := ToFloat(params[1])1387return InRange(value, min, max)1388}1389
1390return false1391}
1392
1393// IsInRaw checks if string is in list of allowed values
1394func IsInRaw(str string, params ...string) bool {1395if len(params) == 1 {1396rawParams := params[0]1397
1398parsedParams := strings.Split(rawParams, "|")1399
1400return IsIn(str, parsedParams...)1401}1402
1403return false1404}
1405
1406// IsIn checks if string str is a member of the set of strings params
1407func IsIn(str string, params ...string) bool {1408for _, param := range params {1409if str == param {1410return true1411}1412}1413
1414return false1415}
1416
1417func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) {1418if nilPtrAllowedByRequired {1419k := v.Kind()1420if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() {1421return true, nil1422}1423}1424
1425if requiredOption, isRequired := options["required"]; isRequired {1426if len(requiredOption.customErrorMessage) > 0 {1427return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}}1428}1429return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}}1430} else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional {1431return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}}1432}1433// not required and empty is valid1434return true, nil1435}
1436
1437func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) {1438if !v.IsValid() {1439return false, nil1440}1441
1442tag := t.Tag.Get(tagName)1443
1444// checks if the field should be ignored1445switch tag {1446case "":1447if v.Kind() != reflect.Slice && v.Kind() != reflect.Map {1448if !fieldsRequiredByDefault {1449return true, nil1450}1451return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}}1452}1453case "-":1454return true, nil1455}1456
1457isRootType := false1458if options == nil {1459isRootType = true1460options = parseTagIntoMap(tag)1461}1462
1463if isEmptyValue(v) {1464// an empty value is not validated, checks only required1465isValid, resultErr = checkRequired(v, t, options)1466for key := range options {1467delete(options, key)1468}1469return isValid, resultErr1470}1471
1472var customTypeErrors Errors1473optionsOrder := options.orderedKeys()1474for _, validatorName := range optionsOrder {1475validatorStruct := options[validatorName]1476if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok {1477delete(options, validatorName)1478
1479if result := validatefunc(v.Interface(), o.Interface()); !result {1480if len(validatorStruct.customErrorMessage) > 0 {1481customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)})1482continue1483}1484customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)})1485}1486}1487}1488
1489if len(customTypeErrors.Errors()) > 0 {1490return false, customTypeErrors1491}1492
1493if isRootType {1494// Ensure that we've checked the value by all specified validators before report that the value is valid1495defer func() {1496delete(options, "optional")1497delete(options, "required")1498
1499if isValid && resultErr == nil && len(options) != 0 {1500optionsOrder := options.orderedKeys()1501for _, validator := range optionsOrder {1502isValid = false1503resultErr = Error{t.Name, fmt.Errorf(1504"The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}}1505return1506}1507}1508}()1509}1510
1511for _, validatorSpec := range optionsOrder {1512validatorStruct := options[validatorSpec]1513var negate bool1514validator := validatorSpec1515customMsgExists := len(validatorStruct.customErrorMessage) > 01516
1517// checks whether the tag looks like '!something' or 'something'1518if validator[0] == '!' {1519validator = validator[1:]1520negate = true1521}1522
1523// checks for interface param validators1524for key, value := range InterfaceParamTagRegexMap {1525ps := value.FindStringSubmatch(validator)1526if len(ps) == 0 {1527continue1528}1529
1530validatefunc, ok := InterfaceParamTagMap[key]1531if !ok {1532continue1533}1534
1535delete(options, validatorSpec)1536
1537field := fmt.Sprint(v)1538if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) {1539if customMsgExists {1540return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1541}1542if negate {1543return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1544}1545return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1546}1547}1548}1549
1550switch v.Kind() {1551case reflect.Bool,1552reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,1553reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,1554reflect.Float32, reflect.Float64,1555reflect.String:1556// for each tag option checks the map of validator functions1557for _, validatorSpec := range optionsOrder {1558validatorStruct := options[validatorSpec]1559var negate bool1560validator := validatorSpec1561customMsgExists := len(validatorStruct.customErrorMessage) > 01562
1563// checks whether the tag looks like '!something' or 'something'1564if validator[0] == '!' {1565validator = validator[1:]1566negate = true1567}1568
1569// checks for param validators1570for key, value := range ParamTagRegexMap {1571ps := value.FindStringSubmatch(validator)1572if len(ps) == 0 {1573continue1574}1575
1576validatefunc, ok := ParamTagMap[key]1577if !ok {1578continue1579}1580
1581delete(options, validatorSpec)1582
1583switch v.Kind() {1584case reflect.String,1585reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,1586reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,1587reflect.Float32, reflect.Float64:1588
1589field := fmt.Sprint(v) // make value into string, then validate with regex1590if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) {1591if customMsgExists {1592return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1593}1594if negate {1595return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1596}1597return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1598}1599default:1600// type not yet supported, fail1601return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}}1602}1603}1604
1605if validatefunc, ok := TagMap[validator]; ok {1606delete(options, validatorSpec)1607
1608switch v.Kind() {1609case reflect.String,1610reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,1611reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,1612reflect.Float32, reflect.Float64:1613field := fmt.Sprint(v) // make value into string, then validate with regex1614if result := validatefunc(field); !result && !negate || result && negate {1615if customMsgExists {1616return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1617}1618if negate {1619return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1620}1621return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}1622}1623default:1624//Not Yet Supported Types (Fail here!)1625err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v)1626return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}}1627}1628}1629}1630return true, nil1631case reflect.Map:1632if v.Type().Key().Kind() != reflect.String {1633return false, &UnsupportedTypeError{v.Type()}1634}1635var sv stringValues1636sv = v.MapKeys()1637sort.Sort(sv)1638result := true1639for i, k := range sv {1640var resultItem bool1641var err error1642if v.MapIndex(k).Kind() != reflect.Struct {1643resultItem, err = typeCheck(v.MapIndex(k), t, o, options)1644if err != nil {1645return false, err1646}1647} else {1648resultItem, err = ValidateStruct(v.MapIndex(k).Interface())1649if err != nil {1650err = prependPathToErrors(err, t.Name+"."+sv[i].Interface().(string))1651return false, err1652}1653}1654result = result && resultItem1655}1656return result, nil1657case reflect.Slice, reflect.Array:1658result := true1659for i := 0; i < v.Len(); i++ {1660var resultItem bool1661var err error1662if v.Index(i).Kind() != reflect.Struct {1663resultItem, err = typeCheck(v.Index(i), t, o, options)1664if err != nil {1665return false, err1666}1667} else {1668resultItem, err = ValidateStruct(v.Index(i).Interface())1669if err != nil {1670err = prependPathToErrors(err, t.Name+"."+strconv.Itoa(i))1671return false, err1672}1673}1674result = result && resultItem1675}1676return result, nil1677case reflect.Interface:1678// If the value is an interface then encode its element1679if v.IsNil() {1680return true, nil1681}1682return ValidateStruct(v.Interface())1683case reflect.Ptr:1684// If the value is a pointer then checks its element1685if v.IsNil() {1686return true, nil1687}1688return typeCheck(v.Elem(), t, o, options)1689case reflect.Struct:1690return true, nil1691default:1692return false, &UnsupportedTypeError{v.Type()}1693}1694}
1695
1696func stripParams(validatorString string) string {1697return paramsRegexp.ReplaceAllString(validatorString, "")1698}
1699
1700// isEmptyValue checks whether value empty or not
1701func isEmptyValue(v reflect.Value) bool {1702switch v.Kind() {1703case reflect.String, reflect.Array:1704return v.Len() == 01705case reflect.Map, reflect.Slice:1706return v.Len() == 0 || v.IsNil()1707case reflect.Bool:1708return !v.Bool()1709case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:1710return v.Int() == 01711case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:1712return v.Uint() == 01713case reflect.Float32, reflect.Float64:1714return v.Float() == 01715case reflect.Interface, reflect.Ptr:1716return v.IsNil()1717}1718
1719return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())1720}
1721
1722// ErrorByField returns error for specified field of the struct
1723// validated by ValidateStruct or empty string if there are no errors
1724// or this field doesn't exists or doesn't have any errors.
1725func ErrorByField(e error, field string) string {1726if e == nil {1727return ""1728}1729return ErrorsByField(e)[field]1730}
1731
1732// ErrorsByField returns map of errors of the struct validated
1733// by ValidateStruct or empty map if there are no errors.
1734func ErrorsByField(e error) map[string]string {1735m := make(map[string]string)1736if e == nil {1737return m1738}1739// prototype for ValidateStruct1740
1741switch e := e.(type) {1742case Error:1743m[e.Name] = e.Err.Error()1744case Errors:1745for _, item := range e.Errors() {1746n := ErrorsByField(item)1747for k, v := range n {1748m[k] = v1749}1750}1751}1752
1753return m1754}
1755
1756// Error returns string equivalent for reflect.Type
1757func (e *UnsupportedTypeError) Error() string {1758return "validator: unsupported type: " + e.Type.String()1759}
1760
1761func (sv stringValues) Len() int { return len(sv) }1762func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }1763func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }1764func (sv stringValues) get(i int) string { return sv[i].String() }1765
1766func IsE164(str string) bool {1767return rxE164.MatchString(str)1768}
1769