12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622 |
- // Package govalidator is package of validators and sanitizers for strings, structs and collections.
- package govalidator
- import (
- "bytes"
- "crypto/rsa"
- "crypto/x509"
- "encoding/base64"
- "encoding/json"
- "encoding/pem"
- "fmt"
- "io/ioutil"
- "net"
- "net/url"
- "reflect"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "time"
- "unicode"
- "unicode/utf8"
- )
- var (
- fieldsRequiredByDefault bool
- nilPtrAllowedByRequired = false
- notNumberRegexp = regexp.MustCompile("[^0-9]+")
- whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`)
- paramsRegexp = regexp.MustCompile(`\(.*\)$`)
- )
- const maxURLRuneCount = 2083
- const minURLRuneCount = 3
- const rfc3339WithoutZone = "2006-01-02T15:04:05"
- // SetFieldsRequiredByDefault causes validation to fail when struct fields
- // do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
- // This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
- // type exampleStruct struct {
- // Name string ``
- // Email string `valid:"email"`
- // This, however, will only fail when Email is empty or an invalid email address:
- // type exampleStruct2 struct {
- // Name string `valid:"-"`
- // Email string `valid:"email"`
- // Lastly, this will only fail when Email is an invalid email address but not when it's empty:
- // type exampleStruct2 struct {
- // Name string `valid:"-"`
- // Email string `valid:"email,optional"`
- func SetFieldsRequiredByDefault(value bool) {
- fieldsRequiredByDefault = value
- }
- // SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required.
- // The validation will still reject ptr fields in their zero value state. Example with this enabled:
- // type exampleStruct struct {
- // Name *string `valid:"required"`
- // With `Name` set to "", this will be considered invalid input and will cause a validation error.
- // With `Name` set to nil, this will be considered valid by validation.
- // By default this is disabled.
- func SetNilPtrAllowedByRequired(value bool) {
- nilPtrAllowedByRequired = value
- }
- // IsEmail checks if the string is an email.
- func IsEmail(str string) bool {
- // TODO uppercase letters are not supported
- return rxEmail.MatchString(str)
- }
- // IsExistingEmail checks if the string is an email of existing domain
- func IsExistingEmail(email string) bool {
- if len(email) < 6 || len(email) > 254 {
- return false
- }
- at := strings.LastIndex(email, "@")
- if at <= 0 || at > len(email)-3 {
- return false
- }
- user := email[:at]
- host := email[at+1:]
- if len(user) > 64 {
- return false
- }
- switch host {
- case "localhost", "example.com":
- return true
- }
- if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) {
- return false
- }
- if _, err := net.LookupMX(host); err != nil {
- if _, err := net.LookupIP(host); err != nil {
- return false
- }
- }
- return true
- }
- // IsURL checks if the string is an URL.
- func IsURL(str string) bool {
- if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") {
- return false
- }
- strTemp := str
- if strings.Contains(str, ":") && !strings.Contains(str, "://") {
- // support no indicated urlscheme but with colon for port number
- // http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
- strTemp = "http://" + str
- }
- u, err := url.Parse(strTemp)
- if err != nil {
- return false
- }
- if strings.HasPrefix(u.Host, ".") {
- return false
- }
- if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
- return false
- }
- return rxURL.MatchString(str)
- }
- // IsRequestURL checks if the string rawurl, assuming
- // it was received in an HTTP request, is a valid
- // URL confirm to RFC 3986
- func IsRequestURL(rawurl string) bool {
- url, err := url.ParseRequestURI(rawurl)
- if err != nil {
- return false //Couldn't even parse the rawurl
- }
- if len(url.Scheme) == 0 {
- return false //No Scheme found
- }
- return true
- }
- // IsRequestURI checks if the string rawurl, assuming
- // it was received in an HTTP request, is an
- // absolute URI or an absolute path.
- func IsRequestURI(rawurl string) bool {
- _, err := url.ParseRequestURI(rawurl)
- return err == nil
- }
- // IsAlpha checks if the string contains only letters (a-zA-Z). Empty string is valid.
- func IsAlpha(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxAlpha.MatchString(str)
- }
- //IsUTFLetter checks if the string contains only unicode letter characters.
- //Similar to IsAlpha but for all languages. Empty string is valid.
- func IsUTFLetter(str string) bool {
- if IsNull(str) {
- return true
- }
- for _, c := range str {
- if !unicode.IsLetter(c) {
- return false
- }
- }
- return true
- }
- // IsAlphanumeric checks if the string contains only letters and numbers. Empty string is valid.
- func IsAlphanumeric(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxAlphanumeric.MatchString(str)
- }
- // IsUTFLetterNumeric checks if the string contains only unicode letters and numbers. Empty string is valid.
- func IsUTFLetterNumeric(str string) bool {
- if IsNull(str) {
- return true
- }
- for _, c := range str {
- if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok
- return false
- }
- }
- return true
- }
- // IsNumeric checks if the string contains only numbers. Empty string is valid.
- func IsNumeric(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxNumeric.MatchString(str)
- }
- // IsUTFNumeric checks if the string contains only unicode numbers of any kind.
- // Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
- func IsUTFNumeric(str string) bool {
- if IsNull(str) {
- return true
- }
- if strings.IndexAny(str, "+-") > 0 {
- return false
- }
- if len(str) > 1 {
- str = strings.TrimPrefix(str, "-")
- str = strings.TrimPrefix(str, "+")
- }
- for _, c := range str {
- if !unicode.IsNumber(c) { //numbers && minus sign are ok
- return false
- }
- }
- return true
- }
- // IsUTFDigit checks if the string contains only unicode radix-10 decimal digits. Empty string is valid.
- func IsUTFDigit(str string) bool {
- if IsNull(str) {
- return true
- }
- if strings.IndexAny(str, "+-") > 0 {
- return false
- }
- if len(str) > 1 {
- str = strings.TrimPrefix(str, "-")
- str = strings.TrimPrefix(str, "+")
- }
- for _, c := range str {
- if !unicode.IsDigit(c) { //digits && minus sign are ok
- return false
- }
- }
- return true
- }
- // IsHexadecimal checks if the string is a hexadecimal number.
- func IsHexadecimal(str string) bool {
- return rxHexadecimal.MatchString(str)
- }
- // IsHexcolor checks if the string is a hexadecimal color.
- func IsHexcolor(str string) bool {
- return rxHexcolor.MatchString(str)
- }
- // IsRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
- func IsRGBcolor(str string) bool {
- return rxRGBcolor.MatchString(str)
- }
- // IsLowerCase checks if the string is lowercase. Empty string is valid.
- func IsLowerCase(str string) bool {
- if IsNull(str) {
- return true
- }
- return str == strings.ToLower(str)
- }
- // IsUpperCase checks if the string is uppercase. Empty string is valid.
- func IsUpperCase(str string) bool {
- if IsNull(str) {
- return true
- }
- return str == strings.ToUpper(str)
- }
- // HasLowerCase checks if the string contains at least 1 lowercase. Empty string is valid.
- func HasLowerCase(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxHasLowerCase.MatchString(str)
- }
- // HasUpperCase checks if the string contains as least 1 uppercase. Empty string is valid.
- func HasUpperCase(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxHasUpperCase.MatchString(str)
- }
- // IsInt checks if the string is an integer. Empty string is valid.
- func IsInt(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxInt.MatchString(str)
- }
- // IsFloat checks if the string is a float.
- func IsFloat(str string) bool {
- return str != "" && rxFloat.MatchString(str)
- }
- // IsDivisibleBy checks if the string is a number that's divisible by another.
- // If second argument is not valid integer or zero, it's return false.
- // Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
- func IsDivisibleBy(str, num string) bool {
- f, _ := ToFloat(str)
- p := int64(f)
- q, _ := ToInt(num)
- if q == 0 {
- return false
- }
- return (p == 0) || (p%q == 0)
- }
- // IsNull checks if the string is null.
- func IsNull(str string) bool {
- return len(str) == 0
- }
- // IsNotNull checks if the string is not null.
- func IsNotNull(str string) bool {
- return !IsNull(str)
- }
- // HasWhitespaceOnly checks the string only contains whitespace
- func HasWhitespaceOnly(str string) bool {
- return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str)
- }
- // HasWhitespace checks if the string contains any whitespace
- func HasWhitespace(str string) bool {
- return len(str) > 0 && rxHasWhitespace.MatchString(str)
- }
- // IsByteLength checks if the string's length (in bytes) falls in a range.
- func IsByteLength(str string, min, max int) bool {
- return len(str) >= min && len(str) <= max
- }
- // IsUUIDv3 checks if the string is a UUID version 3.
- func IsUUIDv3(str string) bool {
- return rxUUID3.MatchString(str)
- }
- // IsUUIDv4 checks if the string is a UUID version 4.
- func IsUUIDv4(str string) bool {
- return rxUUID4.MatchString(str)
- }
- // IsUUIDv5 checks if the string is a UUID version 5.
- func IsUUIDv5(str string) bool {
- return rxUUID5.MatchString(str)
- }
- // IsUUID checks if the string is a UUID (version 3, 4 or 5).
- func IsUUID(str string) bool {
- return rxUUID.MatchString(str)
- }
- // IsCreditCard checks if the string is a credit card.
- func IsCreditCard(str string) bool {
- sanitized := notNumberRegexp.ReplaceAllString(str, "")
- if !rxCreditCard.MatchString(sanitized) {
- return false
- }
- var sum int64
- var digit string
- var tmpNum int64
- var shouldDouble bool
- for i := len(sanitized) - 1; i >= 0; i-- {
- digit = sanitized[i:(i + 1)]
- tmpNum, _ = ToInt(digit)
- if shouldDouble {
- tmpNum *= 2
- if tmpNum >= 10 {
- sum += (tmpNum % 10) + 1
- } else {
- sum += tmpNum
- }
- } else {
- sum += tmpNum
- }
- shouldDouble = !shouldDouble
- }
- return sum%10 == 0
- }
- // IsISBN10 checks if the string is an ISBN version 10.
- func IsISBN10(str string) bool {
- return IsISBN(str, 10)
- }
- // IsISBN13 checks if the string is an ISBN version 13.
- func IsISBN13(str string) bool {
- return IsISBN(str, 13)
- }
- // IsISBN checks if the string is an ISBN (version 10 or 13).
- // If version value is not equal to 10 or 13, it will be checks both variants.
- func IsISBN(str string, version int) bool {
- sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")
- var checksum int32
- var i int32
- if version == 10 {
- if !rxISBN10.MatchString(sanitized) {
- return false
- }
- for i = 0; i < 9; i++ {
- checksum += (i + 1) * int32(sanitized[i]-'0')
- }
- if sanitized[9] == 'X' {
- checksum += 10 * 10
- } else {
- checksum += 10 * int32(sanitized[9]-'0')
- }
- if checksum%11 == 0 {
- return true
- }
- return false
- } else if version == 13 {
- if !rxISBN13.MatchString(sanitized) {
- return false
- }
- factor := []int32{1, 3}
- for i = 0; i < 12; i++ {
- checksum += factor[i%2] * int32(sanitized[i]-'0')
- }
- return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0
- }
- return IsISBN(str, 10) || IsISBN(str, 13)
- }
- // IsJSON checks if the string is valid JSON (note: uses json.Unmarshal).
- func IsJSON(str string) bool {
- var js json.RawMessage
- return json.Unmarshal([]byte(str), &js) == nil
- }
- // IsMultibyte checks if the string contains one or more multibyte chars. Empty string is valid.
- func IsMultibyte(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxMultibyte.MatchString(str)
- }
- // IsASCII checks if the string contains ASCII chars only. Empty string is valid.
- func IsASCII(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxASCII.MatchString(str)
- }
- // IsPrintableASCII checks if the string contains printable ASCII chars only. Empty string is valid.
- func IsPrintableASCII(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxPrintableASCII.MatchString(str)
- }
- // IsFullWidth checks if the string contains any full-width chars. Empty string is valid.
- func IsFullWidth(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxFullWidth.MatchString(str)
- }
- // IsHalfWidth checks if the string contains any half-width chars. Empty string is valid.
- func IsHalfWidth(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxHalfWidth.MatchString(str)
- }
- // IsVariableWidth checks if the string contains a mixture of full and half-width chars. Empty string is valid.
- func IsVariableWidth(str string) bool {
- if IsNull(str) {
- return true
- }
- return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str)
- }
- // IsBase64 checks if a string is base64 encoded.
- func IsBase64(str string) bool {
- return rxBase64.MatchString(str)
- }
- // IsFilePath checks is a string is Win or Unix file path and returns it's type.
- func IsFilePath(str string) (bool, int) {
- if rxWinPath.MatchString(str) {
- //check windows path limit see:
- // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
- if len(str[3:]) > 32767 {
- return false, Win
- }
- return true, Win
- } else if rxUnixPath.MatchString(str) {
- return true, Unix
- }
- return false, Unknown
- }
- // IsDataURI checks if a string is base64 encoded data URI such as an image
- func IsDataURI(str string) bool {
- dataURI := strings.Split(str, ",")
- if !rxDataURI.MatchString(dataURI[0]) {
- return false
- }
- return IsBase64(dataURI[1])
- }
- // IsMagnetURI checks if a string is valid magnet URI
- func IsMagnetURI(str string) bool {
- return rxMagnetURI.MatchString(str)
- }
- // IsISO3166Alpha2 checks if a string is valid two-letter country code
- func IsISO3166Alpha2(str string) bool {
- for _, entry := range ISO3166List {
- if str == entry.Alpha2Code {
- return true
- }
- }
- return false
- }
- // IsISO3166Alpha3 checks if a string is valid three-letter country code
- func IsISO3166Alpha3(str string) bool {
- for _, entry := range ISO3166List {
- if str == entry.Alpha3Code {
- return true
- }
- }
- return false
- }
- // IsISO693Alpha2 checks if a string is valid two-letter language code
- func IsISO693Alpha2(str string) bool {
- for _, entry := range ISO693List {
- if str == entry.Alpha2Code {
- return true
- }
- }
- return false
- }
- // IsISO693Alpha3b checks if a string is valid three-letter language code
- func IsISO693Alpha3b(str string) bool {
- for _, entry := range ISO693List {
- if str == entry.Alpha3bCode {
- return true
- }
- }
- return false
- }
- // IsDNSName will validate the given string as a DNS name
- func IsDNSName(str string) bool {
- if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 {
- // constraints already violated
- return false
- }
- return !IsIP(str) && rxDNSName.MatchString(str)
- }
- // IsHash checks if a string is a hash of type algorithm.
- // Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b']
- func IsHash(str string, algorithm string) bool {
- var len string
- algo := strings.ToLower(algorithm)
- if algo == "crc32" || algo == "crc32b" {
- len = "8"
- } else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" {
- len = "32"
- } else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" {
- len = "40"
- } else if algo == "tiger192" {
- len = "48"
- } else if algo == "sha256" {
- len = "64"
- } else if algo == "sha384" {
- len = "96"
- } else if algo == "sha512" {
- len = "128"
- } else {
- return false
- }
- return Matches(str, "^[a-f0-9]{"+len+"}$")
- }
- // IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")`
- func IsSHA512(str string) bool {
- return IsHash(str, "sha512")
- }
- // IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")`
- func IsSHA384(str string) bool {
- return IsHash(str, "sha384")
- }
- // IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")`
- func IsSHA256(str string) bool {
- return IsHash(str, "sha256")
- }
- // IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")`
- func IsTiger192(str string) bool {
- return IsHash(str, "tiger192")
- }
- // IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")`
- func IsTiger160(str string) bool {
- return IsHash(str, "tiger160")
- }
- // IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")`
- func IsRipeMD160(str string) bool {
- return IsHash(str, "ripemd160")
- }
- // IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")`
- func IsSHA1(str string) bool {
- return IsHash(str, "sha1")
- }
- // IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")`
- func IsTiger128(str string) bool {
- return IsHash(str, "tiger128")
- }
- // IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")`
- func IsRipeMD128(str string) bool {
- return IsHash(str, "ripemd128")
- }
- // IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")`
- func IsCRC32(str string) bool {
- return IsHash(str, "crc32")
- }
- // IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")`
- func IsCRC32b(str string) bool {
- return IsHash(str, "crc32b")
- }
- // IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")`
- func IsMD5(str string) bool {
- return IsHash(str, "md5")
- }
- // IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")`
- func IsMD4(str string) bool {
- return IsHash(str, "md4")
- }
- // IsDialString validates the given string for usage with the various Dial() functions
- func IsDialString(str string) bool {
- if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) {
- return true
- }
- return false
- }
- // IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP`
- func IsIP(str string) bool {
- return net.ParseIP(str) != nil
- }
- // IsPort checks if a string represents a valid port
- func IsPort(str string) bool {
- if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 {
- return true
- }
- return false
- }
- // IsIPv4 checks if the string is an IP version 4.
- func IsIPv4(str string) bool {
- ip := net.ParseIP(str)
- return ip != nil && strings.Contains(str, ".")
- }
- // IsIPv6 checks if the string is an IP version 6.
- func IsIPv6(str string) bool {
- ip := net.ParseIP(str)
- return ip != nil && strings.Contains(str, ":")
- }
- // IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6)
- func IsCIDR(str string) bool {
- _, _, err := net.ParseCIDR(str)
- return err == nil
- }
- // IsMAC checks if a string is valid MAC address.
- // Possible MAC formats:
- // 01:23:45:67:89:ab
- // 01:23:45:67:89:ab:cd:ef
- // 01-23-45-67-89-ab
- // 01-23-45-67-89-ab-cd-ef
- // 0123.4567.89ab
- // 0123.4567.89ab.cdef
- func IsMAC(str string) bool {
- _, err := net.ParseMAC(str)
- return err == nil
- }
- // IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name
- func IsHost(str string) bool {
- return IsIP(str) || IsDNSName(str)
- }
- // IsMongoID checks if the string is a valid hex-encoded representation of a MongoDB ObjectId.
- func IsMongoID(str string) bool {
- return rxHexadecimal.MatchString(str) && (len(str) == 24)
- }
- // IsLatitude checks if a string is valid latitude.
- func IsLatitude(str string) bool {
- return rxLatitude.MatchString(str)
- }
- // IsLongitude checks if a string is valid longitude.
- func IsLongitude(str string) bool {
- return rxLongitude.MatchString(str)
- }
- // IsIMEI checks if a string is valid IMEI
- func IsIMEI(str string) bool {
- return rxIMEI.MatchString(str)
- }
- // IsIMSI checks if a string is valid IMSI
- func IsIMSI(str string) bool {
- if !rxIMSI.MatchString(str) {
- return false
- }
- mcc, err := strconv.ParseInt(str[0:3], 10, 32)
- if err != nil {
- return false
- }
- switch mcc {
- case 202, 204, 206, 208, 212, 213, 214, 216, 218, 219:
- case 220, 221, 222, 226, 228, 230, 231, 232, 234, 235:
- case 238, 240, 242, 244, 246, 247, 248, 250, 255, 257:
- case 259, 260, 262, 266, 268, 270, 272, 274, 276, 278:
- case 280, 282, 283, 284, 286, 288, 289, 290, 292, 293:
- case 294, 295, 297, 302, 308, 310, 311, 312, 313, 314:
- case 315, 316, 330, 332, 334, 338, 340, 342, 344, 346:
- case 348, 350, 352, 354, 356, 358, 360, 362, 363, 364:
- case 365, 366, 368, 370, 372, 374, 376, 400, 401, 402:
- case 404, 405, 406, 410, 412, 413, 414, 415, 416, 417:
- case 418, 419, 420, 421, 422, 424, 425, 426, 427, 428:
- case 429, 430, 431, 432, 434, 436, 437, 438, 440, 441:
- case 450, 452, 454, 455, 456, 457, 460, 461, 466, 467:
- case 470, 472, 502, 505, 510, 514, 515, 520, 525, 528:
- case 530, 536, 537, 539, 540, 541, 542, 543, 544, 545:
- case 546, 547, 548, 549, 550, 551, 552, 553, 554, 555:
- case 602, 603, 604, 605, 606, 607, 608, 609, 610, 611:
- case 612, 613, 614, 615, 616, 617, 618, 619, 620, 621:
- case 622, 623, 624, 625, 626, 627, 628, 629, 630, 631:
- case 632, 633, 634, 635, 636, 637, 638, 639, 640, 641:
- case 642, 643, 645, 646, 647, 648, 649, 650, 651, 652:
- case 653, 654, 655, 657, 658, 659, 702, 704, 706, 708:
- case 710, 712, 714, 716, 722, 724, 730, 732, 734, 736:
- case 738, 740, 742, 744, 746, 748, 750, 995:
- return true
- default:
- return false
- }
- return true
- }
- // IsRsaPublicKey checks if a string is valid public key with provided length
- func IsRsaPublicKey(str string, keylen int) bool {
- bb := bytes.NewBufferString(str)
- pemBytes, err := ioutil.ReadAll(bb)
- if err != nil {
- return false
- }
- block, _ := pem.Decode(pemBytes)
- if block != nil && block.Type != "PUBLIC KEY" {
- return false
- }
- var der []byte
- if block != nil {
- der = block.Bytes
- } else {
- der, err = base64.StdEncoding.DecodeString(str)
- if err != nil {
- return false
- }
- }
- key, err := x509.ParsePKIXPublicKey(der)
- if err != nil {
- return false
- }
- pubkey, ok := key.(*rsa.PublicKey)
- if !ok {
- return false
- }
- bitlen := len(pubkey.N.Bytes()) * 8
- return bitlen == int(keylen)
- }
- func toJSONName(tag string) string {
- if tag == "" {
- return ""
- }
- // JSON name always comes first. If there's no options then split[0] is
- // JSON name, if JSON name is not set, then split[0] is an empty string.
- split := strings.SplitN(tag, ",", 2)
- name := split[0]
- // However it is possible that the field is skipped when
- // (de-)serializing from/to JSON, in which case assume that there is no
- // tag name to use
- if name == "-" {
- return ""
- }
- return name
- }
- func prependPathToErrors(err error, path string) error {
- switch err2 := err.(type) {
- case Error:
- err2.Path = append([]string{path}, err2.Path...)
- return err2
- case Errors:
- errors := err2.Errors()
- for i, err3 := range errors {
- errors[i] = prependPathToErrors(err3, path)
- }
- return err2
- }
- return err
- }
- // ValidateMap use validation map for fields.
- // result will be equal to `false` if there are any errors.
- // s is the map containing the data to be validated.
- // m is the validation map in the form:
- // map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}}
- func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) {
- if s == nil {
- return true, nil
- }
- result := true
- var err error
- var errs Errors
- var index int
- val := reflect.ValueOf(s)
- for key, value := range s {
- presentResult := true
- validator, ok := m[key]
- if !ok {
- presentResult = false
- var err error
- err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key)
- err = prependPathToErrors(err, key)
- errs = append(errs, err)
- }
- valueField := reflect.ValueOf(value)
- mapResult := true
- typeResult := true
- structResult := true
- resultField := true
- switch subValidator := validator.(type) {
- case map[string]interface{}:
- var err error
- if v, ok := value.(map[string]interface{}); !ok {
- mapResult = false
- err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String())
- err = prependPathToErrors(err, key)
- errs = append(errs, err)
- } else {
- mapResult, err = ValidateMap(v, subValidator)
- if err != nil {
- mapResult = false
- err = prependPathToErrors(err, key)
- errs = append(errs, err)
- }
- }
- case string:
- if (valueField.Kind() == reflect.Struct ||
- (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
- subValidator != "-" {
- var err error
- structResult, err = ValidateStruct(valueField.Interface())
- if err != nil {
- err = prependPathToErrors(err, key)
- errs = append(errs, err)
- }
- }
- resultField, err = typeCheck(valueField, reflect.StructField{
- Name: key,
- PkgPath: "",
- Type: val.Type(),
- Tag: reflect.StructTag(fmt.Sprintf("%s:%q", tagName, subValidator)),
- Offset: 0,
- Index: []int{index},
- Anonymous: false,
- }, val, nil)
- if err != nil {
- errs = append(errs, err)
- }
- case nil:
- // already handlerd when checked before
- default:
- typeResult = false
- err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String())
- err = prependPathToErrors(err, key)
- errs = append(errs, err)
- }
- result = result && presentResult && typeResult && resultField && structResult && mapResult
- index++
- }
- // checks required keys
- requiredResult := true
- for key, value := range m {
- if schema, ok := value.(string); ok {
- tags := parseTagIntoMap(schema)
- if required, ok := tags["required"]; ok {
- if _, ok := s[key]; !ok {
- requiredResult = false
- if required.customErrorMessage != "" {
- err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}}
- } else {
- err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}}
- }
- errs = append(errs, err)
- }
- }
- }
- }
- if len(errs) > 0 {
- err = errs
- }
- return result && requiredResult, err
- }
- // ValidateStruct use tags for fields.
- // result will be equal to `false` if there are any errors.
- // todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail)
- func ValidateStruct(s interface{}) (bool, error) {
- if s == nil {
- return true, nil
- }
- result := true
- var err error
- val := reflect.ValueOf(s)
- if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
- val = val.Elem()
- }
- // we only accept structs
- if val.Kind() != reflect.Struct {
- return false, fmt.Errorf("function only accepts structs; got %s", val.Kind())
- }
- var errs Errors
- for i := 0; i < val.NumField(); i++ {
- valueField := val.Field(i)
- typeField := val.Type().Field(i)
- if typeField.PkgPath != "" {
- continue // Private field
- }
- structResult := true
- if valueField.Kind() == reflect.Interface {
- valueField = valueField.Elem()
- }
- if (valueField.Kind() == reflect.Struct ||
- (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
- typeField.Tag.Get(tagName) != "-" {
- var err error
- structResult, err = ValidateStruct(valueField.Interface())
- if err != nil {
- err = prependPathToErrors(err, typeField.Name)
- errs = append(errs, err)
- }
- }
- resultField, err2 := typeCheck(valueField, typeField, val, nil)
- if err2 != nil {
- // Replace structure name with JSON name if there is a tag on the variable
- jsonTag := toJSONName(typeField.Tag.Get("json"))
- if jsonTag != "" {
- switch jsonError := err2.(type) {
- case Error:
- jsonError.Name = jsonTag
- err2 = jsonError
- case Errors:
- for i2, err3 := range jsonError {
- switch customErr := err3.(type) {
- case Error:
- customErr.Name = jsonTag
- jsonError[i2] = customErr
- }
- }
- err2 = jsonError
- }
- }
- errs = append(errs, err2)
- }
- result = result && resultField && structResult
- }
- if len(errs) > 0 {
- err = errs
- }
- return result, err
- }
- // ValidateStructAsync performs async validation of the struct and returns results through the channels
- func ValidateStructAsync(s interface{}) (<-chan bool, <-chan error) {
- res := make(chan bool)
- errors := make(chan error)
- go func() {
- defer close(res)
- defer close(errors)
- isValid, isFailed := ValidateStruct(s)
- res <- isValid
- errors <- isFailed
- }()
- return res, errors
- }
- // ValidateMapAsync performs async validation of the map and returns results through the channels
- func ValidateMapAsync(s map[string]interface{}, m map[string]interface{}) (<-chan bool, <-chan error) {
- res := make(chan bool)
- errors := make(chan error)
- go func() {
- defer close(res)
- defer close(errors)
- isValid, isFailed := ValidateMap(s, m)
- res <- isValid
- errors <- isFailed
- }()
- return res, errors
- }
- // parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
- func parseTagIntoMap(tag string) tagOptionsMap {
- optionsMap := make(tagOptionsMap)
- options := strings.Split(tag, ",")
- for i, option := range options {
- option = strings.TrimSpace(option)
- validationOptions := strings.Split(option, "~")
- if !isValidTag(validationOptions[0]) {
- continue
- }
- if len(validationOptions) == 2 {
- optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i}
- } else {
- optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i}
- }
- }
- return optionsMap
- }
- func isValidTag(s string) bool {
- if s == "" {
- return false
- }
- for _, c := range s {
- switch {
- case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
- // Backslash and quote chars are reserved, but
- // otherwise any punctuation chars are allowed
- // in a tag name.
- default:
- if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
- return false
- }
- }
- }
- return true
- }
- // IsSSN will validate the given string as a U.S. Social Security Number
- func IsSSN(str string) bool {
- if str == "" || len(str) != 11 {
- return false
- }
- return rxSSN.MatchString(str)
- }
- // IsSemver checks if string is valid semantic version
- func IsSemver(str string) bool {
- return rxSemver.MatchString(str)
- }
- // IsType checks if interface is of some type
- func IsType(v interface{}, params ...string) bool {
- if len(params) == 1 {
- typ := params[0]
- return strings.Replace(reflect.TypeOf(v).String(), " ", "", -1) == strings.Replace(typ, " ", "", -1)
- }
- return false
- }
- // IsTime checks if string is valid according to given format
- func IsTime(str string, format string) bool {
- _, err := time.Parse(format, str)
- return err == nil
- }
- // IsUnixTime checks if string is valid unix timestamp value
- func IsUnixTime(str string) bool {
- if _, err := strconv.Atoi(str); err == nil {
- return true
- }
- return false
- }
- // IsRFC3339 checks if string is valid timestamp value according to RFC3339
- func IsRFC3339(str string) bool {
- return IsTime(str, time.RFC3339)
- }
- // IsRFC3339WithoutZone checks if string is valid timestamp value according to RFC3339 which excludes the timezone.
- func IsRFC3339WithoutZone(str string) bool {
- return IsTime(str, rfc3339WithoutZone)
- }
- // IsISO4217 checks if string is valid ISO currency code
- func IsISO4217(str string) bool {
- for _, currency := range ISO4217List {
- if str == currency {
- return true
- }
- }
- return false
- }
- // ByteLength checks string's length
- func ByteLength(str string, params ...string) bool {
- if len(params) == 2 {
- min, _ := ToInt(params[0])
- max, _ := ToInt(params[1])
- return len(str) >= int(min) && len(str) <= int(max)
- }
- return false
- }
- // RuneLength checks string's length
- // Alias for StringLength
- func RuneLength(str string, params ...string) bool {
- return StringLength(str, params...)
- }
- // IsRsaPub checks whether string is valid RSA key
- // Alias for IsRsaPublicKey
- func IsRsaPub(str string, params ...string) bool {
- if len(params) == 1 {
- len, _ := ToInt(params[0])
- return IsRsaPublicKey(str, int(len))
- }
- return false
- }
- // StringMatches checks if a string matches a given pattern.
- func StringMatches(s string, params ...string) bool {
- if len(params) == 1 {
- pattern := params[0]
- return Matches(s, pattern)
- }
- return false
- }
- // StringLength checks string's length (including multi byte strings)
- func StringLength(str string, params ...string) bool {
- if len(params) == 2 {
- strLength := utf8.RuneCountInString(str)
- min, _ := ToInt(params[0])
- max, _ := ToInt(params[1])
- return strLength >= int(min) && strLength <= int(max)
- }
- return false
- }
- // MinStringLength checks string's minimum length (including multi byte strings)
- func MinStringLength(str string, params ...string) bool {
- if len(params) == 1 {
- strLength := utf8.RuneCountInString(str)
- min, _ := ToInt(params[0])
- return strLength >= int(min)
- }
- return false
- }
- // MaxStringLength checks string's maximum length (including multi byte strings)
- func MaxStringLength(str string, params ...string) bool {
- if len(params) == 1 {
- strLength := utf8.RuneCountInString(str)
- max, _ := ToInt(params[0])
- return strLength <= int(max)
- }
- return false
- }
- // Range checks string's length
- func Range(str string, params ...string) bool {
- if len(params) == 2 {
- value, _ := ToFloat(str)
- min, _ := ToFloat(params[0])
- max, _ := ToFloat(params[1])
- return InRange(value, min, max)
- }
- return false
- }
- // IsInRaw checks if string is in list of allowed values
- func IsInRaw(str string, params ...string) bool {
- if len(params) == 1 {
- rawParams := params[0]
- parsedParams := strings.Split(rawParams, "|")
- return IsIn(str, parsedParams...)
- }
- return false
- }
- // IsIn checks if string str is a member of the set of strings params
- func IsIn(str string, params ...string) bool {
- for _, param := range params {
- if str == param {
- return true
- }
- }
- return false
- }
- func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) {
- if nilPtrAllowedByRequired {
- k := v.Kind()
- if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() {
- return true, nil
- }
- }
- if requiredOption, isRequired := options["required"]; isRequired {
- if len(requiredOption.customErrorMessage) > 0 {
- return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}}
- }
- return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}}
- } else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional {
- return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}}
- }
- // not required and empty is valid
- return true, nil
- }
- func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) {
- if !v.IsValid() {
- return false, nil
- }
- tag := t.Tag.Get(tagName)
- // checks if the field should be ignored
- switch tag {
- case "":
- if v.Kind() != reflect.Slice && v.Kind() != reflect.Map {
- if !fieldsRequiredByDefault {
- return true, nil
- }
- return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}}
- }
- case "-":
- return true, nil
- }
- isRootType := false
- if options == nil {
- isRootType = true
- options = parseTagIntoMap(tag)
- }
- if isEmptyValue(v) {
- // an empty value is not validated, checks only required
- isValid, resultErr = checkRequired(v, t, options)
- for key := range options {
- delete(options, key)
- }
- return isValid, resultErr
- }
- var customTypeErrors Errors
- optionsOrder := options.orderedKeys()
- for _, validatorName := range optionsOrder {
- validatorStruct := options[validatorName]
- if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok {
- delete(options, validatorName)
- if result := validatefunc(v.Interface(), o.Interface()); !result {
- if len(validatorStruct.customErrorMessage) > 0 {
- customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)})
- continue
- }
- customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)})
- }
- }
- }
- if len(customTypeErrors.Errors()) > 0 {
- return false, customTypeErrors
- }
- if isRootType {
- // Ensure that we've checked the value by all specified validators before report that the value is valid
- defer func() {
- delete(options, "optional")
- delete(options, "required")
- if isValid && resultErr == nil && len(options) != 0 {
- optionsOrder := options.orderedKeys()
- for _, validator := range optionsOrder {
- isValid = false
- resultErr = Error{t.Name, fmt.Errorf(
- "The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}}
- return
- }
- }
- }()
- }
- for _, validatorSpec := range optionsOrder {
- validatorStruct := options[validatorSpec]
- var negate bool
- validator := validatorSpec
- customMsgExists := len(validatorStruct.customErrorMessage) > 0
- // checks whether the tag looks like '!something' or 'something'
- if validator[0] == '!' {
- validator = validator[1:]
- negate = true
- }
- // checks for interface param validators
- for key, value := range InterfaceParamTagRegexMap {
- ps := value.FindStringSubmatch(validator)
- if len(ps) == 0 {
- continue
- }
- validatefunc, ok := InterfaceParamTagMap[key]
- if !ok {
- continue
- }
- delete(options, validatorSpec)
- field := fmt.Sprint(v)
- if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) {
- if customMsgExists {
- return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- if negate {
- return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- }
- }
- switch v.Kind() {
- case reflect.Bool,
- 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,
- reflect.String:
- // for each tag option checks the map of validator functions
- for _, validatorSpec := range optionsOrder {
- validatorStruct := options[validatorSpec]
- var negate bool
- validator := validatorSpec
- customMsgExists := len(validatorStruct.customErrorMessage) > 0
- // checks whether the tag looks like '!something' or 'something'
- if validator[0] == '!' {
- validator = validator[1:]
- negate = true
- }
- // checks for param validators
- for key, value := range ParamTagRegexMap {
- ps := value.FindStringSubmatch(validator)
- if len(ps) == 0 {
- continue
- }
- validatefunc, ok := ParamTagMap[key]
- if !ok {
- continue
- }
- delete(options, validatorSpec)
- switch v.Kind() {
- case reflect.String,
- reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
- reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
- reflect.Float32, reflect.Float64:
- field := fmt.Sprint(v) // make value into string, then validate with regex
- if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) {
- if customMsgExists {
- return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- if negate {
- return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- default:
- // type not yet supported, fail
- return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}}
- }
- }
- if validatefunc, ok := TagMap[validator]; ok {
- delete(options, validatorSpec)
- switch v.Kind() {
- case reflect.String,
- reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
- reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
- reflect.Float32, reflect.Float64:
- field := fmt.Sprint(v) // make value into string, then validate with regex
- if result := validatefunc(field); !result && !negate || result && negate {
- if customMsgExists {
- return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- if negate {
- return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
- }
- default:
- //Not Yet Supported Types (Fail here!)
- err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v)
- return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}}
- }
- }
- }
- return true, nil
- case reflect.Map:
- if v.Type().Key().Kind() != reflect.String {
- return false, &UnsupportedTypeError{v.Type()}
- }
- var sv stringValues
- sv = v.MapKeys()
- sort.Sort(sv)
- result := true
- for i, k := range sv {
- var resultItem bool
- var err error
- if v.MapIndex(k).Kind() != reflect.Struct {
- resultItem, err = typeCheck(v.MapIndex(k), t, o, options)
- if err != nil {
- return false, err
- }
- } else {
- resultItem, err = ValidateStruct(v.MapIndex(k).Interface())
- if err != nil {
- err = prependPathToErrors(err, t.Name+"."+sv[i].Interface().(string))
- return false, err
- }
- }
- result = result && resultItem
- }
- return result, nil
- case reflect.Slice, reflect.Array:
- result := true
- for i := 0; i < v.Len(); i++ {
- var resultItem bool
- var err error
- if v.Index(i).Kind() != reflect.Struct {
- resultItem, err = typeCheck(v.Index(i), t, o, options)
- if err != nil {
- return false, err
- }
- } else {
- resultItem, err = ValidateStruct(v.Index(i).Interface())
- if err != nil {
- err = prependPathToErrors(err, t.Name+"."+strconv.Itoa(i))
- return false, err
- }
- }
- result = result && resultItem
- }
- return result, nil
- case reflect.Interface:
- // If the value is an interface then encode its element
- if v.IsNil() {
- return true, nil
- }
- return ValidateStruct(v.Interface())
- case reflect.Ptr:
- // If the value is a pointer then checks its element
- if v.IsNil() {
- return true, nil
- }
- return typeCheck(v.Elem(), t, o, options)
- case reflect.Struct:
- return true, nil
- default:
- return false, &UnsupportedTypeError{v.Type()}
- }
- }
- func stripParams(validatorString string) string {
- return paramsRegexp.ReplaceAllString(validatorString, "")
- }
- // isEmptyValue checks whether value empty or not
- func isEmptyValue(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.String, reflect.Array:
- return v.Len() == 0
- case reflect.Map, reflect.Slice:
- return v.Len() == 0 || v.IsNil()
- case reflect.Bool:
- return !v.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Interface, reflect.Ptr:
- return v.IsNil()
- }
- return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
- }
- // ErrorByField returns error for specified field of the struct
- // validated by ValidateStruct or empty string if there are no errors
- // or this field doesn't exists or doesn't have any errors.
- func ErrorByField(e error, field string) string {
- if e == nil {
- return ""
- }
- return ErrorsByField(e)[field]
- }
- // ErrorsByField returns map of errors of the struct validated
- // by ValidateStruct or empty map if there are no errors.
- func ErrorsByField(e error) map[string]string {
- m := make(map[string]string)
- if e == nil {
- return m
- }
- // prototype for ValidateStruct
- switch e := e.(type) {
- case Error:
- m[e.Name] = e.Err.Error()
- case Errors:
- for _, item := range e.Errors() {
- n := ErrorsByField(item)
- for k, v := range n {
- m[k] = v
- }
- }
- }
- return m
- }
- // Error returns string equivalent for reflect.Type
- func (e *UnsupportedTypeError) Error() string {
- return "validator: unsupported type: " + e.Type.String()
- }
- func (sv stringValues) Len() int { return len(sv) }
- func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
- func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
- func (sv stringValues) get(i int) string { return sv[i].String() }
|