[chore] Update all but bun libraries (#526)

* update all but bun libraries

Signed-off-by: kim <grufwub@gmail.com>

* remove my personal build script changes

Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
kim
2022-05-02 14:05:18 +01:00
committed by GitHub
parent e06bf9cc9a
commit b56dae8120
350 changed files with 305366 additions and 5943 deletions

View File

@ -1,7 +1,7 @@
Package validator
=================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-10.9.0-green.svg)
![Project status](https://img.shields.io/badge/version-10.11.0-green.svg)
[![Build Status](https://travis-ci.org/go-playground/validator.svg?branch=master)](https://travis-ci.org/go-playground/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
@ -130,7 +130,7 @@ Baked-in Validations
| contains | Contains |
| containsany | Contains Any |
| containsrune | Contains Rune |
| endsnotwith | Ends With |
| endsnotwith | Ends Not With |
| endswith | Ends With |
| excludes | Excludes |
| excludesall | Excludes All |
@ -153,6 +153,7 @@ Baked-in Validations
| bcp47_language_tag | Language tag (BCP 47) |
| btc_addr | Bitcoin Address |
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
| credit_card | Credit Card Number |
| datetime | Datetime |
| e164 | e164 formatted phone number |
| email | E-mail String
@ -189,6 +190,18 @@ Baked-in Validations
| uuid5 | Universally Unique Identifier UUID v5 |
| uuid5_rfc4122 | Universally Unique Identifier UUID v5 RFC4122 |
| uuid_rfc4122 | Universally Unique Identifier UUID RFC4122 |
| md4 | MD4 hash |
| md5 | MD5 hash |
| sha256 | SHA256 hash |
| sha384 | SHA384 hash |
| sha512 | SHA512 hash |
| ripemd128 | RIPEMD-128 hash |
| ripemd128 | RIPEMD-160 hash |
| tiger128 | TIGER128 hash |
| tiger160 | TIGER160 hash |
| tiger192 | TIGER192 hash |
| semver | Semantic Versioning 2.0.0 |
| ulid | Universally Unique Lexicographically Sortable Identifier ULID |
### Comparisons:
| Tag | Description |
@ -217,6 +230,8 @@ Baked-in Validations
| required_with_all | Required With All |
| required_without | Required Without |
| required_without_all | Required Without All |
| excluded_if | Excluded If |
| excluded_unless | Excluded Unless |
| excluded_with | Excluded With |
| excluded_with_all | Excluded With All |
| excluded_without | Excluded Without |

View File

@ -75,6 +75,8 @@ var (
"required_with_all": requiredWithAll,
"required_without": requiredWithout,
"required_without_all": requiredWithoutAll,
"excluded_if": excludedIf,
"excluded_unless": excludedUnless,
"excluded_with": excludedWith,
"excluded_with_all": excludedWithAll,
"excluded_without": excludedWithout,
@ -148,6 +150,17 @@ var (
"uuid3_rfc4122": isUUID3RFC4122,
"uuid4_rfc4122": isUUID4RFC4122,
"uuid5_rfc4122": isUUID5RFC4122,
"ulid": isULID,
"md4": isMD4,
"md5": isMD5,
"sha256": isSHA256,
"sha384": isSHA384,
"sha512": isSHA512,
"ripemd128": isRIPEMD128,
"ripemd160": isRIPEMD160,
"tiger128": isTIGER128,
"tiger160": isTIGER160,
"tiger192": isTIGER192,
"ascii": isASCII,
"printascii": isPrintableASCII,
"multibyte": hasMultiByteCharacter,
@ -198,11 +211,16 @@ var (
"postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
"bic": isIsoBicFormat,
"semver": isSemverFormat,
"dns_rfc1035_label": isDnsRFC1035LabelFormat,
"credit_card": isCreditCard,
}
)
var oneofValsCache = map[string][]string{}
var oneofValsCacheRWLock = sync.RWMutex{}
var (
oneofValsCache = map[string][]string{}
oneofValsCacheRWLock = sync.RWMutex{}
)
func parseOneOfParam2(s string) []string {
oneofValsCacheRWLock.RLock()
@ -258,7 +276,6 @@ func isOneOf(fl FieldLevel) bool {
// isUnique is the validation function for validating if each array|slice|map value is unique
func isUnique(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
v := reflect.ValueOf(struct{}{})
@ -308,7 +325,6 @@ func isUnique(fl FieldLevel) bool {
// isMAC is the validation function for validating if the field's value is a valid MAC address.
func isMAC(fl FieldLevel) bool {
_, err := net.ParseMAC(fl.Field().String())
return err == nil
@ -316,7 +332,6 @@ func isMAC(fl FieldLevel) bool {
// isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
func isCIDRv4(fl FieldLevel) bool {
ip, _, err := net.ParseCIDR(fl.Field().String())
return err == nil && ip.To4() != nil
@ -324,7 +339,6 @@ func isCIDRv4(fl FieldLevel) bool {
// isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
func isCIDRv6(fl FieldLevel) bool {
ip, _, err := net.ParseCIDR(fl.Field().String())
return err == nil && ip.To4() == nil
@ -332,7 +346,6 @@ func isCIDRv6(fl FieldLevel) bool {
// isCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address.
func isCIDR(fl FieldLevel) bool {
_, _, err := net.ParseCIDR(fl.Field().String())
return err == nil
@ -340,7 +353,6 @@ func isCIDR(fl FieldLevel) bool {
// isIPv4 is the validation function for validating if a value is a valid v4 IP address.
func isIPv4(fl FieldLevel) bool {
ip := net.ParseIP(fl.Field().String())
return ip != nil && ip.To4() != nil
@ -348,7 +360,6 @@ func isIPv4(fl FieldLevel) bool {
// isIPv6 is the validation function for validating if the field's value is a valid v6 IP address.
func isIPv6(fl FieldLevel) bool {
ip := net.ParseIP(fl.Field().String())
return ip != nil && ip.To4() == nil
@ -356,7 +367,6 @@ func isIPv6(fl FieldLevel) bool {
// isIP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
func isIP(fl FieldLevel) bool {
ip := net.ParseIP(fl.Field().String())
return ip != nil
@ -364,7 +374,6 @@ func isIP(fl FieldLevel) bool {
// isSSN is the validation function for validating if the field's value is a valid SSN.
func isSSN(fl FieldLevel) bool {
field := fl.Field()
if field.Len() != 11 {
@ -422,7 +431,6 @@ func isLatitude(fl FieldLevel) bool {
// isDataURI is the validation function for validating if the field's value is a valid data URI.
func isDataURI(fl FieldLevel) bool {
uri := strings.SplitN(fl.Field().String(), ",", 2)
if len(uri) != 2 {
@ -438,7 +446,6 @@ func isDataURI(fl FieldLevel) bool {
// hasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character.
func hasMultiByteCharacter(fl FieldLevel) bool {
field := fl.Field()
if field.Len() == 0 {
@ -498,6 +505,61 @@ func isUUIDRFC4122(fl FieldLevel) bool {
return uUIDRFC4122Regex.MatchString(fl.Field().String())
}
// isULID is the validation function for validating if the field's value is a valid ULID.
func isULID(fl FieldLevel) bool {
return uLIDRegex.MatchString(fl.Field().String())
}
// isMD4 is the validation function for validating if the field's value is a valid MD4.
func isMD4(fl FieldLevel) bool {
return md4Regex.MatchString(fl.Field().String())
}
// isMD5 is the validation function for validating if the field's value is a valid MD5.
func isMD5(fl FieldLevel) bool {
return md5Regex.MatchString(fl.Field().String())
}
// isSHA256 is the validation function for validating if the field's value is a valid SHA256.
func isSHA256(fl FieldLevel) bool {
return sha256Regex.MatchString(fl.Field().String())
}
// isSHA384 is the validation function for validating if the field's value is a valid SHA384.
func isSHA384(fl FieldLevel) bool {
return sha384Regex.MatchString(fl.Field().String())
}
// isSHA512 is the validation function for validating if the field's value is a valid SHA512.
func isSHA512(fl FieldLevel) bool {
return sha512Regex.MatchString(fl.Field().String())
}
// isRIPEMD128 is the validation function for validating if the field's value is a valid PIPEMD128.
func isRIPEMD128(fl FieldLevel) bool {
return ripemd128Regex.MatchString(fl.Field().String())
}
// isRIPEMD160 is the validation function for validating if the field's value is a valid PIPEMD160.
func isRIPEMD160(fl FieldLevel) bool {
return ripemd160Regex.MatchString(fl.Field().String())
}
// isTIGER128 is the validation function for validating if the field's value is a valid TIGER128.
func isTIGER128(fl FieldLevel) bool {
return tiger128Regex.MatchString(fl.Field().String())
}
// isTIGER160 is the validation function for validating if the field's value is a valid TIGER160.
func isTIGER160(fl FieldLevel) bool {
return tiger160Regex.MatchString(fl.Field().String())
}
// isTIGER192 is the validation function for validating if the field's value is a valid isTIGER192.
func isTIGER192(fl FieldLevel) bool {
return tiger192Regex.MatchString(fl.Field().String())
}
// isISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
func isISBN(fl FieldLevel) bool {
return isISBN10(fl) || isISBN13(fl)
@ -505,7 +567,6 @@ func isISBN(fl FieldLevel) bool {
// isISBN13 is the validation function for validating if the field's value is a valid v13 ISBN.
func isISBN13(fl FieldLevel) bool {
s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 4), " ", "", 4)
if !iSBN13Regex.MatchString(s) {
@ -526,7 +587,6 @@ func isISBN13(fl FieldLevel) bool {
// isISBN10 is the validation function for validating if the field's value is a valid v10 ISBN.
func isISBN10(fl FieldLevel) bool {
s := strings.Replace(strings.Replace(fl.Field().String(), "-", "", 3), " ", "", 3)
if !iSBN10Regex.MatchString(s) {
@ -714,7 +774,6 @@ func excludes(fl FieldLevel) bool {
// containsRune is the validation function for validating that the field's value contains the rune specified within the param.
func containsRune(fl FieldLevel) bool {
r, _ := utf8.DecodeRuneInString(fl.Param())
return strings.ContainsRune(fl.Field().String(), r)
@ -777,7 +836,6 @@ func fieldExcludes(fl FieldLevel) bool {
// isNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value.
func isNeField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -808,12 +866,7 @@ func isNeField(fl FieldLevel) bool {
fieldType := field.Type()
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return true
}
if fieldType == timeType {
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
@ -821,6 +874,10 @@ func isNeField(fl FieldLevel) bool {
return !fieldTime.Equal(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return true
}
}
// default reflect.String:
@ -834,7 +891,6 @@ func isNe(fl FieldLevel) bool {
// isLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value.
func isLteCrossStructField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -861,18 +917,18 @@ func isLteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
}
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
fieldTime := field.Interface().(time.Time)
topTime := topField.Interface().(time.Time)
return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
}
}
// default reflect.String:
@ -882,7 +938,6 @@ func isLteCrossStructField(fl FieldLevel) bool {
// isLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func isLtCrossStructField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -909,18 +964,18 @@ func isLtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.Before(topTime)
}
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
fieldTime := field.Interface().(time.Time)
topTime := topField.Interface().(time.Time)
return fieldTime.Before(topTime)
}
}
// default reflect.String:
@ -929,7 +984,6 @@ func isLtCrossStructField(fl FieldLevel) bool {
// isGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value.
func isGteCrossStructField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -956,18 +1010,18 @@ func isGteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.After(topTime) || fieldTime.Equal(topTime)
}
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
fieldTime := field.Interface().(time.Time)
topTime := topField.Interface().(time.Time)
return fieldTime.After(topTime) || fieldTime.Equal(topTime)
}
}
// default reflect.String:
@ -976,7 +1030,6 @@ func isGteCrossStructField(fl FieldLevel) bool {
// isGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value.
func isGtCrossStructField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1003,18 +1056,18 @@ func isGtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.After(topTime)
}
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
fieldTime := field.Interface().(time.Time)
topTime := topField.Interface().(time.Time)
return fieldTime.After(topTime)
}
}
// default reflect.String:
@ -1023,7 +1076,6 @@ func isGtCrossStructField(fl FieldLevel) bool {
// isNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value.
func isNeCrossStructField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1053,18 +1105,18 @@ func isNeCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
t := field.Convert(timeType).Interface().(time.Time)
fieldTime := topField.Convert(timeType).Interface().(time.Time)
return !fieldTime.Equal(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return true
}
if fieldType == timeType {
t := field.Interface().(time.Time)
fieldTime := topField.Interface().(time.Time)
return !fieldTime.Equal(t)
}
}
// default reflect.String:
@ -1073,7 +1125,6 @@ func isNeCrossStructField(fl FieldLevel) bool {
// isEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value.
func isEqCrossStructField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1103,18 +1154,18 @@ func isEqCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
t := field.Convert(timeType).Interface().(time.Time)
fieldTime := topField.Convert(timeType).Interface().(time.Time)
return fieldTime.Equal(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != topField.Type() {
return false
}
if fieldType == timeType {
t := field.Interface().(time.Time)
fieldTime := topField.Interface().(time.Time)
return fieldTime.Equal(t)
}
}
// default reflect.String:
@ -1123,7 +1174,6 @@ func isEqCrossStructField(fl FieldLevel) bool {
// isEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value.
func isEqField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1153,19 +1203,18 @@ func isEqField(fl FieldLevel) bool {
fieldType := field.Type()
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
if fieldType == timeType {
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
return fieldTime.Equal(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
}
// default reflect.String:
@ -1174,7 +1223,6 @@ func isEqField(fl FieldLevel) bool {
// isEq is the validation function for validating if the current field's value is equal to the param's value.
func isEq(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
@ -1226,7 +1274,7 @@ func isPostcodeByIso3166Alpha2(fl FieldLevel) bool {
return reg.MatchString(field.String())
}
// isPostcodeByIso3166Alpha2 validates by field which represents for a value of country code in iso 3166 alpha 2
// isPostcodeByIso3166Alpha2Field validates by field which represents for a value of country code in iso 3166 alpha 2
// example: `postcode_iso3166_alpha2_field=CountryCode`
func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
field := fl.Field()
@ -1265,11 +1313,9 @@ func isBase64URL(fl FieldLevel) bool {
// isURI is the validation function for validating if the current field's value is a valid URI.
func isURI(fl FieldLevel) bool {
field := fl.Field()
switch field.Kind() {
case reflect.String:
s := field.String()
@ -1294,11 +1340,9 @@ func isURI(fl FieldLevel) bool {
// isURL is the validation function for validating if the current field's value is a valid URL.
func isURL(fl FieldLevel) bool {
field := fl.Field()
switch field.Kind() {
case reflect.String:
var i int
@ -1331,7 +1375,6 @@ func isUrnRFC2141(fl FieldLevel) bool {
field := fl.Field()
switch field.Kind() {
case reflect.String:
str := field.String()
@ -1534,6 +1577,22 @@ func requiredIf(fl FieldLevel) bool {
return hasValue(fl)
}
// excludedIf is the validation function
// The field under validation must not be present or is empty only if all the other specified fields are equal to the value following with the specified field.
func excludedIf(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for excluded_if %s", fl.FieldName()))
}
for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return false
}
}
return true
}
// requiredUnless is the validation function
// The field under validation must be present and not empty only unless all the other specified fields are equal to the value following with the specified field.
func requiredUnless(fl FieldLevel) bool {
@ -1550,6 +1609,21 @@ func requiredUnless(fl FieldLevel) bool {
return hasValue(fl)
}
// excludedUnless is the validation function
// The field under validation must not be present or is empty unless all the other specified fields are equal to the value following with the specified field.
func excludedUnless(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
if len(params)%2 != 0 {
panic(fmt.Sprintf("Bad param number for excluded_unless %s", fl.FieldName()))
}
for i := 0; i < len(params); i += 2 {
if !requireCheckFieldValue(fl, params[i], params[i+1], false) {
return true
}
}
return !hasValue(fl)
}
// excludedWith is the validation function
// The field under validation must not be present or is empty if any of the other specified fields are present.
func excludedWith(fl FieldLevel) bool {
@ -1642,7 +1716,6 @@ func requiredWithoutAll(fl FieldLevel) bool {
// isGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value.
func isGteField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1669,18 +1742,18 @@ func isGteField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
return fieldTime.After(t) || fieldTime.Equal(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType {
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.After(t) || fieldTime.Equal(t)
}
}
// default reflect.String
@ -1689,7 +1762,6 @@ func isGteField(fl FieldLevel) bool {
// isGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value.
func isGtField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1716,18 +1788,18 @@ func isGtField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
return fieldTime.After(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType {
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.After(t)
}
}
// default reflect.String
@ -1736,7 +1808,6 @@ func isGtField(fl FieldLevel) bool {
// isGte is the validation function for validating if the current field's value is greater than or equal to the param's value.
func isGte(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
@ -1769,10 +1840,10 @@ func isGte(fl FieldLevel) bool {
case reflect.Struct:
if field.Type() == timeType {
if field.Type().ConvertibleTo(timeType) {
now := time.Now().UTC()
t := field.Interface().(time.Time)
t := field.Convert(timeType).Interface().(time.Time)
return t.After(now) || t.Equal(now)
}
@ -1783,7 +1854,6 @@ func isGte(fl FieldLevel) bool {
// isGt is the validation function for validating if the current field's value is greater than the param's value.
func isGt(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
@ -1815,9 +1885,9 @@ func isGt(fl FieldLevel) bool {
return field.Float() > p
case reflect.Struct:
if field.Type() == timeType {
if field.Type().ConvertibleTo(timeType) {
return field.Interface().(time.Time).After(time.Now().UTC())
return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
}
}
@ -1826,7 +1896,6 @@ func isGt(fl FieldLevel) bool {
// hasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
func hasLengthOf(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
@ -1868,7 +1937,6 @@ func hasMinOf(fl FieldLevel) bool {
// isLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value.
func isLteField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1895,18 +1963,18 @@ func isLteField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
return fieldTime.Before(t) || fieldTime.Equal(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType {
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.Before(t) || fieldTime.Equal(t)
}
}
// default reflect.String
@ -1915,7 +1983,6 @@ func isLteField(fl FieldLevel) bool {
// isLtField is the validation function for validating if the current field's value is less than the field specified by the param's value.
func isLtField(fl FieldLevel) bool {
field := fl.Field()
kind := field.Kind()
@ -1942,18 +2009,18 @@ func isLtField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
return fieldTime.Before(t)
}
// Not Same underlying type i.e. struct and time
if fieldType != currentField.Type() {
return false
}
if fieldType == timeType {
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
return fieldTime.Before(t)
}
}
// default reflect.String
@ -1962,7 +2029,6 @@ func isLtField(fl FieldLevel) bool {
// isLte is the validation function for validating if the current field's value is less than or equal to the param's value.
func isLte(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
@ -1995,10 +2061,10 @@ func isLte(fl FieldLevel) bool {
case reflect.Struct:
if field.Type() == timeType {
if field.Type().ConvertibleTo(timeType) {
now := time.Now().UTC()
t := field.Interface().(time.Time)
t := field.Convert(timeType).Interface().(time.Time)
return t.Before(now) || t.Equal(now)
}
@ -2009,7 +2075,6 @@ func isLte(fl FieldLevel) bool {
// isLt is the validation function for validating if the current field's value is less than the param's value.
func isLt(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
@ -2042,9 +2107,9 @@ func isLt(fl FieldLevel) bool {
case reflect.Struct:
if field.Type() == timeType {
if field.Type().ConvertibleTo(timeType) {
return field.Interface().(time.Time).Before(time.Now().UTC())
return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
}
}
@ -2058,7 +2123,6 @@ func hasMaxOf(fl FieldLevel) bool {
// isTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address.
func isTCP4AddrResolvable(fl FieldLevel) bool {
if !isIP4Addr(fl) {
return false
}
@ -2069,7 +2133,6 @@ func isTCP4AddrResolvable(fl FieldLevel) bool {
// isTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address.
func isTCP6AddrResolvable(fl FieldLevel) bool {
if !isIP6Addr(fl) {
return false
}
@ -2081,7 +2144,6 @@ func isTCP6AddrResolvable(fl FieldLevel) bool {
// isTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address.
func isTCPAddrResolvable(fl FieldLevel) bool {
if !isIP4Addr(fl) && !isIP6Addr(fl) {
return false
}
@ -2093,7 +2155,6 @@ func isTCPAddrResolvable(fl FieldLevel) bool {
// isUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address.
func isUDP4AddrResolvable(fl FieldLevel) bool {
if !isIP4Addr(fl) {
return false
}
@ -2105,7 +2166,6 @@ func isUDP4AddrResolvable(fl FieldLevel) bool {
// isUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address.
func isUDP6AddrResolvable(fl FieldLevel) bool {
if !isIP6Addr(fl) {
return false
}
@ -2117,7 +2177,6 @@ func isUDP6AddrResolvable(fl FieldLevel) bool {
// isUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address.
func isUDPAddrResolvable(fl FieldLevel) bool {
if !isIP4Addr(fl) && !isIP6Addr(fl) {
return false
}
@ -2129,7 +2188,6 @@ func isUDPAddrResolvable(fl FieldLevel) bool {
// isIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address.
func isIP4AddrResolvable(fl FieldLevel) bool {
if !isIPv4(fl) {
return false
}
@ -2141,7 +2199,6 @@ func isIP4AddrResolvable(fl FieldLevel) bool {
// isIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address.
func isIP6AddrResolvable(fl FieldLevel) bool {
if !isIPv6(fl) {
return false
}
@ -2153,7 +2210,6 @@ func isIP6AddrResolvable(fl FieldLevel) bool {
// isIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address.
func isIPAddrResolvable(fl FieldLevel) bool {
if !isIP(fl) {
return false
}
@ -2165,14 +2221,12 @@ func isIPAddrResolvable(fl FieldLevel) bool {
// isUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address.
func isUnixAddrResolvable(fl FieldLevel) bool {
_, err := net.ResolveUnixAddr("unix", fl.Field().String())
return err == nil
}
func isIP4Addr(fl FieldLevel) bool {
val := fl.Field().String()
if idx := strings.LastIndex(val, ":"); idx != -1 {
@ -2185,7 +2239,6 @@ func isIP4Addr(fl FieldLevel) bool {
}
func isIP6Addr(fl FieldLevel) bool {
val := fl.Field().String()
if idx := strings.LastIndex(val, ":"); idx != -1 {
@ -2351,6 +2404,12 @@ func isIso3166AlphaNumeric(fl FieldLevel) bool {
var code int
switch field.Kind() {
case reflect.String:
i, err := strconv.Atoi(field.String())
if err != nil {
return false
}
code = i % 1000
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
code = int(field.Int() % 1000)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
@ -2407,3 +2466,56 @@ func isIsoBicFormat(fl FieldLevel) bool {
return bicRegex.MatchString(bicString)
}
// isSemverFormat is the validation function for validating if the current field's value is a valid semver version, defined in Semantic Versioning 2.0.0
func isSemverFormat(fl FieldLevel) bool {
semverString := fl.Field().String()
return semverRegex.MatchString(semverString)
}
// isDnsRFC1035LabelFormat is the validation function
// for validating if the current field's value is
// a valid dns RFC 1035 label, defined in RFC 1035.
func isDnsRFC1035LabelFormat(fl FieldLevel) bool {
val := fl.Field().String()
return dnsRegexRFC1035Label.MatchString(val)
}
// isCreditCard is the validation function for validating if the current field's value is a valid credit card number
func isCreditCard(fl FieldLevel) bool {
val := fl.Field().String()
var creditCard bytes.Buffer
segments := strings.Split(val, " ")
for _, segment := range segments {
if len(segment) < 3 {
return false
}
creditCard.WriteString(segment)
}
ccDigits := strings.Split(creditCard.String(), "")
size := len(ccDigits)
if size < 12 || size > 19 {
return false
}
sum := 0
for i, digit := range ccDigits {
value, err := strconv.Atoi(digit)
if err != nil {
return false
}
if size%2 == 0 && i%2 == 0 || size%2 == 1 && i%2 == 1 {
v := value * 2
if v >= 10 {
sum += 1 + (v % 10)
} else {
sum += v
}
} else {
sum += value
}
}
return (sum % 10) == 0
}

View File

@ -114,12 +114,13 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}
numFields := current.NumField()
rules := v.rules[typ]
var ctag *cTag
var fld reflect.StructField
var tag string
var customName string
for i := 0; i < numFields; i++ {
fld = typ.Field(i)
@ -128,7 +129,11 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
continue
}
tag = fld.Tag.Get(v.tagName)
if rtag, ok := rules[fld.Name]; ok {
tag = rtag
} else {
tag = fld.Tag.Get(v.tagName)
}
if tag == skipValidationTag {
continue

View File

@ -349,6 +349,40 @@ Example:
// require the field if the Field1 and Field2 is not present:
Usage: required_without_all=Field1 Field2
Excluded If
The field under validation must not be present or not empty only if all
the other specified fields are equal to the value following the specified
field. For strings ensures value is not "". For slices, maps, pointers,
interfaces, channels and functions ensures the value is not nil.
Usage: excluded_if
Examples:
// exclude the field if the Field1 is equal to the parameter given:
Usage: excluded_if=Field1 foobar
// exclude the field if the Field1 and Field2 is equal to the value respectively:
Usage: excluded_if=Field1 foo Field2 bar
Excluded Unless
The field under validation must not be present or empty unless all
the other specified fields are equal to the value following the specified
field. For strings ensures value is not "". For slices, maps, pointers,
interfaces, channels and functions ensures the value is not nil.
Usage: excluded_unless
Examples:
// exclude the field unless the Field1 is equal to the parameter given:
Usage: excluded_unless=Field1 foobar
// exclude the field unless the Field1 and Field2 is equal to the value respectively:
Usage: excluded_unless=Field1 foo Field2 bar
Is Default
This validates that the value is the default value and is almost the
@ -1007,6 +1041,12 @@ This validates that a string value contains a valid version 5 UUID. Uppercase U
Usage: uuid5
Universally Unique Lexicographically Sortable Identifier ULID
This validates that a string value contains a valid ULID value.
Usage: ulid
ASCII
This validates that a string value contains only ASCII characters.
@ -1255,6 +1295,13 @@ More information on https://www.iso.org/standard/60390.html
Usage: bic
RFC 1035 label
This validates that a string value is a valid dns RFC 1035 label, defined in RFC 1035.
More information on https://datatracker.ietf.org/doc/html/rfc1035
Usage: dns_rfc1035_label
TimeZone
This validates that a string value is a valid time zone based on the time zone database present on the system.
@ -1263,6 +1310,18 @@ More information on https://golang.org/pkg/time/#LoadLocation
Usage: timezone
Semantic Version
This validates that a string value is a valid semver version, defined in Semantic Versioning 2.0.0.
More information on https://semver.org/
Usage: semver
Credit Card
This validates that a string value contains a valid credit card number using Luhn algoritm.
Usage: credit_card
Alias Validators and Tags

View File

@ -10,7 +10,7 @@ const (
numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
numberRegexString = "^[0-9]+$"
hexadecimalRegexString = "^(0[xX])?[0-9a-fA-F]+$"
hexColorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
hexColorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"
rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$"
rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$"
@ -29,6 +29,17 @@ const (
uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
uUIDRFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
uLIDRegexString = "^[A-HJKMNP-TV-Z0-9]{26}$"
md4RegexString = "^[0-9a-f]{32}$"
md5RegexString = "^[0-9a-f]{32}$"
sha256RegexString = "^[0-9a-f]{64}$"
sha384RegexString = "^[0-9a-f]{96}$"
sha512RegexString = "^[0-9a-f]{128}$"
ripemd128RegexString = "^[0-9a-f]{32}$"
ripemd160RegexString = "^[0-9a-f]{40}$"
tiger128RegexString = "^[0-9a-f]{32}$"
tiger160RegexString = "^[0-9a-f]{40}$"
tiger192RegexString = "^[0-9a-f]{48}$"
aSCIIRegexString = "^[\x00-\x7F]*$"
printableASCIIRegexString = "^[\x20-\x7E]*$"
multibyteRegexString = "[^\x00-\x7F]"
@ -36,12 +47,12 @@ const (
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{0,62})(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
@ -51,6 +62,8 @@ const (
jWTRegexString = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$"
splitParamsRegexString = `'[^']*'|\S+`
bicRegexString = `^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$`
semverRegexString = `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$` // numbered capture groups https://semver.org/
dnsRegexStringRFC1035Label = "^[a-z]([-a-z0-9]*[a-z0-9]){0,62}$"
)
var (
@ -80,6 +93,17 @@ var (
uUID4RFC4122Regex = regexp.MustCompile(uUID4RFC4122RegexString)
uUID5RFC4122Regex = regexp.MustCompile(uUID5RFC4122RegexString)
uUIDRFC4122Regex = regexp.MustCompile(uUIDRFC4122RegexString)
uLIDRegex = regexp.MustCompile(uLIDRegexString)
md4Regex = regexp.MustCompile(md4RegexString)
md5Regex = regexp.MustCompile(md5RegexString)
sha256Regex = regexp.MustCompile(sha256RegexString)
sha384Regex = regexp.MustCompile(sha384RegexString)
sha512Regex = regexp.MustCompile(sha512RegexString)
ripemd128Regex = regexp.MustCompile(ripemd128RegexString)
ripemd160Regex = regexp.MustCompile(ripemd160RegexString)
tiger128Regex = regexp.MustCompile(tiger128RegexString)
tiger160Regex = regexp.MustCompile(tiger160RegexString)
tiger192Regex = regexp.MustCompile(tiger192RegexString)
aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
multibyteRegex = regexp.MustCompile(multibyteRegexString)
@ -102,4 +126,6 @@ var (
jWTRegex = regexp.MustCompile(jWTRegexString)
splitParamsRegex = regexp.MustCompile(splitParamsRegexString)
bicRegex = regexp.MustCompile(bicRegexString)
semverRegex = regexp.MustCompile(semverRegexString)
dnsRegexRFC1035Label = regexp.MustCompile(dnsRegexStringRFC1035Label)
)

View File

@ -82,7 +82,7 @@ BEGIN:
fld := namespace
var ns string
if typ != timeType {
if !typ.ConvertibleTo(timeType) {
idx := strings.Index(namespace, namespaceSeparator)

View File

@ -164,7 +164,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
typ = current.Type()
if typ != timeType {
if !typ.ConvertibleTo(timeType) {
if ct != nil {
@ -355,6 +355,10 @@ OUTER:
v.ct = ct
if ct.fn(ctx, v) {
if ct.isBlockEnd {
ct = ct.next
continue OUTER
}
// drain rest of the 'or' values, then continue or leave
for {
@ -368,6 +372,11 @@ OUTER:
if ct.typeof != typeOr {
continue OUTER
}
if ct.isBlockEnd {
ct = ct.next
continue OUTER
}
}
}

View File

@ -33,6 +33,8 @@ const (
excludedWithoutTag = "excluded_without"
excludedWithTag = "excluded_with"
excludedWithAllTag = "excluded_with_all"
excludedIfTag = "excluded_if"
excludedUnlessTag = "excluded_unless"
skipValidationTag = "-"
diveTag = "dive"
keysTag = "keys"
@ -84,6 +86,7 @@ type Validate struct {
aliases map[string]string
validations map[string]internalValidationFuncWrapper
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
rules map[reflect.Type]map[string]string
tagCache *tagCache
structCache *structCache
}
@ -120,7 +123,7 @@ func New() *Validate {
switch k {
// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag:
_ = v.registerValidation(k, wrapFunc(val), true, true)
default:
// no need to error check here, baked in will always be valid
@ -152,15 +155,24 @@ func (v *Validate) SetTagName(name string) {
func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
errs := make(map[string]interface{})
for field, rule := range rules {
if reflect.ValueOf(rule).Kind() == reflect.Map && reflect.ValueOf(data[field]).Kind() == reflect.Map {
err := v.ValidateMapCtx(ctx, data[field].(map[string]interface{}), rule.(map[string]interface{}))
if len(err) > 0 {
errs[field] = err
if ruleObj, ok := rule.(map[string]interface{}); ok {
if dataObj, ok := data[field].(map[string]interface{}); ok {
err := v.ValidateMapCtx(ctx, dataObj, ruleObj)
if len(err) > 0 {
errs[field] = err
}
} else if dataObjs, ok := data[field].([]map[string]interface{}); ok {
for _, obj := range dataObjs {
err := v.ValidateMapCtx(ctx, obj, ruleObj)
if len(err) > 0 {
errs[field] = err
}
}
} else {
errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
}
} else if reflect.ValueOf(rule).Kind() == reflect.Map {
errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
} else {
err := v.VarCtx(ctx, data[field], rule.(string))
} else if ruleStr, ok := rule.(string); ok {
err := v.VarCtx(ctx, data[field], ruleStr)
if err != nil {
errs[field] = err
}
@ -169,7 +181,7 @@ func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{
return errs
}
// ValidateMap validates map data form a map of tags
// ValidateMap validates map data from a map of tags
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
return v.ValidateMapCtx(context.Background(), data, rules)
}
@ -180,6 +192,7 @@ func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]int
//
// validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
// name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
// // skip if tag key says it should be ignored
// if name == "-" {
// return ""
// }
@ -271,6 +284,34 @@ func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...i
}
}
// RegisterStructValidationMapRules registers validate map rules.
// Be aware that map validation rules supersede those defined on a/the struct if present.
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
if v.rules == nil {
v.rules = make(map[reflect.Type]map[string]string)
}
deepCopyRules := make(map[string]string)
for i, rule := range rules {
deepCopyRules[i] = rule
}
for _, t := range types {
typ := reflect.TypeOf(t)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
continue
}
v.rules[typ] = deepCopyRules
}
}
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
@ -331,7 +372,7 @@ func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
val = val.Elem()
}
if val.Kind() != reflect.Struct || val.Type() == timeType {
if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)}
}
@ -376,7 +417,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt
val = val.Elem()
}
if val.Kind() != reflect.Struct || val.Type() == timeType {
if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)}
}
@ -424,7 +465,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
val = val.Elem()
}
if val.Kind() != reflect.Struct || val.Type() == timeType {
if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)}
}
@ -514,7 +555,7 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ..
val = val.Elem()
}
if val.Kind() != reflect.Struct || val.Type() == timeType {
if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
return &InvalidValidationError{Type: reflect.TypeOf(s)}
}