Add hostname_rfc1123 validation

pull/343/head
Dean Karn 7 years ago
parent 3620d3c069
commit 5e036656b8
  1. 126
      README.md
  2. 171
      baked_in.go
  3. 4
      cache.go
  4. 10
      doc.go
  5. 6
      regexes.go
  6. 2
      util.go
  7. 28
      validator.go
  8. 44
      validator_instance.go
  9. 131
      validator_test.go

@ -66,69 +66,71 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa
Benchmarks Benchmarks
------ ------
###### Run on MacBook Pro (15-inch, 2017) Go version go1.9.2 darwin/amd64 ###### Run on MacBook Pro (15-inch, 2017) Go version go1.9.4 darwin/amd64
```go ```go
go test -bench=. -benchmem=true goos: darwin
BenchmarkFieldSuccess-8 20000000 79.9 ns/op 0 B/op 0 allocs/op goarch: amd64
BenchmarkFieldSuccessParallel-8 50000000 25.0 ns/op 0 B/op 0 allocs/op pkg: github.com/go-playground/validator
BenchmarkFieldFailure-8 5000000 281 ns/op 208 B/op 4 allocs/op BenchmarkFieldSuccess-8 20000000 86.4 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailureParallel-8 20000000 97.0 ns/op 208 B/op 4 allocs/op BenchmarkFieldSuccessParallel-8 50000000 27.6 ns/op 0 B/op 0 allocs/op
BenchmarkFieldArrayDiveSuccess-8 3000000 591 ns/op 201 B/op 11 allocs/op BenchmarkFieldFailure-8 5000000 297 ns/op 208 B/op 4 allocs/op
BenchmarkFieldArrayDiveSuccessParallel-8 10000000 195 ns/op 201 B/op 11 allocs/op BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op
BenchmarkFieldArrayDiveFailure-8 2000000 878 ns/op 412 B/op 16 allocs/op BenchmarkFieldArrayDiveSuccess-8 2000000 618 ns/op 201 B/op 11 allocs/op
BenchmarkFieldArrayDiveFailureParallel-8 5000000 274 ns/op 413 B/op 16 allocs/op BenchmarkFieldArrayDiveSuccessParallel-8 10000000 225 ns/op 201 B/op 11 allocs/op
BenchmarkFieldMapDiveSuccess-8 1000000 1279 ns/op 432 B/op 18 allocs/op BenchmarkFieldArrayDiveFailure-8 2000000 863 ns/op 412 B/op 16 allocs/op
BenchmarkFieldMapDiveSuccessParallel-8 5000000 401 ns/op 432 B/op 18 allocs/op BenchmarkFieldArrayDiveFailureParallel-8 5000000 322 ns/op 413 B/op 16 allocs/op
BenchmarkFieldMapDiveFailure-8 1000000 1060 ns/op 512 B/op 16 allocs/op BenchmarkFieldMapDiveSuccess-8 1000000 1336 ns/op 432 B/op 18 allocs/op
BenchmarkFieldMapDiveFailureParallel-8 5000000 334 ns/op 512 B/op 16 allocs/op BenchmarkFieldMapDiveSuccessParallel-8 3000000 474 ns/op 432 B/op 18 allocs/op
BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1462 ns/op 480 B/op 21 allocs/op BenchmarkFieldMapDiveFailure-8 1000000 1103 ns/op 512 B/op 16 allocs/op
BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 463 ns/op 480 B/op 21 allocs/op BenchmarkFieldMapDiveFailureParallel-8 5000000 412 ns/op 512 B/op 16 allocs/op
BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1414 ns/op 721 B/op 21 allocs/op BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1572 ns/op 480 B/op 21 allocs/op
BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 446 ns/op 721 B/op 21 allocs/op BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 615 ns/op 480 B/op 21 allocs/op
BenchmarkFieldCustomTypeSuccess-8 10000000 211 ns/op 32 B/op 2 allocs/op BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1438 ns/op 721 B/op 21 allocs/op
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 65.9 ns/op 32 B/op 2 allocs/op BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 543 ns/op 721 B/op 21 allocs/op
BenchmarkFieldCustomTypeFailure-8 5000000 270 ns/op 208 B/op 4 allocs/op BenchmarkFieldCustomTypeSuccess-8 10000000 230 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.3 ns/op 208 B/op 4 allocs/op BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.5 ns/op 32 B/op 2 allocs/op
BenchmarkFieldOrTagSuccess-8 2000000 729 ns/op 16 B/op 1 allocs/op BenchmarkFieldCustomTypeFailure-8 5000000 284 ns/op 208 B/op 4 allocs/op
BenchmarkFieldOrTagSuccessParallel-8 5000000 367 ns/op 16 B/op 1 allocs/op BenchmarkFieldCustomTypeFailureParallel-8 20000000 118 ns/op 208 B/op 4 allocs/op
BenchmarkFieldOrTagFailure-8 3000000 472 ns/op 224 B/op 5 allocs/op BenchmarkFieldOrTagSuccess-8 2000000 824 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagFailureParallel-8 5000000 373 ns/op 224 B/op 5 allocs/op BenchmarkFieldOrTagSuccessParallel-8 3000000 472 ns/op 16 B/op 1 allocs/op
BenchmarkStructLevelValidationSuccess-8 10000000 201 ns/op 32 B/op 2 allocs/op BenchmarkFieldOrTagFailure-8 3000000 487 ns/op 224 B/op 5 allocs/op
BenchmarkStructLevelValidationSuccessParallel-8 20000000 66.3 ns/op 32 B/op 2 allocs/op BenchmarkFieldOrTagFailureParallel-8 5000000 405 ns/op 224 B/op 5 allocs/op
BenchmarkStructLevelValidationFailure-8 3000000 468 ns/op 304 B/op 8 allocs/op BenchmarkStructLevelValidationSuccess-8 10000000 214 ns/op 32 B/op 2 allocs/op
BenchmarkStructLevelValidationFailureParallel-8 10000000 172 ns/op 304 B/op 8 allocs/op BenchmarkStructLevelValidationSuccessParallel-8 20000000 78.0 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-8 5000000 376 ns/op 32 B/op 2 allocs/op BenchmarkStructLevelValidationFailure-8 3000000 475 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCustomTypeSuccessParallel-8 20000000 126 ns/op 32 B/op 2 allocs/op BenchmarkStructLevelValidationFailureParallel-8 10000000 200 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCustomTypeFailure-8 2000000 646 ns/op 424 B/op 9 allocs/op BenchmarkStructSimpleCustomTypeSuccess-8 3000000 403 ns/op 32 B/op 2 allocs/op
BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 240 ns/op 440 B/op 10 allocs/op BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 143 ns/op 32 B/op 2 allocs/op
BenchmarkStructFilteredSuccess-8 3000000 582 ns/op 288 B/op 9 allocs/op BenchmarkStructSimpleCustomTypeFailure-8 2000000 655 ns/op 424 B/op 9 allocs/op
BenchmarkStructFilteredSuccessParallel-8 10000000 198 ns/op 288 B/op 9 allocs/op BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 286 ns/op 440 B/op 10 allocs/op
BenchmarkStructFilteredFailure-8 3000000 447 ns/op 256 B/op 7 allocs/op BenchmarkStructFilteredSuccess-8 2000000 598 ns/op 288 B/op 9 allocs/op
BenchmarkStructFilteredFailureParallel-8 10000000 156 ns/op 256 B/op 7 allocs/op BenchmarkStructFilteredSuccessParallel-8 10000000 231 ns/op 288 B/op 9 allocs/op
BenchmarkStructPartialSuccess-8 3000000 536 ns/op 256 B/op 6 allocs/op BenchmarkStructFilteredFailure-8 3000000 455 ns/op 256 B/op 7 allocs/op
BenchmarkStructPartialSuccessParallel-8 10000000 175 ns/op 256 B/op 6 allocs/op BenchmarkStructFilteredFailureParallel-8 10000000 197 ns/op 256 B/op 7 allocs/op
BenchmarkStructPartialFailure-8 2000000 738 ns/op 480 B/op 11 allocs/op BenchmarkStructPartialSuccess-8 3000000 552 ns/op 256 B/op 6 allocs/op
BenchmarkStructPartialFailureParallel-8 5000000 256 ns/op 480 B/op 11 allocs/op BenchmarkStructPartialSuccessParallel-8 10000000 206 ns/op 256 B/op 6 allocs/op
BenchmarkStructExceptSuccess-8 2000000 835 ns/op 496 B/op 12 allocs/op BenchmarkStructPartialFailure-8 2000000 750 ns/op 480 B/op 11 allocs/op
BenchmarkStructExceptSuccessParallel-8 10000000 163 ns/op 240 B/op 5 allocs/op BenchmarkStructPartialFailureParallel-8 5000000 317 ns/op 480 B/op 11 allocs/op
BenchmarkStructExceptFailure-8 2000000 682 ns/op 464 B/op 10 allocs/op BenchmarkStructExceptSuccess-8 2000000 853 ns/op 496 B/op 12 allocs/op
BenchmarkStructExceptFailureParallel-8 10000000 244 ns/op 464 B/op 10 allocs/op BenchmarkStructExceptSuccessParallel-8 10000000 179 ns/op 240 B/op 5 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-8 5000000 392 ns/op 72 B/op 3 allocs/op BenchmarkStructExceptFailure-8 2000000 698 ns/op 464 B/op 10 allocs/op
BenchmarkStructSimpleCrossFieldSuccessParallel-8 20000000 126 ns/op 72 B/op 3 allocs/op BenchmarkStructExceptFailureParallel-8 5000000 276 ns/op 464 B/op 10 allocs/op
BenchmarkStructSimpleCrossFieldFailure-8 2000000 611 ns/op 304 B/op 8 allocs/op BenchmarkStructSimpleCrossFieldSuccess-8 3000000 412 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 214 ns/op 304 B/op 8 allocs/op BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 148 ns/op 72 B/op 3 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 567 ns/op 80 B/op 4 allocs/op BenchmarkStructSimpleCrossFieldFailure-8 2000000 630 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 177 ns/op 80 B/op 4 allocs/op BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 244 ns/op 304 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 807 ns/op 320 B/op 9 allocs/op BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 610 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 268 ns/op 320 B/op 9 allocs/op BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 205 ns/op 80 B/op 4 allocs/op
BenchmarkStructSimpleSuccess-8 5000000 256 ns/op 0 B/op 0 allocs/op BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 861 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleSuccessParallel-8 20000000 76.3 ns/op 0 B/op 0 allocs/op BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 315 ns/op 320 B/op 9 allocs/op
BenchmarkStructSimpleFailure-8 2000000 625 ns/op 424 B/op 9 allocs/op BenchmarkStructSimpleSuccess-8 5000000 279 ns/op 0 B/op 0 allocs/op
BenchmarkStructSimpleFailureParallel-8 10000000 219 ns/op 424 B/op 9 allocs/op BenchmarkStructSimpleSuccessParallel-8 20000000 86.4 ns/op 0 B/op 0 allocs/op
BenchmarkStructComplexSuccess-8 1000000 1431 ns/op 128 B/op 8 allocs/op BenchmarkStructSimpleFailure-8 2000000 636 ns/op 424 B/op 9 allocs/op
BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op BenchmarkStructSimpleFailureParallel-8 10000000 264 ns/op 424 B/op 9 allocs/op
BenchmarkStructComplexFailure-8 300000 4065 ns/op 3041 B/op 53 allocs/op BenchmarkStructComplexSuccess-8 1000000 1539 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexFailureParallel-8 1000000 1478 ns/op 3041 B/op 53 allocs/op BenchmarkStructComplexSuccessParallel-8 3000000 557 ns/op 128 B/op 8 allocs/op
BenchmarkStructComplexFailure-8 300000 4136 ns/op 3041 B/op 53 allocs/op
BenchmarkStructComplexFailureParallel-8 1000000 1855 ns/op 3041 B/op 53 allocs/op
``` ```
Complementary Software Complementary Software

