12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999 |
- package validator
- import (
- "bytes"
- "context"
- "crypto/sha256"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io/fs"
- "net"
- "net/url"
- "os"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "time"
- "unicode/utf8"
- "golang.org/x/crypto/sha3"
- "golang.org/x/text/language"
- "github.com/gabriel-vasile/mimetype"
- urn "github.com/leodido/go-urn"
- )
- // Func accepts a FieldLevel interface for all validation needs. The return
- // value should be true when validation succeeds.
- type Func func(fl FieldLevel) bool
- // FuncCtx accepts a context.Context and FieldLevel interface for all
- // validation needs. The return value should be true when validation succeeds.
- type FuncCtx func(ctx context.Context, fl FieldLevel) bool
- // wrapFunc wraps normal Func makes it compatible with FuncCtx
- func wrapFunc(fn Func) FuncCtx {
- if fn == nil {
- return nil // be sure not to wrap a bad function.
- }
- return func(ctx context.Context, fl FieldLevel) bool {
- return fn(fl)
- }
- }
- var (
- restrictedTags = map[string]struct{}{
- diveTag: {},
- keysTag: {},
- endKeysTag: {},
- structOnlyTag: {},
- omitempty: {},
- omitnil: {},
- skipValidationTag: {},
- utf8HexComma: {},
- utf8Pipe: {},
- noStructLevelTag: {},
- requiredTag: {},
- isdefault: {},
- }
- // bakedInAliases is a default mapping of a single validation tag that
- // defines a common or complex set of validation(s) to simplify
- // adding validation to structs.
- bakedInAliases = map[string]string{
- "iscolor": "hexcolor|rgb|rgba|hsl|hsla",
- "country_code": "iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric",
- "eu_country_code": "iso3166_1_alpha2_eu|iso3166_1_alpha3_eu|iso3166_1_alpha_numeric_eu",
- }
- // bakedInValidators is the default map of ValidationFunc
- // you can add, remove or even replace items to suite your needs,
- // or even disregard and use your own map if so desired.
- bakedInValidators = map[string]Func{
- "required": hasValue,
- "required_if": requiredIf,
- "required_unless": requiredUnless,
- "skip_unless": skipUnless,
- "required_with": requiredWith,
- "required_with_all": requiredWithAll,
- "required_without": requiredWithout,
- "required_without_all": requiredWithoutAll,
- "excluded_if": excludedIf,
- "excluded_unless": excludedUnless,
- "excluded_with": excludedWith,
- "excluded_with_all": excludedWithAll,
- "excluded_without": excludedWithout,
- "excluded_without_all": excludedWithoutAll,
- "isdefault": isDefault,
- "len": hasLengthOf,
- "min": hasMinOf,
- "max": hasMaxOf,
- "eq": isEq,
- "eq_ignore_case": isEqIgnoreCase,
- "ne": isNe,
- "ne_ignore_case": isNeIgnoreCase,
- "lt": isLt,
- "lte": isLte,
- "gt": isGt,
- "gte": isGte,
- "eqfield": isEqField,
- "eqcsfield": isEqCrossStructField,
- "necsfield": isNeCrossStructField,
- "gtcsfield": isGtCrossStructField,
- "gtecsfield": isGteCrossStructField,
- "ltcsfield": isLtCrossStructField,
- "ltecsfield": isLteCrossStructField,
- "nefield": isNeField,
- "gtefield": isGteField,
- "gtfield": isGtField,
- "ltefield": isLteField,
- "ltfield": isLtField,
- "fieldcontains": fieldContains,
- "fieldexcludes": fieldExcludes,
- "alpha": isAlpha,
- "alphanum": isAlphanum,
- "alphaunicode": isAlphaUnicode,
- "alphanumunicode": isAlphanumUnicode,
- "boolean": isBoolean,
- "numeric": isNumeric,
- "number": isNumber,
- "hexadecimal": isHexadecimal,
- "hexcolor": isHEXColor,
- "rgb": isRGB,
- "rgba": isRGBA,
- "hsl": isHSL,
- "hsla": isHSLA,
- "e164": isE164,
- "email": isEmail,
- "url": isURL,
- "http_url": isHttpURL,
- "uri": isURI,
- "urn_rfc2141": isUrnRFC2141, // RFC 2141
- "file": isFile,
- "filepath": isFilePath,
- "base32": isBase32,
- "base64": isBase64,
- "base64url": isBase64URL,
- "base64rawurl": isBase64RawURL,
- "contains": contains,
- "containsany": containsAny,
- "containsrune": containsRune,
- "excludes": excludes,
- "excludesall": excludesAll,
- "excludesrune": excludesRune,
- "startswith": startsWith,
- "endswith": endsWith,
- "startsnotwith": startsNotWith,
- "endsnotwith": endsNotWith,
- "image": isImage,
- "isbn": isISBN,
- "isbn10": isISBN10,
- "isbn13": isISBN13,
- "issn": isISSN,
- "eth_addr": isEthereumAddress,
- "eth_addr_checksum": isEthereumAddressChecksum,
- "btc_addr": isBitcoinAddress,
- "btc_addr_bech32": isBitcoinBech32Address,
- "uuid": isUUID,
- "uuid3": isUUID3,
- "uuid4": isUUID4,
- "uuid5": isUUID5,
- "uuid_rfc4122": isUUIDRFC4122,
- "uuid3_rfc4122": isUUID3RFC4122,
- "uuid4_rfc4122": isUUID4RFC4122,
- "uuid5_rfc4122": isUUID5RFC4122,
- "ulid": isULID,
- "md4": isMD4,
- "md5": isMD5,
- "sha256": isSHA256,
- "sha384": isSHA384,
- "sha512": isSHA512,
- "ripemd128": isRIPEMD128,
- "ripemd160": isRIPEMD160,
- "tiger128": isTIGER128,
- "tiger160": isTIGER160,
- "tiger192": isTIGER192,
- "ascii": isASCII,
- "printascii": isPrintableASCII,
- "multibyte": hasMultiByteCharacter,
- "datauri": isDataURI,
- "latitude": isLatitude,
- "longitude": isLongitude,
- "ssn": isSSN,
- "ipv4": isIPv4,
- "ipv6": isIPv6,
- "ip": isIP,
- "cidrv4": isCIDRv4,
- "cidrv6": isCIDRv6,
- "cidr": isCIDR,
- "tcp4_addr": isTCP4AddrResolvable,
- "tcp6_addr": isTCP6AddrResolvable,
- "tcp_addr": isTCPAddrResolvable,
- "udp4_addr": isUDP4AddrResolvable,
- "udp6_addr": isUDP6AddrResolvable,
- "udp_addr": isUDPAddrResolvable,
- "ip4_addr": isIP4AddrResolvable,
- "ip6_addr": isIP6AddrResolvable,
- "ip_addr": isIPAddrResolvable,
- "unix_addr": isUnixAddrResolvable,
- "mac": isMAC,
- "hostname": isHostnameRFC952, // RFC 952
- "hostname_rfc1123": isHostnameRFC1123, // RFC 1123
- "fqdn": isFQDN,
- "unique": isUnique,
- "oneof": isOneOf,
- "html": isHTML,
- "html_encoded": isHTMLEncoded,
- "url_encoded": isURLEncoded,
- "dir": isDir,
- "dirpath": isDirPath,
- "json": isJSON,
- "jwt": isJWT,
- "hostname_port": isHostnamePort,
- "lowercase": isLowercase,
- "uppercase": isUppercase,
- "datetime": isDatetime,
- "timezone": isTimeZone,
- "iso3166_1_alpha2": isIso3166Alpha2,
- "iso3166_1_alpha2_eu": isIso3166Alpha2EU,
- "iso3166_1_alpha3": isIso3166Alpha3,
- "iso3166_1_alpha3_eu": isIso3166Alpha3EU,
- "iso3166_1_alpha_numeric": isIso3166AlphaNumeric,
- "iso3166_1_alpha_numeric_eu": isIso3166AlphaNumericEU,
- "iso3166_2": isIso31662,
- "iso4217": isIso4217,
- "iso4217_numeric": isIso4217Numeric,
- "bcp47_language_tag": isBCP47LanguageTag,
- "postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
- "postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
- "bic": isIsoBicFormat,
- "semver": isSemverFormat,
- "dns_rfc1035_label": isDnsRFC1035LabelFormat,
- "credit_card": isCreditCard,
- "cve": isCveFormat,
- "luhn_checksum": hasLuhnChecksum,
- "mongodb": isMongoDB,
- "cron": isCron,
- "spicedb": isSpiceDB,
- }
- )
- var (
- oneofValsCache = map[string][]string{}
- oneofValsCacheRWLock = sync.RWMutex{}
- )
- func parseOneOfParam2(s string) []string {
- oneofValsCacheRWLock.RLock()
- vals, ok := oneofValsCache[s]
- oneofValsCacheRWLock.RUnlock()
- if !ok {
- oneofValsCacheRWLock.Lock()
- vals = splitParamsRegex.FindAllString(s, -1)
- for i := 0; i < len(vals); i++ {
- vals[i] = strings.Replace(vals[i], "'", "", -1)
- }
- oneofValsCache[s] = vals
- oneofValsCacheRWLock.Unlock()
- }
- return vals
- }
- func isURLEncoded(fl FieldLevel) bool {
- return uRLEncodedRegex.MatchString(fl.Field().String())
- }
- func isHTMLEncoded(fl FieldLevel) bool {
- return hTMLEncodedRegex.MatchString(fl.Field().String())
- }
- func isHTML(fl FieldLevel) bool {
- return hTMLRegex.MatchString(fl.Field().String())
- }
- func isOneOf(fl FieldLevel) bool {
- vals := parseOneOfParam2(fl.Param())
- field := fl.Field()
- var v string
- switch field.Kind() {
- case reflect.String:
- v = field.String()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- v = strconv.FormatInt(field.Int(), 10)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- v = strconv.FormatUint(field.Uint(), 10)
- default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- for i := 0; i < len(vals); i++ {
- if vals[i] == v {
- return true
- }
- }
- return false
- }
- // isUnique is the validation function for validating if each array|slice|map value is unique
- func isUnique(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- v := reflect.ValueOf(struct{}{})
- switch field.Kind() {
- case reflect.Slice, reflect.Array:
- elem := field.Type().Elem()
- if elem.Kind() == reflect.Ptr {
- elem = elem.Elem()
- }
- if param == "" {
- m := reflect.MakeMap(reflect.MapOf(elem, v.Type()))
- for i := 0; i < field.Len(); i++ {
- m.SetMapIndex(reflect.Indirect(field.Index(i)), v)
- }
- return field.Len() == m.Len()
- }
- sf, ok := elem.FieldByName(param)
- if !ok {
- panic(fmt.Sprintf("Bad field name %s", param))
- }
- sfTyp := sf.Type
- if sfTyp.Kind() == reflect.Ptr {
- sfTyp = sfTyp.Elem()
- }
- m := reflect.MakeMap(reflect.MapOf(sfTyp, v.Type()))
- var fieldlen int
- for i := 0; i < field.Len(); i++ {
- key := reflect.Indirect(reflect.Indirect(field.Index(i)).FieldByName(param))
- if key.IsValid() {
- fieldlen++
- m.SetMapIndex(key, v)
- }
- }
- return fieldlen == m.Len()
- case reflect.Map:
- var m reflect.Value
- if field.Type().Elem().Kind() == reflect.Ptr {
- m = reflect.MakeMap(reflect.MapOf(field.Type().Elem().Elem(), v.Type()))
- } else {
- m = reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
- }
- for _, k := range field.MapKeys() {
- m.SetMapIndex(reflect.Indirect(field.MapIndex(k)), v)
- }
- return field.Len() == m.Len()
- default:
- if parent := fl.Parent(); parent.Kind() == reflect.Struct {
- uniqueField := parent.FieldByName(param)
- if uniqueField == reflect.ValueOf(nil) {
- panic(fmt.Sprintf("Bad field name provided %s", param))
- }
- if uniqueField.Kind() != field.Kind() {
- panic(fmt.Sprintf("Bad field type %T:%T", field.Interface(), uniqueField.Interface()))
- }
- return field.Interface() != uniqueField.Interface()
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- }
- // isMAC is the validation function for validating if the field's value is a valid MAC address.
- func isMAC(fl FieldLevel) bool {
- _, err := net.ParseMAC(fl.Field().String())
- return err == nil
- }
- // isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
- func isCIDRv4(fl FieldLevel) bool {
- ip, net, err := net.ParseCIDR(fl.Field().String())
- return err == nil && ip.To4() != nil && net.IP.Equal(ip)
- }
- // isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
- func isCIDRv6(fl FieldLevel) bool {
- ip, _, err := net.ParseCIDR(fl.Field().String())
- return err == nil && ip.To4() == nil
- }
- // isCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address.
- func isCIDR(fl FieldLevel) bool {
- _, _, err := net.ParseCIDR(fl.Field().String())
- return err == nil
- }
- // isIPv4 is the validation function for validating if a value is a valid v4 IP address.
- func isIPv4(fl FieldLevel) bool {
- ip := net.ParseIP(fl.Field().String())
- return ip != nil && ip.To4() != nil
- }
- // isIPv6 is the validation function for validating if the field's value is a valid v6 IP address.
- func isIPv6(fl FieldLevel) bool {
- ip := net.ParseIP(fl.Field().String())
- return ip != nil && ip.To4() == nil
- }
- // isIP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
- func isIP(fl FieldLevel) bool {
- ip := net.ParseIP(fl.Field().String())
- return ip != nil
- }
- // isSSN is the validation function for validating if the field's value is a valid SSN.
- func isSSN(fl FieldLevel) bool {
- field := fl.Field()
- if field.Len() != 11 {
- return false
- }
- return sSNRegex.MatchString(field.String())
- }
- // isLongitude is the validation function for validating if the field's value is a valid longitude coordinate.
- func isLongitude(fl FieldLevel) bool {
- field := fl.Field()
- var v string
- switch field.Kind() {
- case reflect.String:
- v = field.String()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- v = strconv.FormatInt(field.Int(), 10)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- v = strconv.FormatUint(field.Uint(), 10)
- case reflect.Float32:
- v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
- case reflect.Float64:
- v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
- default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- return longitudeRegex.MatchString(v)
- }
- // isLatitude is the validation function for validating if the field's value is a valid latitude coordinate.
- func isLatitude(fl FieldLevel) bool {
- field := fl.Field()
- var v string
- switch field.Kind() {
- case reflect.String:
- v = field.String()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- v = strconv.FormatInt(field.Int(), 10)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- v = strconv.FormatUint(field.Uint(), 10)
- case reflect.Float32:
- v = strconv.FormatFloat(field.Float(), 'f', -1, 32)
- case reflect.Float64:
- v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
- default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- return latitudeRegex.MatchString(v)
- }
- // isDataURI is the validation function for validating if the field's value is a valid data URI.
- func isDataURI(fl FieldLevel) bool {
- uri := strings.SplitN(fl.Field().String(), ",", 2)
- if len(uri) != 2 {
- return false
- }
- if !dataURIRegex.MatchString(uri[0]) {
- return false
- }
- return base64Regex.MatchString(uri[1])
- }
- // hasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character.
- func hasMultiByteCharacter(fl FieldLevel) bool {
- field := fl.Field()
- if field.Len() == 0 {
- return true
- }
- return multibyteRegex.MatchString(field.String())
- }
- // isPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character.
- func isPrintableASCII(fl FieldLevel) bool {
- return printableASCIIRegex.MatchString(fl.Field().String())
- }
- // isASCII is the validation function for validating if the field's value is a valid ASCII character.
- func isASCII(fl FieldLevel) bool {
- return aSCIIRegex.MatchString(fl.Field().String())
- }
- // isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
- func isUUID5(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUID5Regex, fl)
- }
- // isUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
- func isUUID4(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUID4Regex, fl)
- }
- // isUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
- func isUUID3(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUID3Regex, fl)
- }
- // isUUID is the validation function for validating if the field's value is a valid UUID of any version.
- func isUUID(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUIDRegex, fl)
- }
- // isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
- func isUUID5RFC4122(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUID5RFC4122Regex, fl)
- }
- // isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
- func isUUID4RFC4122(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUID4RFC4122Regex, fl)
- }
- // isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
- func isUUID3RFC4122(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUID3RFC4122Regex, fl)
- }
- // isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
- func isUUIDRFC4122(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uUIDRFC4122Regex, fl)
- }
- // isULID is the validation function for validating if the field's value is a valid ULID.
- func isULID(fl FieldLevel) bool {
- return fieldMatchesRegexByStringerValOrString(uLIDRegex, fl)
- }
- // isMD4 is the validation function for validating if the field's value is a valid MD4.
- func isMD4(fl FieldLevel) bool {
- return md4Regex.MatchString(fl.Field().String())
- }
- // isMD5 is the validation function for validating if the field's value is a valid MD5.
- func isMD5(fl FieldLevel) bool {
- return md5Regex.MatchString(fl.Field().String())
- }
- // isSHA256 is the validation function for validating if the field's value is a valid SHA256.
- func isSHA256(fl FieldLevel) bool {
- return sha256Regex.MatchString(fl.Field().String())
- }
- // isSHA384 is the validation function for validating if the field's value is a valid SHA384.
- func isSHA384(fl FieldLevel) bool {
- return sha384Regex.MatchString(fl.Field().String())
- }
- // isSHA512 is the validation function for validating if the field's value is a valid SHA512.
- func isSHA512(fl FieldLevel) bool {
- return sha512Regex.MatchString(fl.Field().String())
- }
- // isRIPEMD128 is the validation function for validating if the field's value is a valid PIPEMD128.
- func isRIPEMD128(fl FieldLevel) bool {
- return ripemd128Regex.MatchString(fl.Field().String())
- }
- // isRIPEMD160 is the validation function for validating if the field's value is a valid PIPEMD160.
- func isRIPEMD160(fl FieldLevel) bool {
- return ripemd160Regex.MatchString(fl.Field().String())
- }
- // isTIGER128 is the validation function for validating if the field's value is a valid TIGER128.
- func isTIGER128(fl FieldLevel) bool {
- return tiger128Regex.MatchString(fl.Field().String())
- }
- // isTIGER160 is the validation function for validating if the field's value is a valid TIGER160.
- func isTIGER160(fl FieldLevel) bool {
- return tiger160Regex.MatchString(fl.Field().String())
- }
- // isTIGER192 is the validation function for validating if the field's value is a valid isTIGER192.
- func isTIGER192(fl FieldLevel) bool {
- return tiger192Regex.MatchString(fl.Field().String())
- }
- // isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
- func isISBN(fl FieldLevel) bool {
- return isISBN10(fl) || isISBN13(fl)
- }
- // isISBN13 is the validation function for validating if the field's value is a valid v13 ISBN.
- func isISBN13(fl FieldLevel) bool {
- s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4)
- if !iSBN13Regex.MatchString(s) {
- return false
- }
- var checksum int32
- var i int32
- factor := []int32{1, 3}
- for i = 0; i < 12; i++ {
- checksum += factor[i%2] * int32(s[i]-'0')
- }
- return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0
- }
- // isISBN10 is the validation function for validating if the field's value is a valid v10 ISBN.
- func isISBN10(fl FieldLevel) bool {
- s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3)
- if !iSBN10Regex.MatchString(s) {
- return false
- }
- var checksum int32
- var i int32
- for i = 0; i < 9; i++ {
- checksum += (i + 1) * int32(s[i]-'0')
- }
- if s[9] == 'X' {
- checksum += 10 * 10
- } else {
- checksum += 10 * int32(s[9]-'0')
- }
- return checksum%11 == 0
- }
- // isISSN is the validation function for validating if the field's value is a valid ISSN.
- func isISSN(fl FieldLevel) bool {
- s := fl.Field().String()
- if !iSSNRegex.MatchString(s) {
- return false
- }
- s = strings.ReplaceAll(s, "-", "")
- pos := 8
- checksum := 0
- for i := 0; i < 7; i++ {
- checksum += pos * int(s[i]-'0')
- pos--
- }
- if s[7] == 'X' {
- checksum += 10
- } else {
- checksum += int(s[7] - '0')
- }
- return checksum%11 == 0
- }
- // isEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
- func isEthereumAddress(fl FieldLevel) bool {
- address := fl.Field().String()
- return ethAddressRegex.MatchString(address)
- }
- // isEthereumAddressChecksum is the validation function for validating if the field's value is a valid checksumed Ethereum address.
- func isEthereumAddressChecksum(fl FieldLevel) bool {
- address := fl.Field().String()
- if !ethAddressRegex.MatchString(address) {
- return false
- }
- // Checksum validation. Reference: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
- address = address[2:] // Skip "0x" prefix.
- h := sha3.NewLegacyKeccak256()
- // hash.Hash's io.Writer implementation says it never returns an error. https://golang.org/pkg/hash/#Hash
- _, _ = h.Write([]byte(strings.ToLower(address)))
- hash := hex.EncodeToString(h.Sum(nil))
- for i := 0; i < len(address); i++ {
- if address[i] <= '9' { // Skip 0-9 digits: they don't have upper/lower-case.
- continue
- }
- if hash[i] > '7' && address[i] >= 'a' || hash[i] <= '7' && address[i] <= 'F' {
- return false
- }
- }
- return true
- }
- // isBitcoinAddress is the validation function for validating if the field's value is a valid btc address
- func isBitcoinAddress(fl FieldLevel) bool {
- address := fl.Field().String()
- if !btcAddressRegex.MatchString(address) {
- return false
- }
- alphabet := []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
- decode := [25]byte{}
- for _, n := range []byte(address) {
- d := bytes.IndexByte(alphabet, n)
- for i := 24; i >= 0; i-- {
- d += 58 * int(decode[i])
- decode[i] = byte(d % 256)
- d /= 256
- }
- }
- h := sha256.New()
- _, _ = h.Write(decode[:21])
- d := h.Sum([]byte{})
- h = sha256.New()
- _, _ = h.Write(d)
- validchecksum := [4]byte{}
- computedchecksum := [4]byte{}
- copy(computedchecksum[:], h.Sum(d[:0]))
- copy(validchecksum[:], decode[21:])
- return validchecksum == computedchecksum
- }
- // isBitcoinBech32Address is the validation function for validating if the field's value is a valid bech32 btc address
- func isBitcoinBech32Address(fl FieldLevel) bool {
- address := fl.Field().String()
- if !btcLowerAddressRegexBech32.MatchString(address) && !btcUpperAddressRegexBech32.MatchString(address) {
- return false
- }
- am := len(address) % 8
- if am == 0 || am == 3 || am == 5 {
- return false
- }
- address = strings.ToLower(address)
- alphabet := "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
- hr := []int{3, 3, 0, 2, 3} // the human readable part will always be bc
- addr := address[3:]
- dp := make([]int, 0, len(addr))
- for _, c := range addr {
- dp = append(dp, strings.IndexRune(alphabet, c))
- }
- ver := dp[0]
- if ver < 0 || ver > 16 {
- return false
- }
- if ver == 0 {
- if len(address) != 42 && len(address) != 62 {
- return false
- }
- }
- values := append(hr, dp...)
- GEN := []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
- p := 1
- for _, v := range values {
- b := p >> 25
- p = (p&0x1ffffff)<<5 ^ v
- for i := 0; i < 5; i++ {
- if (b>>uint(i))&1 == 1 {
- p ^= GEN[i]
- }
- }
- }
- if p != 1 {
- return false
- }
- b := uint(0)
- acc := 0
- mv := (1 << 5) - 1
- var sw []int
- for _, v := range dp[1 : len(dp)-6] {
- acc = (acc << 5) | v
- b += 5
- for b >= 8 {
- b -= 8
- sw = append(sw, (acc>>b)&mv)
- }
- }
- if len(sw) < 2 || len(sw) > 40 {
- return false
- }
- return true
- }
- // excludesRune is the validation function for validating that the field's value does not contain the rune specified within the param.
- func excludesRune(fl FieldLevel) bool {
- return !containsRune(fl)
- }
- // excludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param.
- func excludesAll(fl FieldLevel) bool {
- return !containsAny(fl)
- }
- // excludes is the validation function for validating that the field's value does not contain the text specified within the param.
- func excludes(fl FieldLevel) bool {
- return !contains(fl)
- }
- // containsRune is the validation function for validating that the field's value contains the rune specified within the param.
- func containsRune(fl FieldLevel) bool {
- r, _ := utf8.DecodeRuneInString(fl.Param())
- return strings.ContainsRune(fl.Field().String(), r)
- }
- // containsAny is the validation function for validating that the field's value contains any of the characters specified within the param.
- func containsAny(fl FieldLevel) bool {
- return strings.ContainsAny(fl.Field().String(), fl.Param())
- }
- // contains is the validation function for validating that the field's value contains the text specified within the param.
- func contains(fl FieldLevel) bool {
- return strings.Contains(fl.Field().String(), fl.Param())
- }
- // startsWith is the validation function for validating that the field's value starts with the text specified within the param.
- func startsWith(fl FieldLevel) bool {
- return strings.HasPrefix(fl.Field().String(), fl.Param())
- }
- // endsWith is the validation function for validating that the field's value ends with the text specified within the param.
- func endsWith(fl FieldLevel) bool {
- return strings.HasSuffix(fl.Field().String(), fl.Param())
- }
- // startsNotWith is the validation function for validating that the field's value does not start with the text specified within the param.
- func startsNotWith(fl FieldLevel) bool {
- return !startsWith(fl)
- }
- // endsNotWith is the validation function for validating that the field's value does not end with the text specified within the param.
- func endsNotWith(fl FieldLevel) bool {
- return !endsWith(fl)
- }
- // fieldContains is the validation function for validating if the current field's value contains the field specified by the param's value.
- func fieldContains(fl FieldLevel) bool {
- field := fl.Field()
- currentField, _, ok := fl.GetStructFieldOK()
- if !ok {
- return false
- }
- return strings.Contains(field.String(), currentField.String())
- }
- // fieldExcludes is the validation function for validating if the current field's value excludes the field specified by the param's value.
- func fieldExcludes(fl FieldLevel) bool {
- field := fl.Field()
- currentField, _, ok := fl.GetStructFieldOK()
- if !ok {
- return true
- }
- return !strings.Contains(field.String(), currentField.String())
- }
- // isNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value.
- func isNeField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- currentField, currentKind, ok := fl.GetStructFieldOK()
- if !ok || currentKind != kind {
- return true
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() != currentField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() != currentField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() != currentField.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(field.Len()) != int64(currentField.Len())
- case reflect.Bool:
- return field.Bool() != currentField.Bool()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
- t := currentField.Interface().(time.Time)
- fieldTime := field.Interface().(time.Time)
- return !fieldTime.Equal(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != currentField.Type() {
- return true
- }
- }
- // default reflect.String:
- return field.String() != currentField.String()
- }
- // isNe is the validation function for validating that the field's value does not equal the provided param value.
- func isNe(fl FieldLevel) bool {
- return !isEq(fl)
- }
- // isNeIgnoreCase is the validation function for validating that the field's string value does not equal the
- // provided param value. The comparison is case-insensitive
- func isNeIgnoreCase(fl FieldLevel) bool {
- return !isEqIgnoreCase(fl)
- }
- // isLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value.
- func isLteCrossStructField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- topField, topKind, ok := fl.GetStructFieldOK()
- if !ok || topKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() <= topField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() <= topField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() <= topField.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(field.Len()) <= int64(topField.Len())
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
- return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != topField.Type() {
- return false
- }
- }
- // default reflect.String:
- return field.String() <= topField.String()
- }
- // isLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value.
- // NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
- func isLtCrossStructField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- topField, topKind, ok := fl.GetStructFieldOK()
- if !ok || topKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() < topField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() < topField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() < topField.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(field.Len()) < int64(topField.Len())
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
- return fieldTime.Before(topTime)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != topField.Type() {
- return false
- }
- }
- // default reflect.String:
- return field.String() < topField.String()
- }
- // isGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value.
- func isGteCrossStructField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- topField, topKind, ok := fl.GetStructFieldOK()
- if !ok || topKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() >= topField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() >= topField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() >= topField.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(field.Len()) >= int64(topField.Len())
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
- return fieldTime.After(topTime) || fieldTime.Equal(topTime)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != topField.Type() {
- return false
- }
- }
- // default reflect.String:
- return field.String() >= topField.String()
- }
- // isGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value.
- func isGtCrossStructField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- topField, topKind, ok := fl.GetStructFieldOK()
- if !ok || topKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() > topField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() > topField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() > topField.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(field.Len()) > int64(topField.Len())
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- topTime := topField.Convert(timeType).Interface().(time.Time)
- return fieldTime.After(topTime)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != topField.Type() {
- return false
- }
- }
- // default reflect.String:
- return field.String() > topField.String()
- }
- // isNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value.
- func isNeCrossStructField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- topField, currentKind, ok := fl.GetStructFieldOK()
- if !ok || currentKind != kind {
- return true
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return topField.Int() != field.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return topField.Uint() != field.Uint()
- case reflect.Float32, reflect.Float64:
- return topField.Float() != field.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(topField.Len()) != int64(field.Len())
- case reflect.Bool:
- return topField.Bool() != field.Bool()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
- t := field.Convert(timeType).Interface().(time.Time)
- fieldTime := topField.Convert(timeType).Interface().(time.Time)
- return !fieldTime.Equal(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != topField.Type() {
- return true
- }
- }
- // default reflect.String:
- return topField.String() != field.String()
- }
- // isEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value.
- func isEqCrossStructField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- topField, topKind, ok := fl.GetStructFieldOK()
- if !ok || topKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return topField.Int() == field.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return topField.Uint() == field.Uint()
- case reflect.Float32, reflect.Float64:
- return topField.Float() == field.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(topField.Len()) == int64(field.Len())
- case reflect.Bool:
- return topField.Bool() == field.Bool()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
- t := field.Convert(timeType).Interface().(time.Time)
- fieldTime := topField.Convert(timeType).Interface().(time.Time)
- return fieldTime.Equal(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != topField.Type() {
- return false
- }
- }
- // default reflect.String:
- return topField.String() == field.String()
- }
- // isEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value.
- func isEqField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- currentField, currentKind, ok := fl.GetStructFieldOK()
- if !ok || currentKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() == currentField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() == currentField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() == currentField.Float()
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(field.Len()) == int64(currentField.Len())
- case reflect.Bool:
- return field.Bool() == currentField.Bool()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- return fieldTime.Equal(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != currentField.Type() {
- return false
- }
- }
- // default reflect.String:
- return field.String() == currentField.String()
- }
- // isEq is the validation function for validating if the current field's value is equal to the param's value.
- func isEq(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- switch field.Kind() {
- case reflect.String:
- return field.String() == param
- case reflect.Slice, reflect.Map, reflect.Array:
- p := asInt(param)
- return int64(field.Len()) == p
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p := asIntFromType(field.Type(), param)
- return field.Int() == p
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p := asUint(param)
- return field.Uint() == p
- case reflect.Float32:
- p := asFloat32(param)
- return field.Float() == p
- case reflect.Float64:
- p := asFloat64(param)
- return field.Float() == p
- case reflect.Bool:
- p := asBool(param)
- return field.Bool() == p
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isEqIgnoreCase is the validation function for validating if the current field's string value is
- // equal to the param's value.
- // The comparison is case-insensitive.
- func isEqIgnoreCase(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- switch field.Kind() {
- case reflect.String:
- return strings.EqualFold(field.String(), param)
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isPostcodeByIso3166Alpha2 validates by value which is country code in iso 3166 alpha 2
- // example: `postcode_iso3166_alpha2=US`
- func isPostcodeByIso3166Alpha2(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- reg, found := postCodeRegexDict[param]
- if !found {
- return false
- }
- return reg.MatchString(field.String())
- }
- // isPostcodeByIso3166Alpha2Field validates by field which represents for a value of country code in iso 3166 alpha 2
- // example: `postcode_iso3166_alpha2_field=CountryCode`
- func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
- field := fl.Field()
- params := parseOneOfParam2(fl.Param())
- if len(params) != 1 {
- return false
- }
- currentField, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), params[0])
- if !found {
- return false
- }
- if kind != reflect.String {
- panic(fmt.Sprintf("Bad field type %T", currentField.Interface()))
- }
- reg, found := postCodeRegexDict[currentField.String()]
- if !found {
- return false
- }
- return reg.MatchString(field.String())
- }
- // isBase32 is the validation function for validating if the current field's value is a valid base 32.
- func isBase32(fl FieldLevel) bool {
- return base32Regex.MatchString(fl.Field().String())
- }
- // isBase64 is the validation function for validating if the current field's value is a valid base 64.
- func isBase64(fl FieldLevel) bool {
- return base64Regex.MatchString(fl.Field().String())
- }
- // isBase64URL is the validation function for validating if the current field's value is a valid base64 URL safe string.
- func isBase64URL(fl FieldLevel) bool {
- return base64URLRegex.MatchString(fl.Field().String())
- }
- // isBase64RawURL is the validation function for validating if the current field's value is a valid base64 URL safe string without '=' padding.
- func isBase64RawURL(fl FieldLevel) bool {
- return base64RawURLRegex.MatchString(fl.Field().String())
- }
- // isURI is the validation function for validating if the current field's value is a valid URI.
- func isURI(fl FieldLevel) bool {
- field := fl.Field()
- switch field.Kind() {
- case reflect.String:
- s := field.String()
- // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
- // emulate browser and strip the '#' suffix prior to validation. see issue-#237
- if i := strings.Index(s, "#"); i > -1 {
- s = s[:i]
- }
- if len(s) == 0 {
- return false
- }
- _, err := url.ParseRequestURI(s)
- return err == nil
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isFileURL is the helper function for validating if the `path` valid file URL as per RFC8089
- func isFileURL(path string) bool {
- if !strings.HasPrefix(path, "file:/") {
- return false
- }
- _, err := url.ParseRequestURI(path)
- return err == nil
- }
- // isURL is the validation function for validating if the current field's value is a valid URL.
- func isURL(fl FieldLevel) bool {
- field := fl.Field()
- switch field.Kind() {
- case reflect.String:
- s := strings.ToLower(field.String())
- if len(s) == 0 {
- return false
- }
- if isFileURL(s) {
- return true
- }
- url, err := url.Parse(s)
- if err != nil || url.Scheme == "" {
- return false
- }
- if url.Host == "" && url.Fragment == "" && url.Opaque == "" {
- return false
- }
- return true
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL.
- func isHttpURL(fl FieldLevel) bool {
- if !isURL(fl) {
- return false
- }
- field := fl.Field()
- switch field.Kind() {
- case reflect.String:
- s := strings.ToLower(field.String())
- url, err := url.Parse(s)
- if err != nil || url.Host == "" {
- return false
- }
- return url.Scheme == "http" || url.Scheme == "https"
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141.
- func isUrnRFC2141(fl FieldLevel) bool {
- field := fl.Field()
- switch field.Kind() {
- case reflect.String:
- str := field.String()
- _, match := urn.Parse([]byte(str))
- return match
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isFile is the validation function for validating if the current field's value is a valid existing file path.
- func isFile(fl FieldLevel) bool {
- field := fl.Field()
- switch field.Kind() {
- case reflect.String:
- fileInfo, err := os.Stat(field.String())
- if err != nil {
- return false
- }
- return !fileInfo.IsDir()
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isImage is the validation function for validating if the current field's value contains the path to a valid image file
- func isImage(fl FieldLevel) bool {
- mimetypes := map[string]bool{
- "image/bmp": true,
- "image/cis-cod": true,
- "image/gif": true,
- "image/ief": true,
- "image/jpeg": true,
- "image/jp2": true,
- "image/jpx": true,
- "image/jpm": true,
- "image/pipeg": true,
- "image/png": true,
- "image/svg+xml": true,
- "image/tiff": true,
- "image/webp": true,
- "image/x-cmu-raster": true,
- "image/x-cmx": true,
- "image/x-icon": true,
- "image/x-portable-anymap": true,
- "image/x-portable-bitmap": true,
- "image/x-portable-graymap": true,
- "image/x-portable-pixmap": true,
- "image/x-rgb": true,
- "image/x-xbitmap": true,
- "image/x-xpixmap": true,
- "image/x-xwindowdump": true,
- }
- field := fl.Field()
- switch field.Kind() {
- case reflect.String:
- filePath := field.String()
- fileInfo, err := os.Stat(filePath)
- if err != nil {
- return false
- }
- if fileInfo.IsDir() {
- return false
- }
- file, err := os.Open(filePath)
- if err != nil {
- return false
- }
- defer file.Close()
- mime, err := mimetype.DetectReader(file)
- if err != nil {
- return false
- }
- if _, ok := mimetypes[mime.String()]; ok {
- return true
- }
- }
- return false
- }
- // isFilePath is the validation function for validating if the current field's value is a valid file path.
- func isFilePath(fl FieldLevel) bool {
- var exists bool
- var err error
- field := fl.Field()
- // Not valid if it is a directory.
- if isDir(fl) {
- return false
- }
- // If it exists, it obviously is valid.
- // This is done first to avoid code duplication and unnecessary additional logic.
- if exists = isFile(fl); exists {
- return true
- }
- // It does not exist but may still be a valid filepath.
- switch field.Kind() {
- case reflect.String:
- // Every OS allows for whitespace, but none
- // let you use a file with no filename (to my knowledge).
- // Unless you're dealing with raw inodes, but I digress.
- if strings.TrimSpace(field.String()) == "" {
- return false
- }
- // We make sure it isn't a directory.
- if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
- return false
- }
- if _, err = os.Stat(field.String()); err != nil {
- switch t := err.(type) {
- case *fs.PathError:
- if t.Err == syscall.EINVAL {
- // It's definitely an invalid character in the filepath.
- return false
- }
- // It could be a permission error, a does-not-exist error, etc.
- // Out-of-scope for this validation, though.
- return true
- default:
- // Something went *seriously* wrong.
- /*
- Per https://pkg.go.dev/os#Stat:
- "If there is an error, it will be of type *PathError."
- */
- panic(err)
- }
- }
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
- func isE164(fl FieldLevel) bool {
- return e164Regex.MatchString(fl.Field().String())
- }
- // isEmail is the validation function for validating if the current field's value is a valid email address.
- func isEmail(fl FieldLevel) bool {
- return emailRegex.MatchString(fl.Field().String())
- }
- // isHSLA is the validation function for validating if the current field's value is a valid HSLA color.
- func isHSLA(fl FieldLevel) bool {
- return hslaRegex.MatchString(fl.Field().String())
- }
- // isHSL is the validation function for validating if the current field's value is a valid HSL color.
- func isHSL(fl FieldLevel) bool {
- return hslRegex.MatchString(fl.Field().String())
- }
- // isRGBA is the validation function for validating if the current field's value is a valid RGBA color.
- func isRGBA(fl FieldLevel) bool {
- return rgbaRegex.MatchString(fl.Field().String())
- }
- // isRGB is the validation function for validating if the current field's value is a valid RGB color.
- func isRGB(fl FieldLevel) bool {
- return rgbRegex.MatchString(fl.Field().String())
- }
- // isHEXColor is the validation function for validating if the current field's value is a valid HEX color.
- func isHEXColor(fl FieldLevel) bool {
- return hexColorRegex.MatchString(fl.Field().String())
- }
- // isHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal.
- func isHexadecimal(fl FieldLevel) bool {
- return hexadecimalRegex.MatchString(fl.Field().String())
- }
- // isNumber is the validation function for validating if the current field's value is a valid number.
- func isNumber(fl FieldLevel) bool {
- switch fl.Field().Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
- return true
- default:
- return numberRegex.MatchString(fl.Field().String())
- }
- }
- // isNumeric is the validation function for validating if the current field's value is a valid numeric value.
- func isNumeric(fl FieldLevel) bool {
- switch fl.Field().Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64:
- return true
- default:
- return numericRegex.MatchString(fl.Field().String())
- }
- }
- // isAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value.
- func isAlphanum(fl FieldLevel) bool {
- return alphaNumericRegex.MatchString(fl.Field().String())
- }
- // isAlpha is the validation function for validating if the current field's value is a valid alpha value.
- func isAlpha(fl FieldLevel) bool {
- return alphaRegex.MatchString(fl.Field().String())
- }
- // isAlphanumUnicode is the validation function for validating if the current field's value is a valid alphanumeric unicode value.
- func isAlphanumUnicode(fl FieldLevel) bool {
- return alphaUnicodeNumericRegex.MatchString(fl.Field().String())
- }
- // isAlphaUnicode is the validation function for validating if the current field's value is a valid alpha unicode value.
- func isAlphaUnicode(fl FieldLevel) bool {
- return alphaUnicodeRegex.MatchString(fl.Field().String())
- }
- // isBoolean is the validation function for validating if the current field's value is a valid boolean value or can be safely converted to a boolean value.
- func isBoolean(fl FieldLevel) bool {
- switch fl.Field().Kind() {
- case reflect.Bool:
- return true
- default:
- _, err := strconv.ParseBool(fl.Field().String())
- return err == nil
- }
- }
- // isDefault is the opposite of required aka hasValue
- func isDefault(fl FieldLevel) bool {
- return !hasValue(fl)
- }
- // hasValue is the validation function for validating if the current field's value is not the default static value.
- func hasValue(fl FieldLevel) bool {
- field := fl.Field()
- switch field.Kind() {
- case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
- return !field.IsNil()
- default:
- if fl.(*validate).fldIsPointer && field.Interface() != nil {
- return true
- }
- return field.IsValid() && !field.IsZero()
- }
- }
- // requireCheckFieldKind is a func for check field kind
- func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
- field := fl.Field()
- kind := field.Kind()
- var nullable, found bool
- if len(param) > 0 {
- field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
- if !found {
- return defaultNotFoundValue
- }
- }
- switch kind {
- case reflect.Invalid:
- return defaultNotFoundValue
- case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
- return field.IsNil()
- default:
- if nullable && field.Interface() != nil {
- return false
- }
- return field.IsValid() && field.IsZero()
- }
- }
- // requireCheckFieldValue is a func for check field value
- func requireCheckFieldValue(
- fl FieldLevel, param string, value string, defaultNotFoundValue bool,
- ) bool {
- field, kind, _, found := fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
- if !found {
- return defaultNotFoundValue
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() == asInt(value)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() == asUint(value)
- case reflect.Float32:
- return field.Float() == asFloat32(value)
- case reflect.Float64:
- return field.Float() == asFloat64(value)
- case reflect.Slice, reflect.Map, reflect.Array:
- return int64(field.Len()) == asInt(value)
- case reflect.Bool:
- return field.Bool() == asBool(value)
- }
- // default reflect.String:
- return field.String() == value
- }
- // requiredIf is the validation function
- // The field under validation must be present and not empty only if all the other specified fields are equal to the value following with the specified field.
- func requiredIf(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- if len(params)%2 != 0 {
- panic(fmt.Sprintf("Bad param number for required_if %s", fl.FieldName()))
- }
- for i := 0; i < len(params); i += 2 {
- if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
- return true
- }
- }
- return hasValue(fl)
- }
- // excludedIf is the validation function
- // The field under validation must not be present or is empty only if all the other specified fields are equal to the value following with the specified field.
- func excludedIf(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- if len(params)%2 != 0 {
- panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName()))
- }
- for i := 0; i < len(params); i += 2 {
- if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
- return true
- }
- }
- return !hasValue(fl)
- }
- // requiredUnless is the validation function
- // The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
- func requiredUnless(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- if len(params)%2 != 0 {
- panic(fmt.Sprintf("Bad param number for required_unless %s", fl.FieldName()))
- }
- for i := 0; i < len(params); i += 2 {
- if requireCheckFieldValue(fl, params[i], params[i+1], false) {
- return true
- }
- }
- return hasValue(fl)
- }
- // skipUnless is the validation function
- // The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
- func skipUnless(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- if len(params)%2 != 0 {
- panic(fmt.Sprintf("Bad param number for skip_unless %s", fl.FieldName()))
- }
- for i := 0; i < len(params); i += 2 {
- if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
- return true
- }
- }
- return hasValue(fl)
- }
- // excludedUnless is the validation function
- // The field under validation must not be present or is empty unless all the other specified fields are equal to the value following with the specified field.
- func excludedUnless(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- if len(params)%2 != 0 {
- panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName()))
- }
- for i := 0; i < len(params); i += 2 {
- if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
- return !hasValue(fl)
- }
- }
- return true
- }
- // excludedWith is the validation function
- // The field under validation must not be present or is empty if any of the other specified fields are present.
- func excludedWith(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- for _, param := range params {
- if !requireCheckFieldKind(fl, param, true) {
- return !hasValue(fl)
- }
- }
- return true
- }
- // requiredWith is the validation function
- // The field under validation must be present and not empty only if any of the other specified fields are present.
- func requiredWith(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- for _, param := range params {
- if !requireCheckFieldKind(fl, param, true) {
- return hasValue(fl)
- }
- }
- return true
- }
- // excludedWithAll is the validation function
- // The field under validation must not be present or is empty if all of the other specified fields are present.
- func excludedWithAll(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- for _, param := range params {
- if requireCheckFieldKind(fl, param, true) {
- return true
- }
- }
- return !hasValue(fl)
- }
- // requiredWithAll is the validation function
- // The field under validation must be present and not empty only if all of the other specified fields are present.
- func requiredWithAll(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- for _, param := range params {
- if requireCheckFieldKind(fl, param, true) {
- return true
- }
- }
- return hasValue(fl)
- }
- // excludedWithout is the validation function
- // The field under validation must not be present or is empty when any of the other specified fields are not present.
- func excludedWithout(fl FieldLevel) bool {
- if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
- return !hasValue(fl)
- }
- return true
- }
- // requiredWithout is the validation function
- // The field under validation must be present and not empty only when any of the other specified fields are not present.
- func requiredWithout(fl FieldLevel) bool {
- if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
- return hasValue(fl)
- }
- return true
- }
- // excludedWithoutAll is the validation function
- // The field under validation must not be present or is empty when all of the other specified fields are not present.
- func excludedWithoutAll(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- for _, param := range params {
- if !requireCheckFieldKind(fl, param, true) {
- return true
- }
- }
- return !hasValue(fl)
- }
- // requiredWithoutAll is the validation function
- // The field under validation must be present and not empty only when all of the other specified fields are not present.
- func requiredWithoutAll(fl FieldLevel) bool {
- params := parseOneOfParam2(fl.Param())
- for _, param := range params {
- if !requireCheckFieldKind(fl, param, true) {
- return true
- }
- }
- return hasValue(fl)
- }
- // isGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value.
- func isGteField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- currentField, currentKind, ok := fl.GetStructFieldOK()
- if !ok || currentKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() >= currentField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() >= currentField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() >= currentField.Float()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- return fieldTime.After(t) || fieldTime.Equal(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != currentField.Type() {
- return false
- }
- }
- // default reflect.String
- return len(field.String()) >= len(currentField.String())
- }
- // isGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value.
- func isGtField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- currentField, currentKind, ok := fl.GetStructFieldOK()
- if !ok || currentKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() > currentField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() > currentField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() > currentField.Float()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- return fieldTime.After(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != currentField.Type() {
- return false
- }
- }
- // default reflect.String
- return len(field.String()) > len(currentField.String())
- }
- // isGte is the validation function for validating if the current field's value is greater than or equal to the param's value.
- func isGte(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- switch field.Kind() {
- case reflect.String:
- p := asInt(param)
- return int64(utf8.RuneCountInString(field.String())) >= p
- case reflect.Slice, reflect.Map, reflect.Array:
- p := asInt(param)
- return int64(field.Len()) >= p
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p := asIntFromType(field.Type(), param)
- return field.Int() >= p
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p := asUint(param)
- return field.Uint() >= p
- case reflect.Float32:
- p := asFloat32(param)
- return field.Float() >= p
- case reflect.Float64:
- p := asFloat64(param)
- return field.Float() >= p
- case reflect.Struct:
- if field.Type().ConvertibleTo(timeType) {
- now := time.Now().UTC()
- t := field.Convert(timeType).Interface().(time.Time)
- return t.After(now) || t.Equal(now)
- }
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isGt is the validation function for validating if the current field's value is greater than the param's value.
- func isGt(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- switch field.Kind() {
- case reflect.String:
- p := asInt(param)
- return int64(utf8.RuneCountInString(field.String())) > p
- case reflect.Slice, reflect.Map, reflect.Array:
- p := asInt(param)
- return int64(field.Len()) > p
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p := asIntFromType(field.Type(), param)
- return field.Int() > p
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p := asUint(param)
- return field.Uint() > p
- case reflect.Float32:
- p := asFloat32(param)
- return field.Float() > p
- case reflect.Float64:
- p := asFloat64(param)
- return field.Float() > p
- case reflect.Struct:
- if field.Type().ConvertibleTo(timeType) {
- return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
- }
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // hasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
- func hasLengthOf(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- switch field.Kind() {
- case reflect.String:
- p := asInt(param)
- return int64(utf8.RuneCountInString(field.String())) == p
- case reflect.Slice, reflect.Map, reflect.Array:
- p := asInt(param)
- return int64(field.Len()) == p
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p := asIntFromType(field.Type(), param)
- return field.Int() == p
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p := asUint(param)
- return field.Uint() == p
- case reflect.Float32:
- p := asFloat32(param)
- return field.Float() == p
- case reflect.Float64:
- p := asFloat64(param)
- return field.Float() == p
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // hasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value.
- func hasMinOf(fl FieldLevel) bool {
- return isGte(fl)
- }
- // isLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value.
- func isLteField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- currentField, currentKind, ok := fl.GetStructFieldOK()
- if !ok || currentKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() <= currentField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() <= currentField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() <= currentField.Float()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- return fieldTime.Before(t) || fieldTime.Equal(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != currentField.Type() {
- return false
- }
- }
- // default reflect.String
- return len(field.String()) <= len(currentField.String())
- }
- // isLtField is the validation function for validating if the current field's value is less than the field specified by the param's value.
- func isLtField(fl FieldLevel) bool {
- field := fl.Field()
- kind := field.Kind()
- currentField, currentKind, ok := fl.GetStructFieldOK()
- if !ok || currentKind != kind {
- return false
- }
- switch kind {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return field.Int() < currentField.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return field.Uint() < currentField.Uint()
- case reflect.Float32, reflect.Float64:
- return field.Float() < currentField.Float()
- case reflect.Struct:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
- t := currentField.Convert(timeType).Interface().(time.Time)
- fieldTime := field.Convert(timeType).Interface().(time.Time)
- return fieldTime.Before(t)
- }
- // Not Same underlying type i.e. struct and time
- if fieldType != currentField.Type() {
- return false
- }
- }
- // default reflect.String
- return len(field.String()) < len(currentField.String())
- }
- // isLte is the validation function for validating if the current field's value is less than or equal to the param's value.
- func isLte(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- switch field.Kind() {
- case reflect.String:
- p := asInt(param)
- return int64(utf8.RuneCountInString(field.String())) <= p
- case reflect.Slice, reflect.Map, reflect.Array:
- p := asInt(param)
- return int64(field.Len()) <= p
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p := asIntFromType(field.Type(), param)
- return field.Int() <= p
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p := asUint(param)
- return field.Uint() <= p
- case reflect.Float32:
- p := asFloat32(param)
- return field.Float() <= p
- case reflect.Float64:
- p := asFloat64(param)
- return field.Float() <= p
- case reflect.Struct:
- if field.Type().ConvertibleTo(timeType) {
- now := time.Now().UTC()
- t := field.Convert(timeType).Interface().(time.Time)
- return t.Before(now) || t.Equal(now)
- }
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isLt is the validation function for validating if the current field's value is less than the param's value.
- func isLt(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- switch field.Kind() {
- case reflect.String:
- p := asInt(param)
- return int64(utf8.RuneCountInString(field.String())) < p
- case reflect.Slice, reflect.Map, reflect.Array:
- p := asInt(param)
- return int64(field.Len()) < p
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- p := asIntFromType(field.Type(), param)
- return field.Int() < p
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- p := asUint(param)
- return field.Uint() < p
- case reflect.Float32:
- p := asFloat32(param)
- return field.Float() < p
- case reflect.Float64:
- p := asFloat64(param)
- return field.Float() < p
- case reflect.Struct:
- if field.Type().ConvertibleTo(timeType) {
- return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
- }
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // hasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value.
- func hasMaxOf(fl FieldLevel) bool {
- return isLte(fl)
- }
- // isTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address.
- func isTCP4AddrResolvable(fl FieldLevel) bool {
- if !isIP4Addr(fl) {
- return false
- }
- _, err := net.ResolveTCPAddr("tcp4", fl.Field().String())
- return err == nil
- }
- // isTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address.
- func isTCP6AddrResolvable(fl FieldLevel) bool {
- if !isIP6Addr(fl) {
- return false
- }
- _, err := net.ResolveTCPAddr("tcp6", fl.Field().String())
- return err == nil
- }
- // isTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address.
- func isTCPAddrResolvable(fl FieldLevel) bool {
- if !isIP4Addr(fl) && !isIP6Addr(fl) {
- return false
- }
- _, err := net.ResolveTCPAddr("tcp", fl.Field().String())
- return err == nil
- }
- // isUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address.
- func isUDP4AddrResolvable(fl FieldLevel) bool {
- if !isIP4Addr(fl) {
- return false
- }
- _, err := net.ResolveUDPAddr("udp4", fl.Field().String())
- return err == nil
- }
- // isUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address.
- func isUDP6AddrResolvable(fl FieldLevel) bool {
- if !isIP6Addr(fl) {
- return false
- }
- _, err := net.ResolveUDPAddr("udp6", fl.Field().String())
- return err == nil
- }
- // isUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address.
- func isUDPAddrResolvable(fl FieldLevel) bool {
- if !isIP4Addr(fl) && !isIP6Addr(fl) {
- return false
- }
- _, err := net.ResolveUDPAddr("udp", fl.Field().String())
- return err == nil
- }
- // isIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address.
- func isIP4AddrResolvable(fl FieldLevel) bool {
- if !isIPv4(fl) {
- return false
- }
- _, err := net.ResolveIPAddr("ip4", fl.Field().String())
- return err == nil
- }
- // isIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address.
- func isIP6AddrResolvable(fl FieldLevel) bool {
- if !isIPv6(fl) {
- return false
- }
- _, err := net.ResolveIPAddr("ip6", fl.Field().String())
- return err == nil
- }
- // isIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address.
- func isIPAddrResolvable(fl FieldLevel) bool {
- if !isIP(fl) {
- return false
- }
- _, err := net.ResolveIPAddr("ip", fl.Field().String())
- return err == nil
- }
- // isUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address.
- func isUnixAddrResolvable(fl FieldLevel) bool {
- _, err := net.ResolveUnixAddr("unix", fl.Field().String())
- return err == nil
- }
- func isIP4Addr(fl FieldLevel) bool {
- val := fl.Field().String()
- if idx := strings.LastIndex(val, ":"); idx != -1 {
- val = val[0:idx]
- }
- ip := net.ParseIP(val)
- return ip != nil && ip.To4() != nil
- }
- func isIP6Addr(fl FieldLevel) bool {
- val := fl.Field().String()
- if idx := strings.LastIndex(val, ":"); idx != -1 {
- if idx != 0 && val[idx-1:idx] == "]" {
- val = val[1 : idx-1]
- }
- }
- ip := net.ParseIP(val)
- return ip != nil && ip.To4() == nil
- }
- func isHostnameRFC952(fl FieldLevel) bool {
- return hostnameRegexRFC952.MatchString(fl.Field().String())
- }
- func isHostnameRFC1123(fl FieldLevel) bool {
- return hostnameRegexRFC1123.MatchString(fl.Field().String())
- }
- func isFQDN(fl FieldLevel) bool {
- val := fl.Field().String()
- if val == "" {
- return false
- }
- return fqdnRegexRFC1123.MatchString(val)
- }
- // isDir is the validation function for validating if the current field's value is a valid existing directory.
- func isDir(fl FieldLevel) bool {
- field := fl.Field()
- if field.Kind() == reflect.String {
- fileInfo, err := os.Stat(field.String())
- if err != nil {
- return false
- }
- return fileInfo.IsDir()
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isDirPath is the validation function for validating if the current field's value is a valid directory.
- func isDirPath(fl FieldLevel) bool {
- var exists bool
- var err error
- field := fl.Field()
- // If it exists, it obviously is valid.
- // This is done first to avoid code duplication and unnecessary additional logic.
- if exists = isDir(fl); exists {
- return true
- }
- // It does not exist but may still be a valid path.
- switch field.Kind() {
- case reflect.String:
- // Every OS allows for whitespace, but none
- // let you use a dir with no name (to my knowledge).
- // Unless you're dealing with raw inodes, but I digress.
- if strings.TrimSpace(field.String()) == "" {
- return false
- }
- if _, err = os.Stat(field.String()); err != nil {
- switch t := err.(type) {
- case *fs.PathError:
- if t.Err == syscall.EINVAL {
- // It's definitely an invalid character in the path.
- return false
- }
- // It could be a permission error, a does-not-exist error, etc.
- // Out-of-scope for this validation, though.
- // Lastly, we make sure it is a directory.
- if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
- return true
- } else {
- return false
- }
- default:
- // Something went *seriously* wrong.
- /*
- Per https://pkg.go.dev/os#Stat:
- "If there is an error, it will be of type *PathError."
- */
- panic(err)
- }
- }
- // We repeat the check here to make sure it is an explicit directory in case the above os.Stat didn't trigger an error.
- if strings.HasSuffix(field.String(), string(os.PathSeparator)) {
- return true
- } else {
- return false
- }
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isJSON is the validation function for validating if the current field's value is a valid json string.
- func isJSON(fl FieldLevel) bool {
- field := fl.Field()
- switch field.Kind() {
- case reflect.String:
- val := field.String()
- return json.Valid([]byte(val))
- case reflect.Slice:
- fieldType := field.Type()
- if fieldType.ConvertibleTo(byteSliceType) {
- b := field.Convert(byteSliceType).Interface().([]byte)
- return json.Valid(b)
- }
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isJWT is the validation function for validating if the current field's value is a valid JWT string.
- func isJWT(fl FieldLevel) bool {
- return jWTRegex.MatchString(fl.Field().String())
- }
- // isHostnamePort validates a <dns>:<port> combination for fields typically used for socket address.
- func isHostnamePort(fl FieldLevel) bool {
- val := fl.Field().String()
- host, port, err := net.SplitHostPort(val)
- if err != nil {
- return false
- }
- // Port must be a iny <= 65535.
- if portNum, err := strconv.ParseInt(
- port, 10, 32,
- ); err != nil || portNum > 65535 || portNum < 1 {
- return false
- }
- // If host is specified, it should match a DNS name
- if host != "" {
- return hostnameRegexRFC1123.MatchString(host)
- }
- return true
- }
- // isLowercase is the validation function for validating if the current field's value is a lowercase string.
- func isLowercase(fl FieldLevel) bool {
- field := fl.Field()
- if field.Kind() == reflect.String {
- if field.String() == "" {
- return false
- }
- return field.String() == strings.ToLower(field.String())
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isUppercase is the validation function for validating if the current field's value is an uppercase string.
- func isUppercase(fl FieldLevel) bool {
- field := fl.Field()
- if field.Kind() == reflect.String {
- if field.String() == "" {
- return false
- }
- return field.String() == strings.ToUpper(field.String())
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isDatetime is the validation function for validating if the current field's value is a valid datetime string.
- func isDatetime(fl FieldLevel) bool {
- field := fl.Field()
- param := fl.Param()
- if field.Kind() == reflect.String {
- _, err := time.Parse(param, field.String())
- return err == nil
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isTimeZone is the validation function for validating if the current field's value is a valid time zone string.
- func isTimeZone(fl FieldLevel) bool {
- field := fl.Field()
- if field.Kind() == reflect.String {
- // empty value is converted to UTC by time.LoadLocation but disallow it as it is not a valid time zone name
- if field.String() == "" {
- return false
- }
- // Local value is converted to the current system time zone by time.LoadLocation but disallow it as it is not a valid time zone name
- if strings.ToLower(field.String()) == "local" {
- return false
- }
- _, err := time.LoadLocation(field.String())
- return err == nil
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code.
- func isIso3166Alpha2(fl FieldLevel) bool {
- val := fl.Field().String()
- return iso3166_1_alpha2[val]
- }
- // isIso3166Alpha2EU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 European Union country code.
- func isIso3166Alpha2EU(fl FieldLevel) bool {
- val := fl.Field().String()
- return iso3166_1_alpha2_eu[val]
- }
- // isIso3166Alpha3 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 country code.
- func isIso3166Alpha3(fl FieldLevel) bool {
- val := fl.Field().String()
- return iso3166_1_alpha3[val]
- }
- // isIso3166Alpha3EU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-3 European Union country code.
- func isIso3166Alpha3EU(fl FieldLevel) bool {
- val := fl.Field().String()
- return iso3166_1_alpha3_eu[val]
- }
- // isIso3166AlphaNumeric is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric country code.
- func isIso3166AlphaNumeric(fl FieldLevel) bool {
- field := fl.Field()
- var code int
- switch field.Kind() {
- case reflect.String:
- i, err := strconv.Atoi(field.String())
- if err != nil {
- return false
- }
- code = i % 1000
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- code = int(field.Int() % 1000)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- code = int(field.Uint() % 1000)
- default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- return iso3166_1_alpha_numeric[code]
- }
- // isIso3166AlphaNumericEU is the validation function for validating if the current field's value is a valid iso3166-1 alpha-numeric European Union country code.
- func isIso3166AlphaNumericEU(fl FieldLevel) bool {
- field := fl.Field()
- var code int
- switch field.Kind() {
- case reflect.String:
- i, err := strconv.Atoi(field.String())
- if err != nil {
- return false
- }
- code = i % 1000
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- code = int(field.Int() % 1000)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- code = int(field.Uint() % 1000)
- default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- return iso3166_1_alpha_numeric_eu[code]
- }
- // isIso31662 is the validation function for validating if the current field's value is a valid iso3166-2 code.
- func isIso31662(fl FieldLevel) bool {
- val := fl.Field().String()
- return iso3166_2[val]
- }
- // isIso4217 is the validation function for validating if the current field's value is a valid iso4217 currency code.
- func isIso4217(fl FieldLevel) bool {
- val := fl.Field().String()
- return iso4217[val]
- }
- // isIso4217Numeric is the validation function for validating if the current field's value is a valid iso4217 numeric currency code.
- func isIso4217Numeric(fl FieldLevel) bool {
- field := fl.Field()
- var code int
- switch field.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- code = int(field.Int())
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- code = int(field.Uint())
- default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- return iso4217_numeric[code]
- }
- // isBCP47LanguageTag is the validation function for validating if the current field's value is a valid BCP 47 language tag, as parsed by language.Parse
- func isBCP47LanguageTag(fl FieldLevel) bool {
- field := fl.Field()
- if field.Kind() == reflect.String {
- _, err := language.Parse(field.String())
- return err == nil
- }
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- // isIsoBicFormat is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362
- func isIsoBicFormat(fl FieldLevel) bool {
- bicString := fl.Field().String()
- return bicRegex.MatchString(bicString)
- }
- // isSemverFormat is the validation function for validating if the current field's value is a valid semver version, defined in Semantic Versioning 2.0.0
- func isSemverFormat(fl FieldLevel) bool {
- semverString := fl.Field().String()
- return semverRegex.MatchString(semverString)
- }
- // isCveFormat is the validation function for validating if the current field's value is a valid cve id, defined in CVE mitre org
- func isCveFormat(fl FieldLevel) bool {
- cveString := fl.Field().String()
- return cveRegex.MatchString(cveString)
- }
- // isDnsRFC1035LabelFormat is the validation function
- // for validating if the current field's value is
- // a valid dns RFC 1035 label, defined in RFC 1035.
- func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
- val := fl.Field().String()
- return dnsRegexRFC1035Label.MatchString(val)
- }
- // digitsHaveLuhnChecksum returns true if and only if the last element of the given digits slice is the Luhn checksum of the previous elements
- func digitsHaveLuhnChecksum(digits []string) bool {
- size := len(digits)
- sum := 0
- for i, digit := range digits {
- value, err := strconv.Atoi(digit)
- if err != nil {
- return false
- }
- if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
- v := value * 2
- if v >= 10 {
- sum += 1 + (v % 10)
- } else {
- sum += v
- }
- } else {
- sum += value
- }
- }
- return (sum % 10) == 0
- }
- // isMongoDB is the validation function for validating if the current field's value is valid mongoDB objectID
- func isMongoDB(fl FieldLevel) bool {
- val := fl.Field().String()
- return mongodbRegex.MatchString(val)
- }
- // isSpiceDB is the validation function for validating if the current field's value is valid for use with Authzed SpiceDB in the indicated way
- func isSpiceDB(fl FieldLevel) bool {
- val := fl.Field().String()
- param := fl.Param()
- switch param {
- case "permission":
- return spicedbPermissionRegex.MatchString(val)
- case "type":
- return spicedbTypeRegex.MatchString(val)
- case "id", "":
- return spicedbIDRegex.MatchString(val)
- }
- panic("Unrecognized parameter: " + param)
- }
- // isCreditCard is the validation function for validating if the current field's value is a valid credit card number
- func isCreditCard(fl FieldLevel) bool {
- val := fl.Field().String()
- var creditCard bytes.Buffer
- segments := strings.Split(val, " ")
- for _, segment := range segments {
- if len(segment) < 3 {
- return false
- }
- creditCard.WriteString(segment)
- }
- ccDigits := strings.Split(creditCard.String(), "")
- size := len(ccDigits)
- if size < 12 || size > 19 {
- return false
- }
- return digitsHaveLuhnChecksum(ccDigits)
- }
- // hasLuhnChecksum is the validation for validating if the current field's value has a valid Luhn checksum
- func hasLuhnChecksum(fl FieldLevel) bool {
- field := fl.Field()
- var str string // convert to a string which will then be split into single digits; easier and more readable than shifting/extracting single digits from a number
- switch field.Kind() {
- case reflect.String:
- str = field.String()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- str = strconv.FormatInt(field.Int(), 10)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- str = strconv.FormatUint(field.Uint(), 10)
- default:
- panic(fmt.Sprintf("Bad field type %T", field.Interface()))
- }
- size := len(str)
- if size < 2 { // there has to be at least one digit that carries a meaning + the checksum
- return false
- }
- digits := strings.Split(str, "")
- return digitsHaveLuhnChecksum(digits)
- }
- // isCron is the validation function for validating if the current field's value is a valid cron expression
- func isCron(fl FieldLevel) bool {
- cronString := fl.Field().String()
- return cronRegex.MatchString(cronString)
- }
|