1// Package govalidator is package of validators and sanitizers for strings, structs and collections.
2package govalidator
3
4import (
5	"bytes"
6	"crypto/rsa"
7	"crypto/x509"
8	"encoding/base64"
9	"encoding/json"
10	"encoding/pem"
11	"fmt"
12	"io/ioutil"
13	"net"
14	"net/url"
15	"reflect"
16	"regexp"
17	"sort"
18	"strconv"
19	"strings"
20	"time"
21	"unicode"
22	"unicode/utf8"
23)
24
25var (
26	fieldsRequiredByDefault bool
27	nilPtrAllowedByRequired = false
28	notNumberRegexp         = regexp.MustCompile("[^0-9]+")
29	whiteSpacesAndMinus     = regexp.MustCompile(`[\s-]+`)
30	paramsRegexp            = regexp.MustCompile(`\(.*\)$`)
31)
32
33const maxURLRuneCount = 2083
34const minURLRuneCount = 3
35const RF3339WithoutZone = "2006-01-02T15:04:05"
36
37// SetFieldsRequiredByDefault causes validation to fail when struct fields
38// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
39// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
40//     type exampleStruct struct {
41//         Name  string ``
42//         Email string `valid:"email"`
43// This, however, will only fail when Email is empty or an invalid email address:
44//     type exampleStruct2 struct {
45//         Name  string `valid:"-"`
46//         Email string `valid:"email"`
47// Lastly, this will only fail when Email is an invalid email address but not when it's empty:
48//     type exampleStruct2 struct {
49//         Name  string `valid:"-"`
50//         Email string `valid:"email,optional"`
51func SetFieldsRequiredByDefault(value bool) {
52	fieldsRequiredByDefault = value
53}
54
55// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required.
56// The validation will still reject ptr fields in their zero value state. Example with this enabled:
57//     type exampleStruct struct {
58//         Name  *string `valid:"required"`
59// With `Name` set to "", this will be considered invalid input and will cause a validation error.
60// With `Name` set to nil, this will be considered valid by validation.
61// By default this is disabled.
62func SetNilPtrAllowedByRequired(value bool) {
63	nilPtrAllowedByRequired = value
64}
65
66// IsEmail check if the string is an email.
67func IsEmail(str string) bool {
68	// TODO uppercase letters are not supported
69	return rxEmail.MatchString(str)
70}
71
72// IsExistingEmail check if the string is an email of existing domain
73func IsExistingEmail(email string) bool {
74
75	if len(email) < 6 || len(email) > 254 {
76		return false
77	}
78	at := strings.LastIndex(email, "@")
79	if at <= 0 || at > len(email)-3 {
80		return false
81	}
82	user := email[:at]
83	host := email[at+1:]
84	if len(user) > 64 {
85		return false
86	}
87	if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) {
88		return false
89	}
90	switch host {
91	case "localhost", "example.com":
92		return true
93	}
94	if _, err := net.LookupMX(host); err != nil {
95		if _, err := net.LookupIP(host); err != nil {
96			return false
97		}
98	}
99
100	return true
101}
102
103// IsURL check if the string is an URL.
104func IsURL(str string) bool {
105	if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") {
106		return false
107	}
108	strTemp := str
109	if strings.Contains(str, ":") && !strings.Contains(str, "://") {
110		// support no indicated urlscheme but with colon for port number
111		// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
112		strTemp = "http://" + str
113	}
114	u, err := url.Parse(strTemp)
115	if err != nil {
116		return false
117	}
118	if strings.HasPrefix(u.Host, ".") {
119		return false
120	}
121	if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
122		return false
123	}
124	return rxURL.MatchString(str)
125}
126
127// IsRequestURL check if the string rawurl, assuming
128// it was received in an HTTP request, is a valid
129// URL confirm to RFC 3986
130func IsRequestURL(rawurl string) bool {
131	url, err := url.ParseRequestURI(rawurl)
132	if err != nil {
133		return false //Couldn't even parse the rawurl
134	}
135	if len(url.Scheme) == 0 {
136		return false //No Scheme found
137	}
138	return true
139}
140
141// IsRequestURI check if the string rawurl, assuming
142// it was received in an HTTP request, is an
143// absolute URI or an absolute path.
144func IsRequestURI(rawurl string) bool {
145	_, err := url.ParseRequestURI(rawurl)
146	return err == nil
147}
148
149// IsAlpha check if the string contains only letters (a-zA-Z). Empty string is valid.
150func IsAlpha(str string) bool {
151	if IsNull(str) {
152		return true
153	}
154	return rxAlpha.MatchString(str)
155}
156
157//IsUTFLetter check if the string contains only unicode letter characters.
158//Similar to IsAlpha but for all languages. Empty string is valid.
159func IsUTFLetter(str string) bool {
160	if IsNull(str) {
161		return true
162	}
163
164	for _, c := range str {
165		if !unicode.IsLetter(c) {
166			return false
167		}
168	}
169	return true
170
171}
172
173// IsAlphanumeric check if the string contains only letters and numbers. Empty string is valid.
174func IsAlphanumeric(str string) bool {
175	if IsNull(str) {
176		return true
177	}
178	return rxAlphanumeric.MatchString(str)
179}
180
181// IsUTFLetterNumeric check if the string contains only unicode letters and numbers. Empty string is valid.
182func IsUTFLetterNumeric(str string) bool {
183	if IsNull(str) {
184		return true
185	}
186	for _, c := range str {
187		if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok
188			return false
189		}
190	}
191	return true
192
193}
194
195// IsNumeric check if the string contains only numbers. Empty string is valid.
196func IsNumeric(str string) bool {
197	if IsNull(str) {
198		return true
199	}
200	return rxNumeric.MatchString(str)
201}
202
203// IsUTFNumeric check if the string contains only unicode numbers of any kind.
204// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
205func IsUTFNumeric(str string) bool {
206	if IsNull(str) {
207		return true
208	}
209	if strings.IndexAny(str, "+-") > 0 {
210		return false
211	}
212	if len(str) > 1 {
213		str = strings.TrimPrefix(str, "-")
214		str = strings.TrimPrefix(str, "+")
215	}
216	for _, c := range str {
217		if !unicode.IsNumber(c) { //numbers && minus sign are ok
218			return false
219		}
220	}
221	return true
222
223}
224
225// IsUTFDigit check if the string contains only unicode radix-10 decimal digits. Empty string is valid.
226func IsUTFDigit(str string) bool {
227	if IsNull(str) {
228		return true
229	}
230	if strings.IndexAny(str, "+-") > 0 {
231		return false
232	}
233	if len(str) > 1 {
234		str = strings.TrimPrefix(str, "-")
235		str = strings.TrimPrefix(str, "+")
236	}
237	for _, c := range str {
238		if !unicode.IsDigit(c) { //digits && minus sign are ok
239			return false
240		}
241	}
242	return true
243
244}
245
246// IsHexadecimal check if the string is a hexadecimal number.
247func IsHexadecimal(str string) bool {
248	return rxHexadecimal.MatchString(str)
249}
250
251// IsHexcolor check if the string is a hexadecimal color.
252func IsHexcolor(str string) bool {
253	return rxHexcolor.MatchString(str)
254}
255
256// IsRGBcolor check if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
257func IsRGBcolor(str string) bool {
258	return rxRGBcolor.MatchString(str)
259}
260
261// IsLowerCase check if the string is lowercase. Empty string is valid.
262func IsLowerCase(str string) bool {
263	if IsNull(str) {
264		return true
265	}
266	return str == strings.ToLower(str)
267}
268
269// IsUpperCase check if the string is uppercase. Empty string is valid.
270func IsUpperCase(str string) bool {
271	if IsNull(str) {
272		return true
273	}
274	return str == strings.ToUpper(str)
275}
276
277// HasLowerCase check if the string contains at least 1 lowercase. Empty string is valid.
278func HasLowerCase(str string) bool {
279	if IsNull(str) {
280		return true
281	}
282	return rxHasLowerCase.MatchString(str)
283}
284
285// HasUpperCase check if the string contians as least 1 uppercase. Empty string is valid.
286func HasUpperCase(str string) bool {
287	if IsNull(str) {
288		return true
289	}
290	return rxHasUpperCase.MatchString(str)
291}
292
293// IsInt check if the string is an integer. Empty string is valid.
294func IsInt(str string) bool {
295	if IsNull(str) {
296		return true
297	}
298	return rxInt.MatchString(str)
299}
300
301// IsFloat check if the string is a float.
302func IsFloat(str string) bool {
303	return str != "" && rxFloat.MatchString(str)
304}
305
306// IsDivisibleBy check if the string is a number that's divisible by another.
307// If second argument is not valid integer or zero, it's return false.
308// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
309func IsDivisibleBy(str, num string) bool {
310	f, _ := ToFloat(str)
311	p := int64(f)
312	q, _ := ToInt(num)
313	if q == 0 {
314		return false
315	}
316	return (p == 0) || (p%q == 0)
317}
318
319// IsNull check if the string is null.
320func IsNull(str string) bool {
321	return len(str) == 0
322}
323
324// HasWhitespaceOnly checks the string only contains whitespace
325func HasWhitespaceOnly(str string) bool {
326    return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str)
327}
328
329// HasWhitespace checks if the string contains any whitespace
330func HasWhitespace(str string) bool {
331    return len(str) > 0 && rxHasWhitespace.MatchString(str)
332}
333
334// IsByteLength check if the string's length (in bytes) falls in a range.
335func IsByteLength(str string, min, max int) bool {
336	return len(str) >= min && len(str) <= max
337}
338
339// IsUUIDv3 check if the string is a UUID version 3.
340func IsUUIDv3(str string) bool {
341	return rxUUID3.MatchString(str)
342}
343
344// IsUUIDv4 check if the string is a UUID version 4.
345func IsUUIDv4(str string) bool {
346	return rxUUID4.MatchString(str)
347}
348
349// IsUUIDv5 check if the string is a UUID version 5.
350func IsUUIDv5(str string) bool {
351	return rxUUID5.MatchString(str)
352}
353
354// IsUUID check if the string is a UUID (version 3, 4 or 5).
355func IsUUID(str string) bool {
356	return rxUUID.MatchString(str)
357}
358
359// IsCreditCard check if the string is a credit card.
360func IsCreditCard(str string) bool {
361	sanitized := notNumberRegexp.ReplaceAllString(str, "")
362	if !rxCreditCard.MatchString(sanitized) {
363		return false
364	}
365	var sum int64
366	var digit string
367	var tmpNum int64
368	var shouldDouble bool
369	for i := len(sanitized) - 1; i >= 0; i-- {
370		digit = sanitized[i:(i + 1)]
371		tmpNum, _ = ToInt(digit)
372		if shouldDouble {
373			tmpNum *= 2
374			if tmpNum >= 10 {
375				sum += ((tmpNum % 10) + 1)
376			} else {
377				sum += tmpNum
378			}
379		} else {
380			sum += tmpNum
381		}
382		shouldDouble = !shouldDouble
383	}
384
385	return sum%10 == 0
386}
387
388// IsISBN10 check if the string is an ISBN version 10.
389func IsISBN10(str string) bool {
390	return IsISBN(str, 10)
391}
392
393// IsISBN13 check if the string is an ISBN version 13.
394func IsISBN13(str string) bool {
395	return IsISBN(str, 13)
396}
397
398// IsISBN check if the string is an ISBN (version 10 or 13).
399// If version value is not equal to 10 or 13, it will be check both variants.
400func IsISBN(str string, version int) bool {
401	sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")
402	var checksum int32
403	var i int32
404	if version == 10 {
405		if !rxISBN10.MatchString(sanitized) {
406			return false
407		}
408		for i = 0; i < 9; i++ {
409			checksum += (i + 1) * int32(sanitized[i]-'0')
410		}
411		if sanitized[9] == 'X' {
412			checksum += 10 * 10
413		} else {
414			checksum += 10 * int32(sanitized[9]-'0')
415		}
416		if checksum%11 == 0 {
417			return true
418		}
419		return false
420	} else if version == 13 {
421		if !rxISBN13.MatchString(sanitized) {
422			return false
423		}
424		factor := []int32{1, 3}
425		for i = 0; i < 12; i++ {
426			checksum += factor[i%2] * int32(sanitized[i]-'0')
427		}
428		return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0
429	}
430	return IsISBN(str, 10) || IsISBN(str, 13)
431}
432
433// IsJSON check if the string is valid JSON (note: uses json.Unmarshal).
434func IsJSON(str string) bool {
435	var js json.RawMessage
436	return json.Unmarshal([]byte(str), &js) == nil
437}
438
439// IsMultibyte check if the string contains one or more multibyte chars. Empty string is valid.
440func IsMultibyte(str string) bool {
441	if IsNull(str) {
442		return true
443	}
444	return rxMultibyte.MatchString(str)
445}
446
447// IsASCII check if the string contains ASCII chars only. Empty string is valid.
448func IsASCII(str string) bool {
449	if IsNull(str) {
450		return true
451	}
452	return rxASCII.MatchString(str)
453}
454
455// IsPrintableASCII check if the string contains printable ASCII chars only. Empty string is valid.
456func IsPrintableASCII(str string) bool {
457	if IsNull(str) {
458		return true
459	}
460	return rxPrintableASCII.MatchString(str)
461}
462
463// IsFullWidth check if the string contains any full-width chars. Empty string is valid.
464func IsFullWidth(str string) bool {
465	if IsNull(str) {
466		return true
467	}
468	return rxFullWidth.MatchString(str)
469}
470
471// IsHalfWidth check if the string contains any half-width chars. Empty string is valid.
472func IsHalfWidth(str string) bool {
473	if IsNull(str) {
474		return true
475	}
476	return rxHalfWidth.MatchString(str)
477}
478
479// IsVariableWidth check if the string contains a mixture of full and half-width chars. Empty string is valid.
480func IsVariableWidth(str string) bool {
481	if IsNull(str) {
482		return true
483	}
484	return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str)
485}
486
487// IsBase64 check if a string is base64 encoded.
488func IsBase64(str string) bool {
489	return rxBase64.MatchString(str)
490}
491
492// IsFilePath check is a string is Win or Unix file path and returns it's type.
493func IsFilePath(str string) (bool, int) {
494	if rxWinPath.MatchString(str) {
495		//check windows path limit see:
496		//  http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
497		if len(str[3:]) > 32767 {
498			return false, Win
499		}
500		return true, Win
501	} else if rxUnixPath.MatchString(str) {
502		return true, Unix
503	}
504	return false, Unknown
505}
506
507// IsDataURI checks if a string is base64 encoded data URI such as an image
508func IsDataURI(str string) bool {
509	dataURI := strings.Split(str, ",")
510	if !rxDataURI.MatchString(dataURI[0]) {
511		return false
512	}
513	return IsBase64(dataURI[1])
514}
515
516// IsISO3166Alpha2 checks if a string is valid two-letter country code
517func IsISO3166Alpha2(str string) bool {
518	for _, entry := range ISO3166List {
519		if str == entry.Alpha2Code {
520			return true
521		}
522	}
523	return false
524}
525
526// IsISO3166Alpha3 checks if a string is valid three-letter country code
527func IsISO3166Alpha3(str string) bool {
528	for _, entry := range ISO3166List {
529		if str == entry.Alpha3Code {
530			return true
531		}
532	}
533	return false
534}
535
536// IsISO693Alpha2 checks if a string is valid two-letter language code
537func IsISO693Alpha2(str string) bool {
538	for _, entry := range ISO693List {
539		if str == entry.Alpha2Code {
540			return true
541		}
542	}
543	return false
544}
545
546// IsISO693Alpha3b checks if a string is valid three-letter language code
547func IsISO693Alpha3b(str string) bool {
548	for _, entry := range ISO693List {
549		if str == entry.Alpha3bCode {
550			return true
551		}
552	}
553	return false
554}
555
556// IsDNSName will validate the given string as a DNS name
557func IsDNSName(str string) bool {
558	if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 {
559		// constraints already violated
560		return false
561	}
562	return !IsIP(str) && rxDNSName.MatchString(str)
563}
564
565// IsHash checks if a string is a hash of type algorithm.
566// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b']
567func IsHash(str string, algorithm string) bool {
568	len := "0"
569	algo := strings.ToLower(algorithm)
570
571	if algo == "crc32" || algo == "crc32b" {
572		len = "8"
573	} else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" {
574		len = "32"
575	} else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" {
576		len = "40"
577	} else if algo == "tiger192" {
578		len = "48"
579	} else if algo == "sha256" {
580		len = "64"
581	} else if algo == "sha384" {
582		len = "96"
583	} else if algo == "sha512" {
584		len = "128"
585	} else {
586		return false
587	}
588
589	return Matches(str, "^[a-f0-9]{"+len+"}$")
590}
591
592// IsDialString validates the given string for usage with the various Dial() functions
593func IsDialString(str string) bool {
594
595	if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) {
596		return true
597	}
598
599	return false
600}
601
602// IsIP checks if a string is either IP version 4 or 6.
603func IsIP(str string) bool {
604	return net.ParseIP(str) != nil
605}
606
607// IsPort checks if a string represents a valid port
608func IsPort(str string) bool {
609	if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 {
610		return true
611	}
612	return false
613}
614
615// IsIPv4 check if the string is an IP version 4.
616func IsIPv4(str string) bool {
617	ip := net.ParseIP(str)
618	return ip != nil && strings.Contains(str, ".")
619}
620
621// IsIPv6 check if the string is an IP version 6.
622func IsIPv6(str string) bool {
623	ip := net.ParseIP(str)
624	return ip != nil && strings.Contains(str, ":")
625}
626
627// IsCIDR check if the string is an valid CIDR notiation (IPV4 & IPV6)
628func IsCIDR(str string) bool {
629	_, _, err := net.ParseCIDR(str)
630	return err == nil
631}
632
633// IsMAC check if a string is valid MAC address.
634// Possible MAC formats:
635// 01:23:45:67:89:ab
636// 01:23:45:67:89:ab:cd:ef
637// 01-23-45-67-89-ab
638// 01-23-45-67-89-ab-cd-ef
639// 0123.4567.89ab
640// 0123.4567.89ab.cdef
641func IsMAC(str string) bool {
642	_, err := net.ParseMAC(str)
643	return err == nil
644}
645
646// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name
647func IsHost(str string) bool {
648	return IsIP(str) || IsDNSName(str)
649}
650
651// IsMongoID check if the string is a valid hex-encoded representation of a MongoDB ObjectId.
652func IsMongoID(str string) bool {
653	return rxHexadecimal.MatchString(str) && (len(str) == 24)
654}
655
656// IsLatitude check if a string is valid latitude.
657func IsLatitude(str string) bool {
658	return rxLatitude.MatchString(str)
659}
660
661// IsLongitude check if a string is valid longitude.
662func IsLongitude(str string) bool {
663	return rxLongitude.MatchString(str)
664}
665
666// IsRsaPublicKey check if a string is valid public key with provided length
667func IsRsaPublicKey(str string, keylen int) bool {
668	bb := bytes.NewBufferString(str)
669	pemBytes, err := ioutil.ReadAll(bb)
670	if err != nil {
671		return false
672	}
673	block, _ := pem.Decode(pemBytes)
674	if block != nil && block.Type != "PUBLIC KEY" {
675		return false
676	}
677	var der []byte
678
679	if block != nil {
680		der = block.Bytes
681	} else {
682		der, err = base64.StdEncoding.DecodeString(str)
683		if err != nil {
684			return false
685		}
686	}
687
688	key, err := x509.ParsePKIXPublicKey(der)
689	if err != nil {
690		return false
691	}
692	pubkey, ok := key.(*rsa.PublicKey)
693	if !ok {
694		return false
695	}
696	bitlen := len(pubkey.N.Bytes()) * 8
697	return bitlen == int(keylen)
698}
699
700func toJSONName(tag string) string {
701	if tag == "" {
702		return ""
703	}
704
705	// JSON name always comes first. If there's no options then split[0] is
706	// JSON name, if JSON name is not set, then split[0] is an empty string.
707	split := strings.SplitN(tag, ",", 2)
708
709	name := split[0]
710
711	// However it is possible that the field is skipped when
712	// (de-)serializing from/to JSON, in which case assume that there is no
713	// tag name to use
714	if name == "-" {
715		return ""
716	}
717	return name
718}
719
720func PrependPathToErrors(err error, path string) error {
721	switch err2 := err.(type) {
722	case Error:
723		err2.Path = append([]string{path}, err2.Path...)
724		return err2
725	case Errors:
726		errors := err2.Errors()
727		for i, err3 := range errors {
728			errors[i] = PrependPathToErrors(err3, path)
729		}
730		return err2
731	}
732	fmt.Println(err)
733	return err
734}
735
736// ValidateStruct use tags for fields.
737// result will be equal to `false` if there are any errors.
738func ValidateStruct(s interface{}) (bool, error) {
739	if s == nil {
740		return true, nil
741	}
742	result := true
743	var err error
744	val := reflect.ValueOf(s)
745	if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
746		val = val.Elem()
747	}
748	// we only accept structs
749	if val.Kind() != reflect.Struct {
750		return false, fmt.Errorf("function only accepts structs; got %s", val.Kind())
751	}
752	var errs Errors
753	for i := 0; i < val.NumField(); i++ {
754		valueField := val.Field(i)
755		typeField := val.Type().Field(i)
756		if typeField.PkgPath != "" {
757			continue // Private field
758		}
759		structResult := true
760		if valueField.Kind() == reflect.Interface {
761			valueField = valueField.Elem()
762		}
763		if (valueField.Kind() == reflect.Struct ||
764			(valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) &&
765			typeField.Tag.Get(tagName) != "-" {
766			var err error
767			structResult, err = ValidateStruct(valueField.Interface())
768			if err != nil {
769				err = PrependPathToErrors(err, typeField.Name)
770				errs = append(errs, err)
771			}
772		}
773		resultField, err2 := typeCheck(valueField, typeField, val, nil)
774		if err2 != nil {
775
776			// Replace structure name with JSON name if there is a tag on the variable
777			jsonTag := toJSONName(typeField.Tag.Get("json"))
778			if jsonTag != "" {
779				switch jsonError := err2.(type) {
780				case Error:
781					jsonError.Name = jsonTag
782					err2 = jsonError
783				case Errors:
784					for i2, err3 := range jsonError {
785						switch customErr := err3.(type) {
786						case Error:
787							customErr.Name = jsonTag
788							jsonError[i2] = customErr
789						}
790					}
791
792					err2 = jsonError
793				}
794			}
795
796			errs = append(errs, err2)
797		}
798		result = result && resultField && structResult
799	}
800	if len(errs) > 0 {
801		err = errs
802	}
803	return result, err
804}
805
806// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
807func parseTagIntoMap(tag string) tagOptionsMap {
808	optionsMap := make(tagOptionsMap)
809	options := strings.Split(tag, ",")
810
811	for i, option := range options {
812		option = strings.TrimSpace(option)
813
814		validationOptions := strings.Split(option, "~")
815		if !isValidTag(validationOptions[0]) {
816			continue
817		}
818		if len(validationOptions) == 2 {
819			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i}
820		} else {
821			optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i}
822		}
823	}
824	return optionsMap
825}
826
827func isValidTag(s string) bool {
828	if s == "" {
829		return false
830	}
831	for _, c := range s {
832		switch {
833		case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
834			// Backslash and quote chars are reserved, but
835			// otherwise any punctuation chars are allowed
836			// in a tag name.
837		default:
838			if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
839				return false
840			}
841		}
842	}
843	return true
844}
845
846// IsSSN will validate the given string as a U.S. Social Security Number
847func IsSSN(str string) bool {
848	if str == "" || len(str) != 11 {
849		return false
850	}
851	return rxSSN.MatchString(str)
852}
853
854// IsSemver check if string is valid semantic version
855func IsSemver(str string) bool {
856	return rxSemver.MatchString(str)
857}
858
859// IsTime check if string is valid according to given format
860func IsTime(str string, format string) bool {
861	_, err := time.Parse(format, str)
862	return err == nil
863}
864
865// IsRFC3339 check if string is valid timestamp value according to RFC3339
866func IsRFC3339(str string) bool {
867	return IsTime(str, time.RFC3339)
868}
869
870// IsRFC3339WithoutZone check if string is valid timestamp value according to RFC3339 which excludes the timezone.
871func IsRFC3339WithoutZone(str string) bool {
872	return IsTime(str, RF3339WithoutZone)
873}
874
875// IsISO4217 check if string is valid ISO currency code
876func IsISO4217(str string) bool {
877	for _, currency := range ISO4217List {
878		if str == currency {
879			return true
880		}
881	}
882
883	return false
884}
885
886// ByteLength check string's length
887func ByteLength(str string, params ...string) bool {
888	if len(params) == 2 {
889		min, _ := ToInt(params[0])
890		max, _ := ToInt(params[1])
891		return len(str) >= int(min) && len(str) <= int(max)
892	}
893
894	return false
895}
896
897// RuneLength check string's length
898// Alias for StringLength
899func RuneLength(str string, params ...string) bool {
900	return StringLength(str, params...)
901}
902
903// IsRsaPub check whether string is valid RSA key
904// Alias for IsRsaPublicKey
905func IsRsaPub(str string, params ...string) bool {
906	if len(params) == 1 {
907		len, _ := ToInt(params[0])
908		return IsRsaPublicKey(str, int(len))
909	}
910
911	return false
912}
913
914// StringMatches checks if a string matches a given pattern.
915func StringMatches(s string, params ...string) bool {
916	if len(params) == 1 {
917		pattern := params[0]
918		return Matches(s, pattern)
919	}
920	return false
921}
922
923// StringLength check string's length (including multi byte strings)
924func StringLength(str string, params ...string) bool {
925
926	if len(params) == 2 {
927		strLength := utf8.RuneCountInString(str)
928		min, _ := ToInt(params[0])
929		max, _ := ToInt(params[1])
930		return strLength >= int(min) && strLength <= int(max)
931	}
932
933	return false
934}
935
936// Range check string's length
937func Range(str string, params ...string) bool {
938	if len(params) == 2 {
939		value, _ := ToFloat(str)
940		min, _ := ToFloat(params[0])
941		max, _ := ToFloat(params[1])
942		return InRange(value, min, max)
943	}
944
945	return false
946}
947
948func isInRaw(str string, params ...string) bool {
949	if len(params) == 1 {
950		rawParams := params[0]
951
952		parsedParams := strings.Split(rawParams, "|")
953
954		return IsIn(str, parsedParams...)
955	}
956
957	return false
958}
959
960// IsIn check if string str is a member of the set of strings params
961func IsIn(str string, params ...string) bool {
962	for _, param := range params {
963		if str == param {
964			return true
965		}
966	}
967
968	return false
969}
970
971func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) {
972	if nilPtrAllowedByRequired {
973		k := v.Kind()
974		if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() {
975			return true, nil
976		}
977	}
978
979	if requiredOption, isRequired := options["required"]; isRequired {
980		if len(requiredOption.customErrorMessage) > 0 {
981			return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}}
982		}
983		return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}}
984	} else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional {
985		return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}}
986	}
987	// not required and empty is valid
988	return true, nil
989}
990
991func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) {
992	if !v.IsValid() {
993		return false, nil
994	}
995
996	tag := t.Tag.Get(tagName)
997
998	// Check if the field should be ignored
999	switch tag {
1000	case "":
1001		if v.Kind() != reflect.Slice && v.Kind() != reflect.Map {
1002			if !fieldsRequiredByDefault {
1003				return true, nil
1004			}
1005			return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}}
1006		}
1007	case "-":
1008		return true, nil
1009	}
1010
1011	isRootType := false
1012	if options == nil {
1013		isRootType = true
1014		options = parseTagIntoMap(tag)
1015	}
1016
1017	if isEmptyValue(v) {
1018		// an empty value is not validated, check only required
1019		isValid, resultErr = checkRequired(v, t, options)
1020		for key := range options {
1021			delete(options, key)
1022		}
1023		return isValid, resultErr
1024	}
1025
1026	var customTypeErrors Errors
1027	optionsOrder := options.orderedKeys()
1028	for _, validatorName := range optionsOrder {
1029		validatorStruct := options[validatorName]
1030		if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok {
1031			delete(options, validatorName)
1032
1033			if result := validatefunc(v.Interface(), o.Interface()); !result {
1034				if len(validatorStruct.customErrorMessage) > 0 {
1035					customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)})
1036					continue
1037				}
1038				customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)})
1039			}
1040		}
1041	}
1042
1043	if len(customTypeErrors.Errors()) > 0 {
1044		return false, customTypeErrors
1045	}
1046
1047	if isRootType {
1048		// Ensure that we've checked the value by all specified validators before report that the value is valid
1049		defer func() {
1050			delete(options, "optional")
1051			delete(options, "required")
1052
1053			if isValid && resultErr == nil && len(options) != 0 {
1054				optionsOrder := options.orderedKeys()
1055				for _, validator := range optionsOrder {
1056					isValid = false
1057					resultErr = Error{t.Name, fmt.Errorf(
1058						"The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}}
1059					return
1060				}
1061			}
1062		}()
1063	}
1064
1065	switch v.Kind() {
1066	case reflect.Bool,
1067		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
1068		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
1069		reflect.Float32, reflect.Float64,
1070		reflect.String:
1071		// for each tag option check the map of validator functions
1072		for _, validatorSpec := range optionsOrder {
1073			validatorStruct := options[validatorSpec]
1074			var negate bool
1075			validator := validatorSpec
1076			customMsgExists := len(validatorStruct.customErrorMessage) > 0
1077
1078			// Check whether the tag looks like '!something' or 'something'
1079			if validator[0] == '!' {
1080				validator = validator[1:]
1081				negate = true
1082			}
1083
1084			// Check for param validators
1085			for key, value := range ParamTagRegexMap {
1086				ps := value.FindStringSubmatch(validator)
1087				if len(ps) == 0 {
1088					continue
1089				}
1090
1091				validatefunc, ok := ParamTagMap[key]
1092				if !ok {
1093					continue
1094				}
1095
1096				delete(options, validatorSpec)
1097
1098				switch v.Kind() {
1099				case reflect.String,
1100					reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
1101					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
1102					reflect.Float32, reflect.Float64:
1103
1104					field := fmt.Sprint(v) // make value into string, then validate with regex
1105					if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) {
1106						if customMsgExists {
1107							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1108						}
1109						if negate {
1110							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1111						}
1112						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1113					}
1114				default:
1115					// type not yet supported, fail
1116					return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}}
1117				}
1118			}
1119
1120			if validatefunc, ok := TagMap[validator]; ok {
1121				delete(options, validatorSpec)
1122
1123				switch v.Kind() {
1124                case reflect.String,
1125                    reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
1126                    reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
1127                    reflect.Float32, reflect.Float64:
1128					field := fmt.Sprint(v) // make value into string, then validate with regex
1129					if result := validatefunc(field); !result && !negate || result && negate {
1130						if customMsgExists {
1131							return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1132						}
1133						if negate {
1134							return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1135						}
1136						return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}}
1137					}
1138				default:
1139					//Not Yet Supported Types (Fail here!)
1140					err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v)
1141					return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}}
1142				}
1143			}
1144		}
1145		return true, nil
1146	case reflect.Map:
1147		if v.Type().Key().Kind() != reflect.String {
1148			return false, &UnsupportedTypeError{v.Type()}
1149		}
1150		var sv stringValues
1151		sv = v.MapKeys()
1152		sort.Sort(sv)
1153		result := true
1154		for i, k := range sv {
1155			var resultItem bool
1156			var err error
1157			if v.MapIndex(k).Kind() != reflect.Struct {
1158				resultItem, err = typeCheck(v.MapIndex(k), t, o, options)
1159				if err != nil {
1160					return false, err
1161				}
1162			} else {
1163				resultItem, err = ValidateStruct(v.MapIndex(k).Interface())
1164				if err != nil {
1165					err = PrependPathToErrors(err, t.Name+"."+sv[i].Interface().(string))
1166					return false, err
1167				}
1168			}
1169			result = result && resultItem
1170		}
1171		return result, nil
1172	case reflect.Slice, reflect.Array:
1173		result := true
1174		for i := 0; i < v.Len(); i++ {
1175			var resultItem bool
1176			var err error
1177			if v.Index(i).Kind() != reflect.Struct {
1178				resultItem, err = typeCheck(v.Index(i), t, o, options)
1179				if err != nil {
1180					return false, err
1181				}
1182			} else {
1183				resultItem, err = ValidateStruct(v.Index(i).Interface())
1184				if err != nil {
1185					err = PrependPathToErrors(err, t.Name+"."+strconv.Itoa(i))
1186					return false, err
1187				}
1188			}
1189			result = result && resultItem
1190		}
1191		return result, nil
1192	case reflect.Interface:
1193		// If the value is an interface then encode its element
1194		if v.IsNil() {
1195			return true, nil
1196		}
1197		return ValidateStruct(v.Interface())
1198	case reflect.Ptr:
1199		// If the value is a pointer then check its element
1200		if v.IsNil() {
1201			return true, nil
1202		}
1203		return typeCheck(v.Elem(), t, o, options)
1204	case reflect.Struct:
1205		return ValidateStruct(v.Interface())
1206	default:
1207		return false, &UnsupportedTypeError{v.Type()}
1208	}
1209}
1210
1211func stripParams(validatorString string) string {
1212	return paramsRegexp.ReplaceAllString(validatorString, "")
1213}
1214
1215func isEmptyValue(v reflect.Value) bool {
1216	switch v.Kind() {
1217	case reflect.String, reflect.Array:
1218		return v.Len() == 0
1219	case reflect.Map, reflect.Slice:
1220		return v.Len() == 0 || v.IsNil()
1221	case reflect.Bool:
1222		return !v.Bool()
1223	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
1224		return v.Int() == 0
1225	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
1226		return v.Uint() == 0
1227	case reflect.Float32, reflect.Float64:
1228		return v.Float() == 0
1229	case reflect.Interface, reflect.Ptr:
1230		return v.IsNil()
1231	}
1232
1233	return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
1234}
1235
1236// ErrorByField returns error for specified field of the struct
1237// validated by ValidateStruct or empty string if there are no errors
1238// or this field doesn't exists or doesn't have any errors.
1239func ErrorByField(e error, field string) string {
1240	if e == nil {
1241		return ""
1242	}
1243	return ErrorsByField(e)[field]
1244}
1245
1246// ErrorsByField returns map of errors of the struct validated
1247// by ValidateStruct or empty map if there are no errors.
1248func ErrorsByField(e error) map[string]string {
1249	m := make(map[string]string)
1250	if e == nil {
1251		return m
1252	}
1253	// prototype for ValidateStruct
1254
1255	switch e.(type) {
1256	case Error:
1257		m[e.(Error).Name] = e.(Error).Err.Error()
1258	case Errors:
1259		for _, item := range e.(Errors).Errors() {
1260			n := ErrorsByField(item)
1261			for k, v := range n {
1262				m[k] = v
1263			}
1264		}
1265	}
1266
1267	return m
1268}
1269
1270// Error returns string equivalent for reflect.Type
1271func (e *UnsupportedTypeError) Error() string {
1272	return "validator: unsupported type: " + e.Type.String()
1273}
1274
1275func (sv stringValues) Len() int           { return len(sv) }
1276func (sv stringValues) Swap(i, j int)      { sv[i], sv[j] = sv[j], sv[i] }
1277func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
1278func (sv stringValues) get(i int) string   { return sv[i].String() }
1279