@ -55,85 +55,86 @@ var (
// you can add, remove or even replace items to suite your needs, // you can add, remove or even replace items to suite your needs,
// or even disregard and use your own map if so desired. // or even disregard and use your own map if so desired.
bakedInValidators = map[string]Func{ bakedInValidators = map[string]Func{
"required": hasValue, "required": hasValue,
"isdefault": isDefault, "isdefault": isDefault,
"len": hasLengthOf, "len": hasLengthOf,
"min": hasMinOf, "min": hasMinOf,
"max": hasMaxOf, "max": hasMaxOf,
"eq": isEq, "eq": isEq,
"ne": isNe, "ne": isNe,
"lt": isLt, "lt": isLt,
"lte": isLte, "lte": isLte,
"gt": isGt, "gt": isGt,
"gte": isGte, "gte": isGte,
"eqfield": isEqField, "eqfield": isEqField,
"eqcsfield": isEqCrossStructField, "eqcsfield": isEqCrossStructField,
"necsfield": isNeCrossStructField, "necsfield": isNeCrossStructField,
"gtcsfield": isGtCrossStructField, "gtcsfield": isGtCrossStructField,
"gtecsfield": isGteCrossStructField, "gtecsfield": isGteCrossStructField,
"ltcsfield": isLtCrossStructField, "ltcsfield": isLtCrossStructField,
"ltecsfield": isLteCrossStructField, "ltecsfield": isLteCrossStructField,
"nefield": isNeField, "nefield": isNeField,
"gtefield": isGteField, "gtefield": isGteField,
"gtfield": isGtField, "gtfield": isGtField,
"ltefield": isLteField, "ltefield": isLteField,
"ltfield": isLtField, "ltfield": isLtField,
"alpha": isAlpha, "alpha": isAlpha,
"alphanum": isAlphanum, "alphanum": isAlphanum,
"alphaunicode": isAlphaUnicode, "alphaunicode": isAlphaUnicode,
"alphanumunicode": isAlphanumUnicode, "alphanumunicode": isAlphanumUnicode,
"numeric": isNumeric, "numeric": isNumeric,
"number": isNumber, "number": isNumber,
"hexadecimal": isHexadecimal, "hexadecimal": isHexadecimal,
"hexcolor": isHEXColor, "hexcolor": isHEXColor,
"rgb": isRGB, "rgb": isRGB,
"rgba": isRGBA, "rgba": isRGBA,
"hsl": isHSL, "hsl": isHSL,
"hsla": isHSLA, "hsla": isHSLA,
"email": isEmail, "email": isEmail,
"url": isURL, "url": isURL,
"uri": isURI, "uri": isURI,
"base64": isBase64, "base64": isBase64,
"contains": contains, "contains": contains,
"containsany": containsAny, "containsany": containsAny,
"containsrune": containsRune, "containsrune": containsRune,
"excludes": excludes, "excludes": excludes,
"excludesall": excludesAll, "excludesall": excludesAll,
"excludesrune": excludesRune, "excludesrune": excludesRune,
"isbn": isISBN, "isbn": isISBN,
"isbn10": isISBN10, "isbn10": isISBN10,
"isbn13": isISBN13, "isbn13": isISBN13,
"uuid": isUUID, "uuid": isUUID,
"uuid3": isUUID3, "uuid3": isUUID3,
"uuid4": isUUID4, "uuid4": isUUID4,
"uuid5": isUUID5, "uuid5": isUUID5,
"ascii": isASCII, "ascii": isASCII,
"printascii": isPrintableASCII, "printascii": isPrintableASCII,
"multibyte": hasMultiByteCharacter, "multibyte": hasMultiByteCharacter,
"datauri": isDataURI, "datauri": isDataURI,
"latitude": isLatitude, "latitude": isLatitude,
"longitude": isLongitude, "longitude": isLongitude,
"ssn": isSSN, "ssn": isSSN,
"ipv4": isIPv4, "ipv4": isIPv4,
"ipv6": isIPv6, "ipv6": isIPv6,
"ip": isIP, "ip": isIP,
"cidrv4": isCIDRv4, "cidrv4": isCIDRv4,
"cidrv6": isCIDRv6, "cidrv6": isCIDRv6,
"cidr": isCIDR, "cidr": isCIDR,
"tcp4_addr": isTCP4AddrResolvable, "tcp4_addr": isTCP4AddrResolvable,
"tcp6_addr": isTCP6AddrResolvable, "tcp6_addr": isTCP6AddrResolvable,
"tcp_addr": isTCPAddrResolvable, "tcp_addr": isTCPAddrResolvable,
"udp4_addr": isUDP4AddrResolvable, "udp4_addr": isUDP4AddrResolvable,
"udp6_addr": isUDP6AddrResolvable, "udp6_addr": isUDP6AddrResolvable,
"udp_addr": isUDPAddrResolvable, "udp_addr": isUDPAddrResolvable,
"ip4_addr": isIP4AddrResolvable, "ip4_addr": isIP4AddrResolvable,
"ip6_addr": isIP6AddrResolvable, "ip6_addr": isIP6AddrResolvable,
"ip_addr": isIPAddrResolvable, "ip_addr": isIPAddrResolvable,
"unix_addr": isUnixAddrResolvable, "unix_addr": isUnixAddrResolvable,
"mac": isMAC, "mac": isMAC,
"hostname": isHostname, "hostname": isHostnameRFC952, // RFC 952
"fqdn": isFQDN, "hostname_rfc1123": isHostnameRFC1123, // RFC 1123
"unique": isUnique, "fqdn": isFQDN,
"unique": isUnique,
} }
) )
@ -1511,8 +1512,12 @@ func isIP6Addr(fl FieldLevel) bool {
return ip != nil && ip.To4() == nil return ip != nil && ip.To4() == nil
} }
func isHostname(fl FieldLevel) bool { func isHostnameRFC952(fl FieldLevel) bool {
return hostnameRegex.MatchString(fl.Field().String()) return hostnameRegexRFC952.MatchString(fl.Field().String())
}
func isHostnameRFC1123(fl FieldLevel) bool {
return hostnameRegexRFC1123.MatchString(fl.Field().String())
} }
func isFQDN(fl FieldLevel) bool { func isFQDN(fl FieldLevel) bool {
@ -1526,6 +1531,6 @@ func isFQDN(fl FieldLevel) bool {
val = val[0 : len(val)-1] val = val[0 : len(val)-1]
} }
return (strings.IndexAny(val, ".") > -1) && return strings.ContainsAny(val, ".") &&
hostnameRegex.MatchString(val) hostnameRegexRFC952.MatchString(val)
} }

