From 5e036656b8574372ec461f675804f9b8d1fa8320 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 15 Feb 2018 08:50:04 -0800 Subject: [PATCH] Add hostname_rfc1123 validation --- README.md | 126 ++++++++++++++++--------------- baked_in.go | 171 ++++++++++++++++++++++-------------------- cache.go | 4 +- doc.go | 10 ++- regexes.go | 6 +- util.go | 2 +- validator.go | 28 +++---- validator_instance.go | 44 ++++++----- validator_test.go | 131 ++++++++++++++++++++++++++------ 9 files changed, 308 insertions(+), 214 deletions(-) diff --git a/README.md b/README.md index 4e645cc..0475539 100644 --- a/README.md +++ b/README.md @@ -66,69 +66,71 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa 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 test -bench=. -benchmem=true -BenchmarkFieldSuccess-8 20000000 79.9 ns/op 0 B/op 0 allocs/op -BenchmarkFieldSuccessParallel-8 50000000 25.0 ns/op 0 B/op 0 allocs/op -BenchmarkFieldFailure-8 5000000 281 ns/op 208 B/op 4 allocs/op -BenchmarkFieldFailureParallel-8 20000000 97.0 ns/op 208 B/op 4 allocs/op -BenchmarkFieldArrayDiveSuccess-8 3000000 591 ns/op 201 B/op 11 allocs/op -BenchmarkFieldArrayDiveSuccessParallel-8 10000000 195 ns/op 201 B/op 11 allocs/op -BenchmarkFieldArrayDiveFailure-8 2000000 878 ns/op 412 B/op 16 allocs/op -BenchmarkFieldArrayDiveFailureParallel-8 5000000 274 ns/op 413 B/op 16 allocs/op -BenchmarkFieldMapDiveSuccess-8 1000000 1279 ns/op 432 B/op 18 allocs/op -BenchmarkFieldMapDiveSuccessParallel-8 5000000 401 ns/op 432 B/op 18 allocs/op -BenchmarkFieldMapDiveFailure-8 1000000 1060 ns/op 512 B/op 16 allocs/op -BenchmarkFieldMapDiveFailureParallel-8 5000000 334 ns/op 512 B/op 16 allocs/op -BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1462 ns/op 480 B/op 21 allocs/op -BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 463 ns/op 480 B/op 21 allocs/op -BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1414 ns/op 721 B/op 21 allocs/op -BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 446 ns/op 721 B/op 21 allocs/op -BenchmarkFieldCustomTypeSuccess-8 10000000 211 ns/op 32 B/op 2 allocs/op -BenchmarkFieldCustomTypeSuccessParallel-8 20000000 65.9 ns/op 32 B/op 2 allocs/op -BenchmarkFieldCustomTypeFailure-8 5000000 270 ns/op 208 B/op 4 allocs/op -BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.3 ns/op 208 B/op 4 allocs/op -BenchmarkFieldOrTagSuccess-8 2000000 729 ns/op 16 B/op 1 allocs/op -BenchmarkFieldOrTagSuccessParallel-8 5000000 367 ns/op 16 B/op 1 allocs/op -BenchmarkFieldOrTagFailure-8 3000000 472 ns/op 224 B/op 5 allocs/op -BenchmarkFieldOrTagFailureParallel-8 5000000 373 ns/op 224 B/op 5 allocs/op -BenchmarkStructLevelValidationSuccess-8 10000000 201 ns/op 32 B/op 2 allocs/op -BenchmarkStructLevelValidationSuccessParallel-8 20000000 66.3 ns/op 32 B/op 2 allocs/op -BenchmarkStructLevelValidationFailure-8 3000000 468 ns/op 304 B/op 8 allocs/op -BenchmarkStructLevelValidationFailureParallel-8 10000000 172 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCustomTypeSuccess-8 5000000 376 ns/op 32 B/op 2 allocs/op -BenchmarkStructSimpleCustomTypeSuccessParallel-8 20000000 126 ns/op 32 B/op 2 allocs/op -BenchmarkStructSimpleCustomTypeFailure-8 2000000 646 ns/op 424 B/op 9 allocs/op -BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 240 ns/op 440 B/op 10 allocs/op -BenchmarkStructFilteredSuccess-8 3000000 582 ns/op 288 B/op 9 allocs/op -BenchmarkStructFilteredSuccessParallel-8 10000000 198 ns/op 288 B/op 9 allocs/op -BenchmarkStructFilteredFailure-8 3000000 447 ns/op 256 B/op 7 allocs/op -BenchmarkStructFilteredFailureParallel-8 10000000 156 ns/op 256 B/op 7 allocs/op -BenchmarkStructPartialSuccess-8 3000000 536 ns/op 256 B/op 6 allocs/op -BenchmarkStructPartialSuccessParallel-8 10000000 175 ns/op 256 B/op 6 allocs/op -BenchmarkStructPartialFailure-8 2000000 738 ns/op 480 B/op 11 allocs/op -BenchmarkStructPartialFailureParallel-8 5000000 256 ns/op 480 B/op 11 allocs/op -BenchmarkStructExceptSuccess-8 2000000 835 ns/op 496 B/op 12 allocs/op -BenchmarkStructExceptSuccessParallel-8 10000000 163 ns/op 240 B/op 5 allocs/op -BenchmarkStructExceptFailure-8 2000000 682 ns/op 464 B/op 10 allocs/op -BenchmarkStructExceptFailureParallel-8 10000000 244 ns/op 464 B/op 10 allocs/op -BenchmarkStructSimpleCrossFieldSuccess-8 5000000 392 ns/op 72 B/op 3 allocs/op -BenchmarkStructSimpleCrossFieldSuccessParallel-8 20000000 126 ns/op 72 B/op 3 allocs/op -BenchmarkStructSimpleCrossFieldFailure-8 2000000 611 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 214 ns/op 304 B/op 8 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 567 ns/op 80 B/op 4 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 177 ns/op 80 B/op 4 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 807 ns/op 320 B/op 9 allocs/op -BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 268 ns/op 320 B/op 9 allocs/op -BenchmarkStructSimpleSuccess-8 5000000 256 ns/op 0 B/op 0 allocs/op -BenchmarkStructSimpleSuccessParallel-8 20000000 76.3 ns/op 0 B/op 0 allocs/op -BenchmarkStructSimpleFailure-8 2000000 625 ns/op 424 B/op 9 allocs/op -BenchmarkStructSimpleFailureParallel-8 10000000 219 ns/op 424 B/op 9 allocs/op -BenchmarkStructComplexSuccess-8 1000000 1431 ns/op 128 B/op 8 allocs/op -BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op -BenchmarkStructComplexFailure-8 300000 4065 ns/op 3041 B/op 53 allocs/op -BenchmarkStructComplexFailureParallel-8 1000000 1478 ns/op 3041 B/op 53 allocs/op +goos: darwin +goarch: amd64 +pkg: github.com/go-playground/validator +BenchmarkFieldSuccess-8 20000000 86.4 ns/op 0 B/op 0 allocs/op +BenchmarkFieldSuccessParallel-8 50000000 27.6 ns/op 0 B/op 0 allocs/op +BenchmarkFieldFailure-8 5000000 297 ns/op 208 B/op 4 allocs/op +BenchmarkFieldFailureParallel-8 20000000 107 ns/op 208 B/op 4 allocs/op +BenchmarkFieldArrayDiveSuccess-8 2000000 618 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveSuccessParallel-8 10000000 225 ns/op 201 B/op 11 allocs/op +BenchmarkFieldArrayDiveFailure-8 2000000 863 ns/op 412 B/op 16 allocs/op +BenchmarkFieldArrayDiveFailureParallel-8 5000000 322 ns/op 413 B/op 16 allocs/op +BenchmarkFieldMapDiveSuccess-8 1000000 1336 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveSuccessParallel-8 3000000 474 ns/op 432 B/op 18 allocs/op +BenchmarkFieldMapDiveFailure-8 1000000 1103 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveFailureParallel-8 5000000 412 ns/op 512 B/op 16 allocs/op +BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1572 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 615 ns/op 480 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1438 ns/op 721 B/op 21 allocs/op +BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 543 ns/op 721 B/op 21 allocs/op +BenchmarkFieldCustomTypeSuccess-8 10000000 230 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeSuccessParallel-8 20000000 82.5 ns/op 32 B/op 2 allocs/op +BenchmarkFieldCustomTypeFailure-8 5000000 284 ns/op 208 B/op 4 allocs/op +BenchmarkFieldCustomTypeFailureParallel-8 20000000 118 ns/op 208 B/op 4 allocs/op +BenchmarkFieldOrTagSuccess-8 2000000 824 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagSuccessParallel-8 3000000 472 ns/op 16 B/op 1 allocs/op +BenchmarkFieldOrTagFailure-8 3000000 487 ns/op 224 B/op 5 allocs/op +BenchmarkFieldOrTagFailureParallel-8 5000000 405 ns/op 224 B/op 5 allocs/op +BenchmarkStructLevelValidationSuccess-8 10000000 214 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationSuccessParallel-8 20000000 78.0 ns/op 32 B/op 2 allocs/op +BenchmarkStructLevelValidationFailure-8 3000000 475 ns/op 304 B/op 8 allocs/op +BenchmarkStructLevelValidationFailureParallel-8 10000000 200 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCustomTypeSuccess-8 3000000 403 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeSuccessParallel-8 10000000 143 ns/op 32 B/op 2 allocs/op +BenchmarkStructSimpleCustomTypeFailure-8 2000000 655 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleCustomTypeFailureParallel-8 5000000 286 ns/op 440 B/op 10 allocs/op +BenchmarkStructFilteredSuccess-8 2000000 598 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredSuccessParallel-8 10000000 231 ns/op 288 B/op 9 allocs/op +BenchmarkStructFilteredFailure-8 3000000 455 ns/op 256 B/op 7 allocs/op +BenchmarkStructFilteredFailureParallel-8 10000000 197 ns/op 256 B/op 7 allocs/op +BenchmarkStructPartialSuccess-8 3000000 552 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialSuccessParallel-8 10000000 206 ns/op 256 B/op 6 allocs/op +BenchmarkStructPartialFailure-8 2000000 750 ns/op 480 B/op 11 allocs/op +BenchmarkStructPartialFailureParallel-8 5000000 317 ns/op 480 B/op 11 allocs/op +BenchmarkStructExceptSuccess-8 2000000 853 ns/op 496 B/op 12 allocs/op +BenchmarkStructExceptSuccessParallel-8 10000000 179 ns/op 240 B/op 5 allocs/op +BenchmarkStructExceptFailure-8 2000000 698 ns/op 464 B/op 10 allocs/op +BenchmarkStructExceptFailureParallel-8 5000000 276 ns/op 464 B/op 10 allocs/op +BenchmarkStructSimpleCrossFieldSuccess-8 3000000 412 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 148 ns/op 72 B/op 3 allocs/op +BenchmarkStructSimpleCrossFieldFailure-8 2000000 630 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 244 ns/op 304 B/op 8 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 610 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 205 ns/op 80 B/op 4 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 861 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 315 ns/op 320 B/op 9 allocs/op +BenchmarkStructSimpleSuccess-8 5000000 279 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleSuccessParallel-8 20000000 86.4 ns/op 0 B/op 0 allocs/op +BenchmarkStructSimpleFailure-8 2000000 636 ns/op 424 B/op 9 allocs/op +BenchmarkStructSimpleFailureParallel-8 10000000 264 ns/op 424 B/op 9 allocs/op +BenchmarkStructComplexSuccess-8 1000000 1539 ns/op 128 B/op 8 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 diff --git a/baked_in.go b/baked_in.go index 6654094..28cd1be 100644 --- a/baked_in.go +++ b/baked_in.go @@ -55,85 +55,86 @@ var ( // you can add, remove or even replace items to suite your needs, // or even disregard and use your own map if so desired. bakedInValidators = map[string]Func{ - "required": hasValue, - "isdefault": isDefault, - "len": hasLengthOf, - "min": hasMinOf, - "max": hasMaxOf, - "eq": isEq, - "ne": isNe, - "lt": isLt, - "lte": isLte, - "gt": isGt, - "gte": isGte, - "eqfield": isEqField, - "eqcsfield": isEqCrossStructField, - "necsfield": isNeCrossStructField, - "gtcsfield": isGtCrossStructField, - "gtecsfield": isGteCrossStructField, - "ltcsfield": isLtCrossStructField, - "ltecsfield": isLteCrossStructField, - "nefield": isNeField, - "gtefield": isGteField, - "gtfield": isGtField, - "ltefield": isLteField, - "ltfield": isLtField, - "alpha": isAlpha, - "alphanum": isAlphanum, - "alphaunicode": isAlphaUnicode, - "alphanumunicode": isAlphanumUnicode, - "numeric": isNumeric, - "number": isNumber, - "hexadecimal": isHexadecimal, - "hexcolor": isHEXColor, - "rgb": isRGB, - "rgba": isRGBA, - "hsl": isHSL, - "hsla": isHSLA, - "email": isEmail, - "url": isURL, - "uri": isURI, - "base64": isBase64, - "contains": contains, - "containsany": containsAny, - "containsrune": containsRune, - "excludes": excludes, - "excludesall": excludesAll, - "excludesrune": excludesRune, - "isbn": isISBN, - "isbn10": isISBN10, - "isbn13": isISBN13, - "uuid": isUUID, - "uuid3": isUUID3, - "uuid4": isUUID4, - "uuid5": isUUID5, - "ascii": isASCII, - "printascii": isPrintableASCII, - "multibyte": hasMultiByteCharacter, - "datauri": isDataURI, - "latitude": isLatitude, - "longitude": isLongitude, - "ssn": isSSN, - "ipv4": isIPv4, - "ipv6": isIPv6, - "ip": isIP, - "cidrv4": isCIDRv4, - "cidrv6": isCIDRv6, - "cidr": isCIDR, - "tcp4_addr": isTCP4AddrResolvable, - "tcp6_addr": isTCP6AddrResolvable, - "tcp_addr": isTCPAddrResolvable, - "udp4_addr": isUDP4AddrResolvable, - "udp6_addr": isUDP6AddrResolvable, - "udp_addr": isUDPAddrResolvable, - "ip4_addr": isIP4AddrResolvable, - "ip6_addr": isIP6AddrResolvable, - "ip_addr": isIPAddrResolvable, - "unix_addr": isUnixAddrResolvable, - "mac": isMAC, - "hostname": isHostname, - "fqdn": isFQDN, - "unique": isUnique, + "required": hasValue, + "isdefault": isDefault, + "len": hasLengthOf, + "min": hasMinOf, + "max": hasMaxOf, + "eq": isEq, + "ne": isNe, + "lt": isLt, + "lte": isLte, + "gt": isGt, + "gte": isGte, + "eqfield": isEqField, + "eqcsfield": isEqCrossStructField, + "necsfield": isNeCrossStructField, + "gtcsfield": isGtCrossStructField, + "gtecsfield": isGteCrossStructField, + "ltcsfield": isLtCrossStructField, + "ltecsfield": isLteCrossStructField, + "nefield": isNeField, + "gtefield": isGteField, + "gtfield": isGtField, + "ltefield": isLteField, + "ltfield": isLtField, + "alpha": isAlpha, + "alphanum": isAlphanum, + "alphaunicode": isAlphaUnicode, + "alphanumunicode": isAlphanumUnicode, + "numeric": isNumeric, + "number": isNumber, + "hexadecimal": isHexadecimal, + "hexcolor": isHEXColor, + "rgb": isRGB, + "rgba": isRGBA, + "hsl": isHSL, + "hsla": isHSLA, + "email": isEmail, + "url": isURL, + "uri": isURI, + "base64": isBase64, + "contains": contains, + "containsany": containsAny, + "containsrune": containsRune, + "excludes": excludes, + "excludesall": excludesAll, + "excludesrune": excludesRune, + "isbn": isISBN, + "isbn10": isISBN10, + "isbn13": isISBN13, + "uuid": isUUID, + "uuid3": isUUID3, + "uuid4": isUUID4, + "uuid5": isUUID5, + "ascii": isASCII, + "printascii": isPrintableASCII, + "multibyte": hasMultiByteCharacter, + "datauri": isDataURI, + "latitude": isLatitude, + "longitude": isLongitude, + "ssn": isSSN, + "ipv4": isIPv4, + "ipv6": isIPv6, + "ip": isIP, + "cidrv4": isCIDRv4, + "cidrv6": isCIDRv6, + "cidr": isCIDR, + "tcp4_addr": isTCP4AddrResolvable, + "tcp6_addr": isTCP6AddrResolvable, + "tcp_addr": isTCPAddrResolvable, + "udp4_addr": isUDP4AddrResolvable, + "udp6_addr": isUDP6AddrResolvable, + "udp_addr": isUDPAddrResolvable, + "ip4_addr": isIP4AddrResolvable, + "ip6_addr": isIP6AddrResolvable, + "ip_addr": isIPAddrResolvable, + "unix_addr": isUnixAddrResolvable, + "mac": isMAC, + "hostname": isHostnameRFC952, // RFC 952 + "hostname_rfc1123": isHostnameRFC1123, // RFC 1123 + "fqdn": isFQDN, + "unique": isUnique, } ) @@ -1511,8 +1512,12 @@ func isIP6Addr(fl FieldLevel) bool { return ip != nil && ip.To4() == nil } -func isHostname(fl FieldLevel) bool { - return hostnameRegex.MatchString(fl.Field().String()) +func isHostnameRFC952(fl FieldLevel) bool { + return hostnameRegexRFC952.MatchString(fl.Field().String()) +} + +func isHostnameRFC1123(fl FieldLevel) bool { + return hostnameRegexRFC1123.MatchString(fl.Field().String()) } func isFQDN(fl FieldLevel) bool { @@ -1526,6 +1531,6 @@ func isFQDN(fl FieldLevel) bool { val = val[0 : len(val)-1] } - return (strings.IndexAny(val, ".") > -1) && - hostnameRegex.MatchString(val) + return strings.ContainsAny(val, ".") && + hostnameRegexRFC952.MatchString(val) } diff --git a/cache.go b/cache.go index 3906d25..c7fb0fb 100644 --- a/cache.go +++ b/cache.go @@ -91,14 +91,14 @@ type cTag struct { aliasTag string actualAliasTag string param string - typeof tagType keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation next *cTag + fn FuncCtx + typeof tagType hasTag bool hasAlias bool 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 - fn FuncCtx } func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { diff --git a/doc.go b/doc.go index f0a748d..5d26131 100644 --- a/doc.go +++ b/doc.go @@ -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 -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 +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) This validates that a string value contains a valid FQDN. diff --git a/regexes.go b/regexes.go index ec78ceb..78f3ea0 100644 --- a/regexes.go +++ b/regexes.go @@ -30,7 +30,8 @@ const ( latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" 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 ( @@ -61,5 +62,6 @@ var ( latitudeRegex = regexp.MustCompile(latitudeRegexString) longitudeRegex = regexp.MustCompile(longitudeRegexString) sSNRegex = regexp.MustCompile(sSNRegexString) - hostnameRegex = regexp.MustCompile(hostnameRegexString) + hostnameRegexRFC952 = regexp.MustCompile(hostnameRegexStringRFC952) + hostnameRegexRFC1123 = regexp.MustCompile(hostnameRegexStringRFC1123) ) diff --git a/util.go b/util.go index a01d4b1..16a5517 100644 --- a/util.go +++ b/util.go @@ -80,7 +80,7 @@ BEGIN: typ := current.Type() fld := namespace - ns := namespace + var ns string if typ != timeType { diff --git a/validator.go b/validator.go index 5fbb166..483e0a2 100644 --- a/validator.go +++ b/validator.go @@ -14,24 +14,19 @@ type validate struct { ns []byte actualNs []byte 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 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 @@ -127,7 +122,6 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr } if kind == reflect.Invalid { - v.errs = append(v.errs, &fieldError{ v: v.v, diff --git a/validator_instance.go b/validator_instance.go index f4b49a8..e84b452 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -370,39 +370,37 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields . typ := val.Type() name := typ.Name() - if fields != nil { - for _, k := range fields { + for _, k := range fields { - flds := strings.Split(k, namespaceSeparator) - if len(flds) > 0 { + flds := strings.Split(k, namespaceSeparator) + if len(flds) > 0 { - vd.misc = append(vd.misc[0:0], name...) - vd.misc = append(vd.misc, '.') - - for _, s := range flds { + vd.misc = append(vd.misc[0:0], name...) + vd.misc = append(vd.misc, '.') - idx := strings.Index(s, leftBracket) + for _, s := range flds { - if idx != -1 { - for idx != -1 { - vd.misc = append(vd.misc, s[:idx]...) - vd.includeExclude[string(vd.misc)] = struct{}{} + idx := strings.Index(s, leftBracket) - idx2 := strings.Index(s, rightBracket) - idx2++ - vd.misc = append(vd.misc, s[idx:idx2]...) - vd.includeExclude[string(vd.misc)] = struct{}{} - s = s[idx2:] - idx = strings.Index(s, leftBracket) - } - } else { + if idx != -1 { + for idx != -1 { + vd.misc = append(vd.misc, s[:idx]...) + vd.includeExclude[string(vd.misc)] = struct{}{} - 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{}{} + 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, '.') } } } diff --git a/validator_test.go b/validator_test.go index 752300f..02c1776 100644 --- a/validator_test.go +++ b/validator_test.go @@ -1564,10 +1564,6 @@ func TestCrossNamespaceFieldValidation(t *testing.T) { Name string } - type MapStruct struct { - Name string - } - type Inner struct { CreatedAt *time.Time Slice []string @@ -1653,10 +1649,10 @@ func TestCrossNamespaceFieldValidation(t *testing.T) { Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") - current, kind, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField") + current, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField") 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) 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") - type myValuer valuer - myVal := valuer{ 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") - type myValuer valuer - myVal := valuer{ Name: "", } @@ -2696,11 +2688,8 @@ func TestBadKeyValidation(t *testing.T) { func TestInterfaceErrValidation(t *testing.T) { - var v1 interface{} - var v2 interface{} - - v2 = 1 - v1 = v2 + var v2 interface{} = 1 + var v1 interface{} = v2 validate := New() errs := validate.Var(v1, "len=1") @@ -7146,7 +7135,7 @@ func TestValidateStructRegisterCtx(t *testing.T) { Equal(t, ctxSlVal, "slVal") } -func TestHostnameValidation(t *testing.T) { +func TestHostnameRFC952Validation(t *testing.T) { tests := []struct { param string expected bool @@ -7157,6 +7146,7 @@ func TestHostnameValidation(t *testing.T) { {"test.example24.com", true}, {"test24.example24.com", true}, {"example", true}, + {"1.foo.com", false}, {"test.example.com.", false}, {"example.com.", false}, {"example24.com.", false}, @@ -7178,15 +7168,112 @@ func TestHostnameValidation(t *testing.T) { if test.expected { 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 { 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 { val := getError(errs, "", "") 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 !IsEqual(errs, nil) { - t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) + t.Fatalf("Index: %d fqdn failed Error: %v", i, errs) } } else { 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 { val := getError(errs, "", "") if val.Tag() != "fqdn" { - t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) + t.Fatalf("Index: %d fqdn failed Error: %v", i, errs) } } }