2024-02-18 10:42:21 +00:00
// Package govalidator is package of validators and sanitizers for strings, structs and collections.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
package govalidator
import (
"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net"
"net/url"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
)
var (
fieldsRequiredByDefault bool
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
nilPtrAllowedByRequired = false
2024-07-24 23:45:04 +00:00
notNumberRegexp = regexp . MustCompile ( "[^0-9]+" )
whiteSpacesAndMinus = regexp . MustCompile ( ` [\s-]+ ` )
paramsRegexp = regexp . MustCompile ( ` \(.*\)$ ` )
2024-02-18 10:42:21 +00:00
)
const maxURLRuneCount = 2083
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
const minURLRuneCount = 3
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
const RF3339WithoutZone = "2006-01-02T15:04:05"
// SetFieldsRequiredByDefault causes validation to fail when struct fields
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
//
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// type exampleStruct struct {
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Name string ``
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Email string `valid:"email"`
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
//
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// This, however, will only fail when Email is empty or an invalid email address:
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
//
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// type exampleStruct2 struct {
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Name string `valid:"-"`
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Email string `valid:"email"`
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
//
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Lastly, this will only fail when Email is an invalid email address but not when it's empty:
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
//
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// type exampleStruct2 struct {
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Name string `valid:"-"`
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Email string `valid:"email,optional"`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func SetFieldsRequiredByDefault ( value bool ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
fieldsRequiredByDefault = value
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// The validation will still reject ptr fields in their zero value state. Example with this enabled:
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
//
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// type exampleStruct struct {
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Name *string `valid:"required"`
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
//
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// With `Name` set to "", this will be considered invalid input and will cause a validation error.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// With `Name` set to nil, this will be considered valid by validation.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// By default this is disabled.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func SetNilPtrAllowedByRequired ( value bool ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
nilPtrAllowedByRequired = value
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsEmail check if the string is an email.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsEmail ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// TODO uppercase letters are not supported
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxEmail . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsExistingEmail check if the string is an email of existing domain
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsExistingEmail ( email string ) bool {
if len ( email ) < 6 || len ( email ) > 254 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
at := strings . LastIndex ( email , "@" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if at <= 0 || at > len ( email ) - 3 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
user := email [ : at ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
host := email [ at + 1 : ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( user ) > 64 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if userDotRegexp . MatchString ( user ) || ! userRegexp . MatchString ( user ) || ! hostRegexp . MatchString ( host ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch host {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case "localhost" , "example.com" :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if _ , err := net . LookupMX ( host ) ; err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if _ , err := net . LookupIP ( host ) ; err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsURL check if the string is an URL.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsURL ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == "" || utf8 . RuneCountInString ( str ) >= maxURLRuneCount || len ( str ) <= minURLRuneCount || strings . HasPrefix ( str , "." ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
strTemp := str
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if strings . Contains ( str , ":" ) && ! strings . Contains ( str , "://" ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// support no indicated urlscheme but with colon for port number
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
strTemp = "http://" + str
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
u , err := url . Parse ( strTemp )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if strings . HasPrefix ( u . Host , "." ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if u . Host == "" && ( u . Path != "" && ! strings . Contains ( u . Path , "." ) ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxURL . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRequestURL check if the string rawurl, assuming
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// it was received in an HTTP request, is a valid
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// URL confirm to RFC 3986
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRequestURL ( rawurl string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
url , err := url . ParseRequestURI ( rawurl )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false //Couldn't even parse the rawurl
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( url . Scheme ) == 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false //No Scheme found
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRequestURI check if the string rawurl, assuming
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// it was received in an HTTP request, is an
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// absolute URI or an absolute path.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRequestURI ( rawurl string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
_ , err := url . ParseRequestURI ( rawurl )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return err == nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsAlpha check if the string contains only letters (a-zA-Z). Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsAlpha ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxAlpha . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-06-14 08:41:36 +00:00
// IsUTFLetter check if the string contains only unicode letter characters.
2024-07-24 23:45:04 +00:00
2024-06-14 08:41:36 +00:00
// Similar to IsAlpha but for all languages. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUTFLetter ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
for _ , c := range str {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! unicode . IsLetter ( c ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
}
// IsAlphanumeric check if the string contains only letters and numbers. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsAlphanumeric ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxAlphanumeric . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUTFLetterNumeric check if the string contains only unicode letters and numbers. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUTFLetterNumeric ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , c := range str {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! unicode . IsLetter ( c ) && ! unicode . IsNumber ( c ) { //letters && numbers are ok
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
}
// IsNumeric check if the string contains only numbers. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsNumeric ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxNumeric . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUTFNumeric check if the string contains only unicode numbers of any kind.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUTFNumeric ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if strings . IndexAny ( str , "+-" ) > 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( str ) > 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
str = strings . TrimPrefix ( str , "-" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
str = strings . TrimPrefix ( str , "+" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , c := range str {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! unicode . IsNumber ( c ) { //numbers && minus sign are ok
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
}
// IsUTFDigit check if the string contains only unicode radix-10 decimal digits. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUTFDigit ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if strings . IndexAny ( str , "+-" ) > 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( str ) > 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
str = strings . TrimPrefix ( str , "-" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
str = strings . TrimPrefix ( str , "+" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , c := range str {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! unicode . IsDigit ( c ) { //digits && minus sign are ok
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
}
// IsHexadecimal check if the string is a hexadecimal number.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsHexadecimal ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxHexadecimal . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsHexcolor check if the string is a hexadecimal color.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsHexcolor ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxHexcolor . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRGBcolor check if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRGBcolor ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxRGBcolor . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsLowerCase check if the string is lowercase. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsLowerCase ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return str == strings . ToLower ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUpperCase check if the string is uppercase. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUpperCase ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return str == strings . ToUpper ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// HasLowerCase check if the string contains at least 1 lowercase. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func HasLowerCase ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxHasLowerCase . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// HasUpperCase check if the string contians as least 1 uppercase. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func HasUpperCase ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxHasUpperCase . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsInt check if the string is an integer. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsInt ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxInt . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsFloat check if the string is a float.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsFloat ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return str != "" && rxFloat . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsDivisibleBy check if the string is a number that's divisible by another.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// If second argument is not valid integer or zero, it's return false.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsDivisibleBy ( str , num string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
f , _ := ToFloat ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
p := int64 ( f )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
q , _ := ToInt ( num )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if q == 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ( p == 0 ) || ( p % q == 0 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsNull check if the string is null.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsNull ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return len ( str ) == 0
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsNotNull check if the string is not null.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsNotNull ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ! IsNull ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// HasWhitespaceOnly checks the string only contains whitespace
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func HasWhitespaceOnly ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return len ( str ) > 0 && rxHasWhitespaceOnly . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// HasWhitespace checks if the string contains any whitespace
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func HasWhitespace ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return len ( str ) > 0 && rxHasWhitespace . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsByteLength check if the string's length (in bytes) falls in a range.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsByteLength ( str string , min , max int ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return len ( str ) >= min && len ( str ) <= max
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUUIDv3 check if the string is a UUID version 3.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUUIDv3 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxUUID3 . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUUIDv4 check if the string is a UUID version 4.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUUIDv4 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxUUID4 . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUUIDv5 check if the string is a UUID version 5.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUUIDv5 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxUUID5 . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUUID check if the string is a UUID (version 3, 4 or 5).
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUUID ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxUUID . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsCreditCard check if the string is a credit card.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsCreditCard ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
sanitized := notNumberRegexp . ReplaceAllString ( str , "" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! rxCreditCard . MatchString ( sanitized ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var sum int64
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var digit string
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var tmpNum int64
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var shouldDouble bool
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i := len ( sanitized ) - 1 ; i >= 0 ; i -- {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
digit = sanitized [ i : ( i + 1 ) ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
tmpNum , _ = ToInt ( digit )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if shouldDouble {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
tmpNum *= 2
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if tmpNum >= 10 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
sum += ( ( tmpNum % 10 ) + 1 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
sum += tmpNum
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
sum += tmpNum
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
shouldDouble = ! shouldDouble
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return sum % 10 == 0
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISBN10 check if the string is an ISBN version 10.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISBN10 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsISBN ( str , 10 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISBN13 check if the string is an ISBN version 13.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISBN13 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsISBN ( str , 13 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISBN check if the string is an ISBN (version 10 or 13).
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// If version value is not equal to 10 or 13, it will be check both variants.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISBN ( str string , version int ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
sanitized := whiteSpacesAndMinus . ReplaceAllString ( str , "" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var checksum int32
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var i int32
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if version == 10 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! rxISBN10 . MatchString ( sanitized ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i = 0 ; i < 9 ; i ++ {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
checksum += ( i + 1 ) * int32 ( sanitized [ i ] - '0' )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if sanitized [ 9 ] == 'X' {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
checksum += 10 * 10
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
checksum += 10 * int32 ( sanitized [ 9 ] - '0' )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if checksum % 11 == 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if version == 13 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! rxISBN13 . MatchString ( sanitized ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
factor := [ ] int32 { 1 , 3 }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i = 0 ; i < 12 ; i ++ {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
checksum += factor [ i % 2 ] * int32 ( sanitized [ i ] - '0' )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ( int32 ( sanitized [ 12 ] - '0' ) ) - ( ( 10 - ( checksum % 10 ) ) % 10 ) == 0
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsISBN ( str , 10 ) || IsISBN ( str , 13 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsJSON check if the string is valid JSON (note: uses json.Unmarshal).
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsJSON ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var js json . RawMessage
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return json . Unmarshal ( [ ] byte ( str ) , & js ) == nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsMultibyte check if the string contains one or more multibyte chars. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsMultibyte ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxMultibyte . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsASCII check if the string contains ASCII chars only. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsASCII ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxASCII . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsPrintableASCII check if the string contains printable ASCII chars only. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsPrintableASCII ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxPrintableASCII . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsFullWidth check if the string contains any full-width chars. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsFullWidth ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxFullWidth . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsHalfWidth check if the string contains any half-width chars. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsHalfWidth ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxHalfWidth . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsVariableWidth check if the string contains a mixture of full and half-width chars. Empty string is valid.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsVariableWidth ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if IsNull ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxHalfWidth . MatchString ( str ) && rxFullWidth . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsBase64 check if a string is base64 encoded.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsBase64 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxBase64 . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsFilePath check is a string is Win or Unix file path and returns it's type.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsFilePath ( str string ) ( bool , int ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if rxWinPath . MatchString ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
//check windows path limit see:
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( str [ 3 : ] ) > 32767 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Win
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , Win
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if rxUnixPath . MatchString ( str ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , Unix
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Unknown
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsDataURI checks if a string is base64 encoded data URI such as an image
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsDataURI ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
dataURI := strings . Split ( str , "," )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! rxDataURI . MatchString ( dataURI [ 0 ] ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsBase64 ( dataURI [ 1 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsMagnetURI checks if a string is valid magnet URI
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsMagnetURI ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxMagnetURI . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISO3166Alpha2 checks if a string is valid two-letter country code
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISO3166Alpha2 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , entry := range ISO3166List {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == entry . Alpha2Code {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISO3166Alpha3 checks if a string is valid three-letter country code
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISO3166Alpha3 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , entry := range ISO3166List {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == entry . Alpha3Code {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISO693Alpha2 checks if a string is valid two-letter language code
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISO693Alpha2 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , entry := range ISO693List {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == entry . Alpha2Code {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISO693Alpha3b checks if a string is valid three-letter language code
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISO693Alpha3b ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , entry := range ISO693List {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == entry . Alpha3bCode {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsDNSName will validate the given string as a DNS name
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsDNSName ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == "" || len ( strings . Replace ( str , "." , "" , - 1 ) ) > 255 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// constraints already violated
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ! IsIP ( str ) && rxDNSName . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsHash checks if a string is a hash of type algorithm.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b']
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsHash ( str string , algorithm string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len := "0"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
algo := strings . ToLower ( algorithm )
if algo == "crc32" || algo == "crc32b" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len = "8"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len = "32"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len = "40"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if algo == "tiger192" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len = "48"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if algo == "sha256" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len = "64"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if algo == "sha384" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len = "96"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if algo == "sha512" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len = "128"
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return Matches ( str , "^[a-f0-9]{" + len + "}$" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsSHA512 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "sha512" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsSHA384 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "sha384" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsSHA256 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "sha256" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsTiger192 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "tiger192" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsTiger160 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "tiger160" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRipeMD160 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "ripemd160" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsSHA1 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "sha1" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsTiger128 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "tiger128" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRipeMD128 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "ripemd128" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsCRC32 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "crc32" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsCRC32b ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "crc32b" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsMD5 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "md5" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsMD4 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsHash ( str , "md4" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsDialString validates the given string for usage with the various Dial() functions
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsDialString ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if h , p , err := net . SplitHostPort ( str ) ; err == nil && h != "" && p != "" && ( IsDNSName ( h ) || IsIP ( h ) ) && IsPort ( p ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP`
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsIP ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return net . ParseIP ( str ) != nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsPort checks if a string represents a valid port
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsPort ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if i , err := strconv . Atoi ( str ) ; err == nil && i > 0 && i < 65536 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsIPv4 check if the string is an IP version 4.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsIPv4 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
ip := net . ParseIP ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ip != nil && strings . Contains ( str , "." )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsIPv6 check if the string is an IP version 6.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsIPv6 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
ip := net . ParseIP ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ip != nil && strings . Contains ( str , ":" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsCIDR check if the string is an valid CIDR notiation (IPV4 & IPV6)
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsCIDR ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
_ , _ , err := net . ParseCIDR ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return err == nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsMAC check if a string is valid MAC address.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Possible MAC formats:
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// 01:23:45:67:89:ab
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// 01:23:45:67:89:ab:cd:ef
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// 01-23-45-67-89-ab
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// 01-23-45-67-89-ab-cd-ef
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// 0123.4567.89ab
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// 0123.4567.89ab.cdef
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsMAC ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
_ , err := net . ParseMAC ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return err == nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsHost ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsIP ( str ) || IsDNSName ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsMongoID check if the string is a valid hex-encoded representation of a MongoDB ObjectId.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsMongoID ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxHexadecimal . MatchString ( str ) && ( len ( str ) == 24 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsLatitude check if a string is valid latitude.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsLatitude ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxLatitude . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsLongitude check if a string is valid longitude.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsLongitude ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxLongitude . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRsaPublicKey check if a string is valid public key with provided length
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRsaPublicKey ( str string , keylen int ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
bb := bytes . NewBufferString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
pemBytes , err := ioutil . ReadAll ( bb )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
block , _ := pem . Decode ( pemBytes )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if block != nil && block . Type != "PUBLIC KEY" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var der [ ] byte
if block != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
der = block . Bytes
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
der , err = base64 . StdEncoding . DecodeString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
key , err := x509 . ParsePKIXPublicKey ( der )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
pubkey , ok := key . ( * rsa . PublicKey )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
bitlen := len ( pubkey . N . Bytes ( ) ) * 8
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return bitlen == int ( keylen )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
func toJSONName ( tag string ) string {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if tag == "" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ""
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// JSON name always comes first. If there's no options then split[0] is
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// JSON name, if JSON name is not set, then split[0] is an empty string.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
split := strings . SplitN ( tag , "," , 2 )
name := split [ 0 ]
// However it is possible that the field is skipped when
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// (de-)serializing from/to JSON, in which case assume that there is no
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// tag name to use
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if name == "-" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ""
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return name
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
func PrependPathToErrors ( err error , path string ) error {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch err2 := err . ( type ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case Error :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err2 . Path = append ( [ ] string { path } , err2 . Path ... )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return err2
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case Errors :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errors := err2 . Errors ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i , err3 := range errors {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errors [ i ] = PrependPathToErrors ( err3 , path )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return err2
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return err
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// ValidateMap use validation map for fields.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// result will be equal to `false` if there are any errors.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// m is the validation map in the form
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func ValidateMap ( s map [ string ] interface { } , m map [ string ] interface { } ) ( bool , error ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if s == nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var errs Errors
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var index int
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
val := reflect . ValueOf ( s )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for key , value := range s {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
presentResult := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validator , ok := m [ key ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
presentResult = false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = fmt . Errorf ( "all map keys has to be present in the validation map; got %s" , key )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , key )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
valueField := reflect . ValueOf ( value )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
mapResult := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
typeResult := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
structResult := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultField := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch subValidator := validator . ( type ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case map [ string ] interface { } :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if v , ok := value . ( map [ string ] interface { } ) ; ! ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
mapResult = false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = fmt . Errorf ( "map validator has to be for the map type only; got %s" , valueField . Type ( ) . String ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , key )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
mapResult , err = ValidateMap ( v , subValidator )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
mapResult = false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , key )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case string :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ( valueField . Kind ( ) == reflect . Struct ||
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
( valueField . Kind ( ) == reflect . Ptr && valueField . Elem ( ) . Kind ( ) == reflect . Struct ) ) &&
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
subValidator != "-" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
structResult , err = ValidateStruct ( valueField . Interface ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , key )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultField , err = typeCheck ( valueField , reflect . StructField {
2024-07-24 23:45:04 +00:00
Name : key ,
PkgPath : "" ,
Type : val . Type ( ) ,
Tag : reflect . StructTag ( fmt . Sprintf ( "%s:%q" , tagName , subValidator ) ) ,
Offset : 0 ,
Index : [ ] int { index } ,
2024-02-18 10:42:21 +00:00
Anonymous : false ,
} , val , nil )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case nil :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// already handlerd when checked before
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
default :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
typeResult = false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = fmt . Errorf ( "map validator has to be either map[string]interface{} or string; got %s" , valueField . Type ( ) . String ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , key )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result = result && presentResult && typeResult && resultField && structResult && mapResult
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
index ++
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// check required keys
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
requiredResult := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for key , value := range m {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if schema , ok := value . ( string ) ; ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
tags := parseTagIntoMap ( schema )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if required , ok := tags [ "required" ] ; ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if _ , ok := s [ key ] ; ! ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
requiredResult = false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if required . customErrorMessage != "" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = Error { key , fmt . Errorf ( required . customErrorMessage ) , true , "required" , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = Error { key , fmt . Errorf ( "required field missing" ) , false , "required" , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
if len ( errs ) > 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = errs
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return result && requiredResult , err
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// ValidateStruct use tags for fields.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// result will be equal to `false` if there are any errors.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail)
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func ValidateStruct ( s interface { } ) ( bool , error ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if s == nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
val := reflect . ValueOf ( s )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if val . Kind ( ) == reflect . Interface || val . Kind ( ) == reflect . Ptr {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
val = val . Elem ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// we only accept structs
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if val . Kind ( ) != reflect . Struct {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , fmt . Errorf ( "function only accepts structs; got %s" , val . Kind ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var errs Errors
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i := 0 ; i < val . NumField ( ) ; i ++ {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
valueField := val . Field ( i )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
typeField := val . Type ( ) . Field ( i )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if typeField . PkgPath != "" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
continue // Private field
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
structResult := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if valueField . Kind ( ) == reflect . Interface {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
valueField = valueField . Elem ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ( valueField . Kind ( ) == reflect . Struct ||
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
( valueField . Kind ( ) == reflect . Ptr && valueField . Elem ( ) . Kind ( ) == reflect . Struct ) ) &&
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
typeField . Tag . Get ( tagName ) != "-" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
structResult , err = ValidateStruct ( valueField . Interface ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , typeField . Name )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
errs = append ( errs , err )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultField , err2 := typeCheck ( valueField , typeField , val , nil )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err2 != nil {
// Replace structure name with JSON name if there is a tag on the variable
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
jsonTag := toJSONName ( typeField . Tag . Get ( "json" ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if jsonTag != "" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch jsonError := err2 . ( type ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case Error :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
jsonError . Name = jsonTag
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err2 = jsonError
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case Errors :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i2 , err3 := range jsonError {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch customErr := err3 . ( type ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case Error :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
customErr . Name = jsonTag
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
jsonError [ i2 ] = customErr
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
err2 = jsonError
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
errs = append ( errs , err2 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result = result && resultField && structResult
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( errs ) > 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = errs
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return result , err
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func parseTagIntoMap ( tag string ) tagOptionsMap {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
optionsMap := make ( tagOptionsMap )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
options := strings . Split ( tag , "," )
for i , option := range options {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
option = strings . TrimSpace ( option )
validationOptions := strings . Split ( option , "~" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! isValidTag ( validationOptions [ 0 ] ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
continue
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( validationOptions ) == 2 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
optionsMap [ validationOptions [ 0 ] ] = tagOption { validationOptions [ 0 ] , validationOptions [ 1 ] , i }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
optionsMap [ validationOptions [ 0 ] ] = tagOption { validationOptions [ 0 ] , "" , i }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return optionsMap
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
func isValidTag ( s string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if s == "" {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , c := range s {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case strings . ContainsRune ( "\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ " , c ) :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Backslash and quote chars are reserved, but
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// otherwise any punctuation chars are allowed
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// in a tag name.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
default :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! unicode . IsLetter ( c ) && ! unicode . IsDigit ( c ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsSSN will validate the given string as a U.S. Social Security Number
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsSSN ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == "" || len ( str ) != 11 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxSSN . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsSemver check if string is valid semantic version
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsSemver ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return rxSemver . MatchString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsType check if interface is of some type
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsType ( v interface { } , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( params ) == 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
typ := params [ 0 ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return strings . Replace ( reflect . TypeOf ( v ) . String ( ) , " " , "" , - 1 ) == strings . Replace ( typ , " " , "" , - 1 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsTime check if string is valid according to given format
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsTime ( str string , format string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
_ , err := time . Parse ( format , str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return err == nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsUnixTime check if string is valid unix timestamp value
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsUnixTime ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if _ , err := strconv . Atoi ( str ) ; err == nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRFC3339 check if string is valid timestamp value according to RFC3339
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRFC3339 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsTime ( str , time . RFC3339 )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRFC3339WithoutZone check if string is valid timestamp value according to RFC3339 which excludes the timezone.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRFC3339WithoutZone ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsTime ( str , RF3339WithoutZone )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsISO4217 check if string is valid ISO currency code
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsISO4217 ( str string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , currency := range ISO4217List {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == currency {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// ByteLength check string's length
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func ByteLength ( str string , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( params ) == 2 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
min , _ := ToInt ( params [ 0 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
max , _ := ToInt ( params [ 1 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return len ( str ) >= int ( min ) && len ( str ) <= int ( max )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// RuneLength check string's length
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Alias for StringLength
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func RuneLength ( str string , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return StringLength ( str , params ... )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsRsaPub check whether string is valid RSA key
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Alias for IsRsaPublicKey
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsRsaPub ( str string , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( params ) == 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
len , _ := ToInt ( params [ 0 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return IsRsaPublicKey ( str , int ( len ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// StringMatches checks if a string matches a given pattern.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func StringMatches ( s string , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( params ) == 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
pattern := params [ 0 ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return Matches ( s , pattern )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// StringLength check string's length (including multi byte strings)
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func StringLength ( str string , params ... string ) bool {
if len ( params ) == 2 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
strLength := utf8 . RuneCountInString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
min , _ := ToInt ( params [ 0 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
max , _ := ToInt ( params [ 1 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return strLength >= int ( min ) && strLength <= int ( max )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// MinStringLength check string's minimum length (including multi byte strings)
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func MinStringLength ( str string , params ... string ) bool {
if len ( params ) == 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
strLength := utf8 . RuneCountInString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
min , _ := ToInt ( params [ 0 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return strLength >= int ( min )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// MaxStringLength check string's maximum length (including multi byte strings)
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func MaxStringLength ( str string , params ... string ) bool {
if len ( params ) == 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
strLength := utf8 . RuneCountInString ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
max , _ := ToInt ( params [ 0 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return strLength <= int ( max )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// Range check string's length
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func Range ( str string , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( params ) == 2 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
value , _ := ToFloat ( str )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
min , _ := ToFloat ( params [ 0 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
max , _ := ToFloat ( params [ 1 ] )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return InRange ( value , min , max )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
func IsInRaw ( str string , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( params ) == 1 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
rawParams := params [ 0 ]
parsedParams := strings . Split ( rawParams , "|" )
return IsIn ( str , parsedParams ... )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// IsIn check if string str is a member of the set of strings params
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func IsIn ( str string , params ... string ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , param := range params {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if str == param {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
func checkRequired ( v reflect . Value , t reflect . StructField , options tagOptionsMap ) ( bool , error ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if nilPtrAllowedByRequired {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
k := v . Kind ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ( k == reflect . Ptr || k == reflect . Interface ) && v . IsNil ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
if requiredOption , isRequired := options [ "required" ] ; isRequired {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( requiredOption . customErrorMessage ) > 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( requiredOption . customErrorMessage ) , true , "required" , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "non zero value required" ) , false , "required" , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else if _ , isOptional := options [ "optional" ] ; fieldsRequiredByDefault && ! isOptional {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "Missing required field" ) , false , "required" , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// not required and empty is valid
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
func typeCheck ( v reflect . Value , t reflect . StructField , o reflect . Value , options tagOptionsMap ) ( isValid bool , resultErr error ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! v . IsValid ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
tag := t . Tag . Get ( tagName )
// Check if the field should be ignored
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch tag {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case "" :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if v . Kind ( ) != reflect . Slice && v . Kind ( ) != reflect . Map {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! fieldsRequiredByDefault {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "All fields are required to at least have one validation defined" ) , false , "required" , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case "-" :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
isRootType := false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if options == nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
isRootType = true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
options = parseTagIntoMap ( tag )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
if ! isFieldSet ( v ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// an empty value is not validated, check only required
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
isValid , resultErr = checkRequired ( v , t , options )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for key := range options {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
delete ( options , key )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return isValid , resultErr
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
var customTypeErrors Errors
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
optionsOrder := options . orderedKeys ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , validatorName := range optionsOrder {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validatorStruct := options [ validatorName ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if validatefunc , ok := CustomTypeTagMap . Get ( validatorName ) ; ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
delete ( options , validatorName )
if result := validatefunc ( v . Interface ( ) , o . Interface ( ) ) ; ! result {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( validatorStruct . customErrorMessage ) > 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
customTypeErrors = append ( customTypeErrors , Error { Name : t . Name , Err : TruncatingErrorf ( validatorStruct . customErrorMessage , fmt . Sprint ( v ) , validatorName ) , CustomErrorMessageExists : true , Validator : stripParams ( validatorName ) } )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
continue
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
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 ) } )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
if len ( customTypeErrors . Errors ( ) ) > 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , customTypeErrors
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
if isRootType {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// Ensure that we've checked the value by all specified validators before report that the value is valid
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
defer func ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
delete ( options , "optional" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
delete ( options , "required" )
if isValid && resultErr == nil && len ( options ) != 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
optionsOrder := options . orderedKeys ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , validator := range optionsOrder {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
isValid = false
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultErr = Error { t . Name , fmt . Errorf (
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
"The following validator is invalid or can't be applied to the field: %q" , validator ) , false , stripParams ( validator ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
for _ , validatorSpec := range optionsOrder {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validatorStruct := options [ validatorSpec ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var negate bool
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validator := validatorSpec
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
customMsgExists := len ( validatorStruct . customErrorMessage ) > 0
// Check whether the tag looks like '!something' or 'something'
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if validator [ 0 ] == '!' {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validator = validator [ 1 : ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
negate = true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// Check for interface param validators
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for key , value := range InterfaceParamTagRegexMap {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
ps := value . FindStringSubmatch ( validator )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( ps ) == 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
continue
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
validatefunc , ok := InterfaceParamTagMap [ key ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
continue
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
delete ( options , validatorSpec )
field := fmt . Sprint ( v )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if result := validatefunc ( v . Interface ( ) , ps [ 1 : ] ... ) ; ( ! result && ! negate ) || ( result && negate ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if customMsgExists {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , TruncatingErrorf ( validatorStruct . customErrorMessage , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if negate {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "%s does validate as %s" , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "%s does not validate as %s" , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
switch v . Kind ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . Bool ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uintptr ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Float32 , reflect . Float64 ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . String :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// for each tag option check the map of validator functions
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , validatorSpec := range optionsOrder {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validatorStruct := options [ validatorSpec ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var negate bool
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validator := validatorSpec
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
customMsgExists := len ( validatorStruct . customErrorMessage ) > 0
// Check whether the tag looks like '!something' or 'something'
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if validator [ 0 ] == '!' {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
validator = validator [ 1 : ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
negate = true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// Check for param validators
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for key , value := range ParamTagRegexMap {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
ps := value . FindStringSubmatch ( validator )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if len ( ps ) == 0 {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
continue
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
validatefunc , ok := ParamTagMap [ key ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if ! ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
continue
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
delete ( options , validatorSpec )
switch v . Kind ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . String ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Float32 , reflect . Float64 :
field := fmt . Sprint ( v ) // make value into string, then validate with regex
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if result := validatefunc ( field , ps [ 1 : ] ... ) ; ( ! result && ! negate ) || ( result && negate ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if customMsgExists {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , TruncatingErrorf ( validatorStruct . customErrorMessage , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if negate {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "%s does validate as %s" , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "%s does not validate as %s" , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
default :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// type not yet supported, fail
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "Validator %s doesn't support kind %s" , validator , v . Kind ( ) ) , false , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
if validatefunc , ok := TagMap [ validator ] ; ok {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
delete ( options , validatorSpec )
switch v . Kind ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . String ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 ,
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
reflect . Float32 , reflect . Float64 :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
field := fmt . Sprint ( v ) // make value into string, then validate with regex
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if result := validatefunc ( field ) ; ! result && ! negate || result && negate {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if customMsgExists {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , TruncatingErrorf ( validatorStruct . customErrorMessage , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if negate {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "%s does validate as %s" , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , fmt . Errorf ( "%s does not validate as %s" , field , validator ) , customMsgExists , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
default :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
//Not Yet Supported Types (Fail here!)
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err := fmt . Errorf ( "Validator %s doesn't support kind %s for value %v" , validator , v . Kind ( ) , v )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , Error { t . Name , err , false , stripParams ( validatorSpec ) , [ ] string { } }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . Map :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if v . Type ( ) . Key ( ) . Kind ( ) != reflect . String {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , & UnsupportedTypeError { v . Type ( ) }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var sv stringValues
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
sv = v . MapKeys ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
sort . Sort ( sv )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i , k := range sv {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var resultItem bool
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if v . MapIndex ( k ) . Kind ( ) != reflect . Struct {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultItem , err = typeCheck ( v . MapIndex ( k ) , t , o , options )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , err
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultItem , err = ValidateStruct ( v . MapIndex ( k ) . Interface ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , t . Name + "." + sv [ i ] . Interface ( ) . ( string ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , err
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result = result && resultItem
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return result , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . Slice , reflect . Array :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result := true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for i := 0 ; i < v . Len ( ) ; i ++ {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var resultItem bool
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
var err error
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if v . Index ( i ) . Kind ( ) != reflect . Struct {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultItem , err = typeCheck ( v . Index ( i ) , t , o , options )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , err
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
} else {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
resultItem , err = ValidateStruct ( v . Index ( i ) . Interface ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if err != nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
err = PrependPathToErrors ( err , t . Name + "." + strconv . Itoa ( i ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , err
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
result = result && resultItem
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return result , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . Interface :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// If the value is an interface then encode its element
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if v . IsNil ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ValidateStruct ( v . Interface ( ) )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . Ptr :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// If the value is a pointer then check its element
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if v . IsNil ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return typeCheck ( v . Elem ( ) , t , o , options )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . Struct :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return true , nil
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
default :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return false , & UnsupportedTypeError { v . Type ( ) }
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
func stripParams ( validatorString string ) string {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return paramsRegexp . ReplaceAllString ( validatorString , "" )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// isFieldSet returns false for nil pointers, interfaces, maps, and slices. For all other values, it returns true.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func isFieldSet ( v reflect . Value ) bool {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
switch v . Kind ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case reflect . Map , reflect . Slice , reflect . Interface , reflect . Ptr :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ! v . IsNil ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return true
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// ErrorByField returns error for specified field of the struct
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// validated by ValidateStruct or empty string if there are no errors
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// or this field doesn't exists or doesn't have any errors.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func ErrorByField ( e error , field string ) string {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if e == nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ""
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return ErrorsByField ( e ) [ field ]
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// ErrorsByField returns map of errors of the struct validated
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// by ValidateStruct or empty map if there are no errors.
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func ErrorsByField ( e error ) map [ string ] string {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
m := make ( map [ string ] string )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
if e == nil {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return m
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
// prototype for ValidateStruct
switch e . ( type ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case Error :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
m [ e . ( Error ) . Name ] = e . ( Error ) . Err . Error ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
case Errors :
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for _ , item := range e . ( Errors ) . Errors ( ) {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
n := ErrorsByField ( item )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
for k , v := range n {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
m [ k ] = v
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
return m
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
// Error returns string equivalent for reflect.Type
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
func ( e * UnsupportedTypeError ) Error ( ) string {
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
return "validator: unsupported type: " + e . Type . String ( )
2024-07-24 23:45:04 +00:00
2024-02-18 10:42:21 +00:00
}
2024-07-24 23:45:04 +00:00
func ( sv stringValues ) Len ( ) int { return len ( sv ) }
func ( sv stringValues ) Swap ( i , j int ) { sv [ i ] , sv [ j ] = sv [ j ] , sv [ i ] }
2024-02-18 10:42:21 +00:00
func ( sv stringValues ) Less ( i , j int ) bool { return sv . get ( i ) < sv . get ( j ) }
2024-07-24 23:45:04 +00:00
func ( sv stringValues ) get ( i int ) string { return sv [ i ] . String ( ) }