@ -91,14 +91,14 @@ type cTag struct {
aliasTag string aliasTag string
actualAliasTag string actualAliasTag string
param string param string
typeof tagType
keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation
next *cTag next *cTag
fn FuncCtx
typeof tagType
hasTag bool hasTag bool
hasAlias bool hasAlias bool
hasParam bool // true if parameter used eg. eq= where the equal sign has been set hasParam bool // true if parameter used eg. eq= where the equal sign has been set
isBlockEnd bool // indicates the current tag represents the last validation in the block isBlockEnd bool // indicates the current tag represents the last validation in the block
fn FuncCtx
} }
func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct {

@ -832,12 +832,18 @@ Note: See Go's ParseMAC for accepted formats and types:
http://golang.org/src/net/mac.go?s=866:918#L29 http://golang.org/src/net/mac.go?s=866:918#L29
Hostname Hostname RFC 952
This validates that a string value is a valid Hostname This validates that a string value is a valid Hostname according to RFC 952 https://tools.ietf.org/html/rfc952
Usage: hostname Usage: hostname
Hostname RFC 1123
This validates that a string value is a valid Hostname according to RFC 1123 https://tools.ietf.org/html/rfc1123
Usage: hostname_rfc1123 or if you want to continue to use 'hostname' in your tags, create an alias.
Full Qualified Domain Name (FQDN) Full Qualified Domain Name (FQDN)
This validates that a string value contains a valid FQDN. This validates that a string value contains a valid FQDN.

@ -30,7 +30,8 @@ const (
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
hostnameRegexString = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` hostnameRegexStringRFC952 = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` // https://tools.ietf.org/html/rfc952
hostnameRegexStringRFC1123 = `^[a-zA-Z0-9][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
) )
var ( var (
@ -61,5 +62,6 @@ var (
latitudeRegex = regexp.MustCompile(latitudeRegexString) latitudeRegex = regexp.MustCompile(latitudeRegexString)
longitudeRegex = regexp.MustCompile(longitudeRegexString) longitudeRegex = regexp.MustCompile(longitudeRegexString)
sSNRegex = regexp.MustCompile(sSNRegexString) sSNRegex = regexp.MustCompile(sSNRegexString)
hostnameRegex = regexp.MustCompile(hostnameRegexString) hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952)
hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123)
) )

@ -80,7 +80,7 @@ BEGIN:
typ := current.Type() typ := current.Type()
fld := namespace fld := namespace
ns := namespace var ns string
if typ != timeType { if typ != timeType {

@ -14,24 +14,19 @@ type validate struct {
ns []byte ns []byte
actualNs []byte actualNs []byte
errs ValidationErrors errs ValidationErrors
includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
ffn FilterFunc
slflParent reflect.Value // StructLevel & FieldLevel
slCurrent reflect.Value // StructLevel & FieldLevel
flField reflect.Value // StructLevel & FieldLevel
cf *cField // StructLevel & FieldLevel
ct *cTag // StructLevel & FieldLevel
misc []byte // misc reusable
str1 string // misc reusable
str2 string // misc reusable
fldIsPointer bool // StructLevel & FieldLevel
isPartial bool isPartial bool
hasExcludes bool hasExcludes bool
includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
ffn FilterFunc
// StructLevel & FieldLevel fields
slflParent reflect.Value
slCurrent reflect.Value
flField reflect.Value
fldIsPointer bool
cf *cField
ct *cTag
// misc reusable values
misc []byte
str1 string
str2 string
} }
// parent and current will be the same the first run of validateStruct // parent and current will be the same the first run of validateStruct
@ -127,7 +122,6 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
} }
if kind == reflect.Invalid { if kind == reflect.Invalid {
v.errs = append(v.errs, v.errs = append(v.errs,
&fieldError{ &fieldError{
v: v.v, v: v.v,

@ -370,39 +370,37 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
typ := val.Type() typ := val.Type()
name := typ.Name() name := typ.Name()
if fields != nil { for _, k := range fields {
for _, k := range fields {
flds := strings.Split(k, namespaceSeparator) flds := strings.Split(k, namespaceSeparator)
if len(flds) > 0 { if len(flds) > 0 {
vd.misc = append(vd.misc[0:0], name...) vd.misc = append(vd.misc[0:0], name...)
vd.misc = append(vd.misc, '.') vd.misc = append(vd.misc, '.')
for _, s := range flds {
idx := strings.Index(s, leftBracket) for _, s := range flds {
if idx != -1 { idx := strings.Index(s, leftBracket)
for idx != -1 {
vd.misc = append(vd.misc, s[:idx]...)
vd.includeExclude[string(vd.misc)] = struct{}{}
idx2 := strings.Index(s, rightBracket) if idx != -1 {
idx2++ for idx != -1 {
vd.misc = append(vd.misc, s[idx:idx2]...) vd.misc = append(vd.misc, s[:idx]...)
vd.includeExclude[string(vd.misc)] = struct{}{} vd.includeExclude[string(vd.misc)] = struct{}{}
s = s[idx2:]
idx = strings.Index(s, leftBracket)
}
} else {
vd.misc = append(vd.misc, s...) idx2 := strings.Index(s, rightBracket)
idx2++
vd.misc = append(vd.misc, s[idx:idx2]...)
vd.includeExclude[string(vd.misc)] = struct{}{} vd.includeExclude[string(vd.misc)] = struct{}{}
s = s[idx2:]
idx = strings.Index(s, leftBracket)
} }
} else {
vd.misc = append(vd.misc, '.') vd.misc = append(vd.misc, s...)
vd.includeExclude[string(vd.misc)] = struct{}{}
} }
vd.misc = append(vd.misc, '.')
} }
} }
} }

@ -1564,10 +1564,6 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
Name string Name string
} }
type MapStruct struct {
Name string
}
type Inner struct { type Inner struct {
CreatedAt *time.Time CreatedAt *time.Time
Slice []string Slice []string
@ -1653,10 +1649,10 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
Equal(t, kind, reflect.String) Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2") Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField") current, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField")
Equal(t, ok, false) Equal(t, ok, false)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]") current, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]")
Equal(t, ok, false) Equal(t, ok, false)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]") current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]")
@ -1854,8 +1850,6 @@ func TestSQLValue2Validation(t *testing.T) {
PanicMatches(t, func() { validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error") PanicMatches(t, func() { validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error")
type myValuer valuer
myVal := valuer{ myVal := valuer{
Name: "", Name: "",
} }
@ -1907,8 +1901,6 @@ func TestSQLValueValidation(t *testing.T) {
PanicMatches(t, func() { errs = validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error") PanicMatches(t, func() { errs = validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error")
type myValuer valuer
myVal := valuer{ myVal := valuer{
Name: "", Name: "",
} }
@ -2696,11 +2688,8 @@ func TestBadKeyValidation(t *testing.T) {
func TestInterfaceErrValidation(t *testing.T) { func TestInterfaceErrValidation(t *testing.T) {
var v1 interface{} var v2 interface{} = 1
var v2 interface{} var v1 interface{} = v2
v2 = 1
v1 = v2
validate := New() validate := New()
errs := validate.Var(v1, "len=1") errs := validate.Var(v1, "len=1")
@ -7146,7 +7135,7 @@ func TestValidateStructRegisterCtx(t *testing.T) {
Equal(t, ctxSlVal, "slVal") Equal(t, ctxSlVal, "slVal")
} }
func TestHostnameValidation(t *testing.T) { func TestHostnameRFC952Validation(t *testing.T) {
tests := []struct { tests := []struct {
param string param string
expected bool expected bool
@ -7157,6 +7146,7 @@ func TestHostnameValidation(t *testing.T) {
{"test.example24.com", true}, {"test.example24.com", true},
{"test24.example24.com", true}, {"test24.example24.com", true},
{"example", true}, {"example", true},
{"1.foo.com", false},
{"test.example.com.", false}, {"test.example.com.", false},
{"example.com.", false}, {"example.com.", false},
{"example24.com.", false}, {"example24.com.", false},
@ -7178,15 +7168,112 @@ func TestHostnameValidation(t *testing.T) {
if test.expected { if test.expected {
if !IsEqual(errs, nil) { if !IsEqual(errs, nil) {
t.Fatalf("Index: %d hostname failed Error: %s", i, errs) t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "hostname" {
t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
}
}
}
}
}
func TestHostnameRFC1123Validation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"test.example.com", true},
{"example.com", true},
{"example24.com", true},
{"test.example24.com", true},
{"test24.example24.com", true},
{"example", true},
{"1.foo.com", true},
{"test.example.com.", false},
{"example.com.", false},
{"example24.com.", false},
{"test.example24.com.", false},
{"test24.example24.com.", false},
{"example.", false},
{"192.168.0.1", true},
{"email@example.com", false},
{"2001:cdba:0000:0000:0000:0000:3257:9652", false},
{"2001:cdba:0:0:0:0:3257:9652", false},
{"2001:cdba::3257:9652", false},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.param, "hostname_rfc1123")
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "hostname_rfc1123" {
t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
}
}
}
}
}
func TestHostnameRFC1123AliasValidation(t *testing.T) {
tests := []struct {
param string
expected bool
}{
{"test.example.com", true},
{"example.com", true},
{"example24.com", true},
{"test.example24.com", true},
{"test24.example24.com", true},
{"example", true},
{"1.foo.com", true},
{"test.example.com.", false},
{"example.com.", false},
{"example24.com.", false},
{"test.example24.com.", false},
{"test24.example24.com.", false},
{"example.", false},
{"192.168.0.1", true},
{"email@example.com", false},
{"2001:cdba:0000:0000:0000:0000:3257:9652", false},
{"2001:cdba:0:0:0:0:3257:9652", false},
{"2001:cdba::3257:9652", false},
}
validate := New()
validate.RegisterAlias("hostname", "hostname_rfc1123")
for i, test := range tests {
errs := validate.Var(test.param, "hostname")
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
} }
} else { } else {
if IsEqual(errs, nil) { if IsEqual(errs, nil) {
t.Fatalf("Index: %d hostname failed Error: %s", i, errs) t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
} else { } else {
val := getError(errs, "", "") val := getError(errs, "", "")
if val.Tag() != "hostname" { if val.Tag() != "hostname" {
t.Fatalf("Index: %d hostname failed Error: %s", i, errs) t.Fatalf("Index: %d hostname failed Error: %v", i, errs)
} }
} }
} }
@ -7226,15 +7313,15 @@ func TestFQDNValidation(t *testing.T) {
if test.expected { if test.expected {
if !IsEqual(errs, nil) { if !IsEqual(errs, nil) {
t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) t.Fatalf("Index: %d fqdn failed Error: %v", i, errs)
} }
} else { } else {
if IsEqual(errs, nil) { if IsEqual(errs, nil) {
t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) t.Fatalf("Index: %d fqdn failed Error: %v", i, errs)
} else { } else {
val := getError(errs, "", "") val := getError(errs, "", "")
if val.Tag() != "fqdn" { if val.Tag() != "fqdn" {
t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) t.Fatalf("Index: %d fqdn failed Error: %v", i, errs)
} }
} }
} }

Loading…
Cancel
Save