pax_global_header00006660000000000000000000000064127431562050014517gustar00rootroot0000000000000052 comment=5f57d2222ad794d0dffb07e664ea05e2ee07d60c validator-8.18.1/000077500000000000000000000000001274315620500136035ustar00rootroot00000000000000validator-8.18.1/.gitignore000066400000000000000000000004631274315620500155760ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof *.test *.out *.txt cover.html README.htmlvalidator-8.18.1/LICENSE000066400000000000000000000020651274315620500146130ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Dean Karn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. validator-8.18.1/README.md000066400000000000000000000330651274315620500150710ustar00rootroot00000000000000Package validator ================ [![Join the chat at https://gitter.im/bluesuncorp/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-8.18.1-green.svg) [![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/530054/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v8&service=github)](https://coveralls.io/github/go-playground/validator?branch=v8) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator) [![GoDoc](https://godoc.org/gopkg.in/go-playground/validator.v8?status.svg)](https://godoc.org/gopkg.in/go-playground/validator.v8) ![License](https://img.shields.io/dub/l/vibe-d.svg) Package validator implements value validations for structs and individual fields based on tags. It has the following **unique** features: - Cross Field and Cross Struct validations by using validation tags or custom validators. - Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated. - Handles type interface by determining it's underlying type prior to validation. - Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29) - Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs - Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError Installation ------------ Use go get. go get gopkg.in/go-playground/validator.v8 or to update go get -u gopkg.in/go-playground/validator.v8 Then import the validator package into your own code. import "gopkg.in/go-playground/validator.v8" Error Return Value ------- Validation functions return type error They return type error to avoid the issue discussed in the following, where err is always != nil: * http://stackoverflow.com/a/29138676/3158232 * https://github.com/go-playground/validator/issues/134 validator only returns nil or ValidationErrors as type error; so in you code all you need to do is check if the error returned is not nil, and if it's not type cast it to type ValidationErrors like so: ```go err := validate.Struct(mystruct) validationErrors := err.(validator.ValidationErrors) ``` Usage and documentation ------ Please see http://godoc.org/gopkg.in/go-playground/validator.v8 for detailed usage docs. ##### Examples: Struct & Field validation ```go package main import ( "fmt" "gopkg.in/go-playground/validator.v8" ) // User contains user information type User struct { FirstName string `validate:"required"` LastName string `validate:"required"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"hexcolor|rgb|rgba"` Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... } // Address houses a users address information type Address struct { Street string `validate:"required"` City string `validate:"required"` Planet string `validate:"required"` Phone string `validate:"required"` } var validate *validator.Validate func main() { config := &validator.Config{TagName: "validate"} validate = validator.New(config) validateStruct() validateField() } func validateStruct() { address := &Address{ Street: "Eavesdown Docks", Planet: "Persphone", Phone: "none", } user := &User{ FirstName: "Badger", LastName: "Smith", Age: 135, Email: "Badger.Smith@gmail.com", FavouriteColor: "#000", Addresses: []*Address{address}, } // returns nil or ValidationErrors ( map[string]*FieldError ) errs := validate.Struct(user) if errs != nil { fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag err := errs.(validator.ValidationErrors)["User.Addresses[0].City"] fmt.Println(err.Field) // output: City fmt.Println(err.Tag) // output: required fmt.Println(err.Kind) // output: string fmt.Println(err.Type) // output: string fmt.Println(err.Param) // output: fmt.Println(err.Value) // output: // from here you can create your own error messages in whatever language you wish return } // save user to database } func validateField() { myEmail := "joeybloggs.gmail.com" errs := validate.Field(myEmail, "required,email") if errs != nil { fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag return } // email ok, move on } ``` Custom Field Type ```go package main import ( "database/sql" "database/sql/driver" "fmt" "reflect" "gopkg.in/go-playground/validator.v8" ) // DbBackedUser User struct type DbBackedUser struct { Name sql.NullString `validate:"required"` Age sql.NullInt64 `validate:"required"` } func main() { config := &validator.Config{TagName: "validate"} validate := validator.New(config) // register all sql.Null* types to use the ValidateValuer CustomTypeFunc validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} errs := validate.Struct(x) if len(errs.(validator.ValidationErrors)) > 0 { fmt.Printf("Errs:\n%+v\n", errs) } } // ValidateValuer implements validator.CustomTypeFunc func ValidateValuer(field reflect.Value) interface{} { if valuer, ok := field.Interface().(driver.Valuer); ok { val, err := valuer.Value() if err == nil { return val } // handle the error how you want } return nil } ``` Struct Level Validation ```go package main import ( "fmt" "reflect" "gopkg.in/go-playground/validator.v8" ) // User contains user information type User struct { FirstName string `json:"fname"` LastName string `json:"lname"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"hexcolor|rgb|rgba"` Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... } // Address houses a users address information type Address struct { Street string `validate:"required"` City string `validate:"required"` Planet string `validate:"required"` Phone string `validate:"required"` } var validate *validator.Validate func main() { config := &validator.Config{TagName: "validate"} validate = validator.New(config) validate.RegisterStructValidation(UserStructLevelValidation, User{}) validateStruct() } // UserStructLevelValidation contains custom struct level validations that don't always // make sense at the field validation level. For Example this function validates that either // FirstName or LastName exist; could have done that with a custom field validation but then // would have had to add it to both fields duplicating the logic + overhead, this way it's // only validated once. // // NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way // hooks right into validator and you can combine with validation tags and still have a // common error output format. func UserStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) { user := structLevel.CurrentStruct.Interface().(User) if len(user.FirstName) == 0 && len(user.LastName) == 0 { structLevel.ReportError(reflect.ValueOf(user.FirstName), "FirstName", "fname", "fnameorlname") structLevel.ReportError(reflect.ValueOf(user.LastName), "LastName", "lname", "fnameorlname") } // plus can to more, even with different tag than "fnameorlname" } func validateStruct() { address := &Address{ Street: "Eavesdown Docks", Planet: "Persphone", Phone: "none", City: "Unknown", } user := &User{ FirstName: "", LastName: "", Age: 45, Email: "Badger.Smith@gmail.com", FavouriteColor: "#000", Addresses: []*Address{address}, } // returns nil or ValidationErrors ( map[string]*FieldError ) errs := validate.Struct(user) if errs != nil { fmt.Println(errs) // output: Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag // Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag err := errs.(validator.ValidationErrors)["User.FirstName"] fmt.Println(err.Field) // output: FirstName fmt.Println(err.Tag) // output: fnameorlname fmt.Println(err.Kind) // output: string fmt.Println(err.Type) // output: string fmt.Println(err.Param) // output: fmt.Println(err.Value) // output: // from here you can create your own error messages in whatever language you wish return } // save user to database } ``` Benchmarks ------ ###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.5.3 darwin/amd64 ```go PASS BenchmarkFieldSuccess-8 20000000 118 ns/op 0 B/op 0 allocs/op BenchmarkFieldFailure-8 2000000 758 ns/op 432 B/op 4 allocs/op BenchmarkFieldDiveSuccess-8 500000 2471 ns/op 464 B/op 28 allocs/op BenchmarkFieldDiveFailure-8 500000 3172 ns/op 896 B/op 32 allocs/op BenchmarkFieldCustomTypeSuccess-8 5000000 300 ns/op 32 B/op 2 allocs/op BenchmarkFieldCustomTypeFailure-8 2000000 775 ns/op 432 B/op 4 allocs/op BenchmarkFieldOrTagSuccess-8 1000000 1122 ns/op 4 B/op 1 allocs/op BenchmarkFieldOrTagFailure-8 1000000 1167 ns/op 448 B/op 6 allocs/op BenchmarkStructLevelValidationSuccess-8 3000000 548 ns/op 160 B/op 5 allocs/op BenchmarkStructLevelValidationFailure-8 3000000 558 ns/op 160 B/op 5 allocs/op BenchmarkStructSimpleCustomTypeSuccess-8 2000000 623 ns/op 36 B/op 3 allocs/op BenchmarkStructSimpleCustomTypeFailure-8 1000000 1381 ns/op 640 B/op 9 allocs/op BenchmarkStructPartialSuccess-8 1000000 1036 ns/op 272 B/op 9 allocs/op BenchmarkStructPartialFailure-8 1000000 1734 ns/op 730 B/op 14 allocs/op BenchmarkStructExceptSuccess-8 2000000 888 ns/op 250 B/op 7 allocs/op BenchmarkStructExceptFailure-8 1000000 1036 ns/op 272 B/op 9 allocs/op BenchmarkStructSimpleCrossFieldSuccess-8 2000000 773 ns/op 80 B/op 4 allocs/op BenchmarkStructSimpleCrossFieldFailure-8 1000000 1487 ns/op 536 B/op 9 allocs/op BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 1000000 1261 ns/op 112 B/op 7 allocs/op BenchmarkStructSimpleCrossStructCrossFieldFailure-8 1000000 2055 ns/op 576 B/op 12 allocs/op BenchmarkStructSimpleSuccess-8 3000000 519 ns/op 4 B/op 1 allocs/op BenchmarkStructSimpleFailure-8 1000000 1429 ns/op 640 B/op 9 allocs/op BenchmarkStructSimpleSuccessParallel-8 10000000 146 ns/op 4 B/op 1 allocs/op BenchmarkStructSimpleFailureParallel-8 2000000 551 ns/op 640 B/op 9 allocs/op BenchmarkStructComplexSuccess-8 500000 3269 ns/op 244 B/op 15 allocs/op BenchmarkStructComplexFailure-8 200000 8436 ns/op 3609 B/op 60 allocs/op BenchmarkStructComplexSuccessParallel-8 1000000 1024 ns/op 244 B/op 15 allocs/op BenchmarkStructComplexFailureParallel-8 500000 3536 ns/op 3609 B/op 60 allocs/op ``` Complimentary Software ---------------------- Here is a list of software that compliments using this library either pre or post validation. * [Gorilla Schema](https://github.com/gorilla/schema) - Package gorilla/schema fills a struct with form values. * [Conform](https://github.com/leebenson/conform) - Trims, sanitizes & scrubs data based on struct tags. How to Contribute ------ There will always be a development branch for each version i.e. `v1-development`. In order to contribute, please make your pull requests against those branches. If the changes being proposed or requested are breaking changes, please create an issue, for discussion or create a pull request against the highest development branch for example this package has a v1 and v1-development branch however, there will also be a v2-development branch even though v2 doesn't exist yet. I strongly encourage everyone whom creates a custom validation function to contribute them and help make this package even better. License ------ Distributed under MIT License, please see license file in code for more details. validator-8.18.1/baked_in.go000066400000000000000000001556511274315620500157030ustar00rootroot00000000000000package validator import ( "fmt" "net" "net/url" "reflect" "strings" "time" "unicode/utf8" ) // BakedInAliasValidators is a default mapping of a single validationstag that // defines a common or complex set of validation(s) to simplify // adding validation to structs. i.e. set key "_ageok" and the tags // are "gt=0,lte=130" or key "_preferredname" and tags "omitempty,gt=0,lte=60" var bakedInAliasValidators = map[string]string{ "iscolor": "hexcolor|rgb|rgba|hsl|hsla", } // BakedInValidators is the default map of ValidationFunc // you can add, remove or even replace items to suite your needs, // or even disregard and use your own map if so desired. var bakedInValidators = map[string]Func{ "required": HasValue, "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, "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, } // IsMAC is the validation function for validating if the field's value is a valid MAC address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsMAC(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { _, err := net.ParseMAC(field.String()) return err == nil } // IsCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsCIDRv4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { ip, _, err := net.ParseCIDR(field.String()) return err == nil && ip.To4() != nil } // IsCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsCIDRv6(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { ip, _, err := net.ParseCIDR(field.String()) return err == nil && ip.To4() == nil } // IsCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsCIDR(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { _, _, err := net.ParseCIDR(field.String()) return err == nil } // IsIPv4 is the validation function for validating if a value is a valid v4 IP address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsIPv4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { ip := net.ParseIP(field.String()) return ip != nil && ip.To4() != nil } // IsIPv6 is the validation function for validating if the field's value is a valid v6 IP address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsIPv6(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { ip := net.ParseIP(field.String()) return ip != nil && ip.To4() == nil } // IsIP is the validation function for validating if the field's value is a valid v4 or v6 IP address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsIP(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { ip := net.ParseIP(field.String()) return ip != nil } // IsSSN is the validation function for validating if the field's value is a valid SSN. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsSSN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if field.Len() != 11 { return false } return sSNRegex.MatchString(field.String()) } // IsLongitude is the validation function for validating if the field's value is a valid longitude coordinate. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsLongitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return longitudeRegex.MatchString(field.String()) } // IsLatitude is the validation function for validating if the field's value is a valid latitude coordinate. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsLatitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return latitudeRegex.MatchString(field.String()) } // IsDataURI is the validation function for validating if the field's value is a valid data URI. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsDataURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { uri := strings.SplitN(field.String(), ",", 2) if len(uri) != 2 { return false } if !dataURIRegex.MatchString(uri[0]) { return false } fld := reflect.ValueOf(uri[1]) return IsBase64(v, topStruct, currentStructOrField, fld, fld.Type(), fld.Kind(), param) } // HasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func HasMultiByteCharacter(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if field.Len() == 0 { return true } return multibyteRegex.MatchString(field.String()) } // IsPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsPrintableASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return printableASCIIRegex.MatchString(field.String()) } // IsASCII is the validation function for validating if the field's value is a valid ASCII character. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return aSCIIRegex.MatchString(field.String()) } // IsUUID5 is the validation function for validating if the field's value is a valid v5 UUID. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUUID5(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return uUID5Regex.MatchString(field.String()) } // IsUUID4 is the validation function for validating if the field's value is a valid v4 UUID. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUUID4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return uUID4Regex.MatchString(field.String()) } // IsUUID3 is the validation function for validating if the field's value is a valid v3 UUID. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUUID3(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return uUID3Regex.MatchString(field.String()) } // IsUUID is the validation function for validating if the field's value is a valid UUID of any version. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUUID(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return uUIDRegex.MatchString(field.String()) } // IsISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsISBN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return IsISBN10(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) || IsISBN13(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) } // IsISBN13 is the validation function for validating if the field's value is a valid v13 ISBN. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsISBN13(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { s := strings.Replace(strings.Replace(field.String(), "-", "", 4), " ", "", 4) if !iSBN13Regex.MatchString(s) { return false } var checksum int32 var i int32 factor := []int32{1, 3} for i = 0; i < 12; i++ { checksum += factor[i%2] * int32(s[i]-'0') } return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 } // IsISBN10 is the validation function for validating if the field's value is a valid v10 ISBN. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsISBN10(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { s := strings.Replace(strings.Replace(field.String(), "-", "", 3), " ", "", 3) if !iSBN10Regex.MatchString(s) { return false } var checksum int32 var i int32 for i = 0; i < 9; i++ { checksum += (i + 1) * int32(s[i]-'0') } if s[9] == 'X' { checksum += 10 * 10 } else { checksum += 10 * int32(s[9]-'0') } return checksum%11 == 0 } // ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func ExcludesRune(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return !ContainsRune(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) } // ExcludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func ExcludesAll(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return !ContainsAny(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) } // Excludes is the validation function for validating that the field's value does not contain the text specified within the param. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func Excludes(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return !Contains(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) } // ContainsRune is the validation function for validating that the field's value contains the rune specified within the param. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func ContainsRune(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { r, _ := utf8.DecodeRuneInString(param) return strings.ContainsRune(field.String(), r) } // ContainsAny is the validation function for validating that the field's value contains any of the characters specified within the param. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func ContainsAny(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return strings.ContainsAny(field.String(), param) } // Contains is the validation function for validating that the field's value contains the text specified within the param. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func Contains(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return strings.Contains(field.String(), param) } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsNeField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) if !ok || currentKind != fieldKind { return true } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() != currentField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() != currentField.Uint() case reflect.Float32, reflect.Float64: return field.Float() != currentField.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(field.Len()) != int64(currentField.Len()) case reflect.Struct: // Not Same underlying type i.e. struct and time if fieldType != currentField.Type() { return true } if fieldType == timeType { t := currentField.Interface().(time.Time) fieldTime := field.Interface().(time.Time) return !fieldTime.Equal(t) } } // default reflect.String: return field.String() != currentField.String() } // IsNe is the validation function for validating that the field's value does not equal the provided param value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsNe(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return !IsEq(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsLteCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { topField, topKind, ok := v.GetStructFieldOK(topStruct, param) if !ok || topKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() <= topField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() <= topField.Uint() case reflect.Float32, reflect.Float64: return field.Float() <= topField.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(field.Len()) <= int64(topField.Len()) case reflect.Struct: // 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: return field.String() <= topField.String() } // 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(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { topField, topKind, ok := v.GetStructFieldOK(topStruct, param) if !ok || topKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() < topField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() < topField.Uint() case reflect.Float32, reflect.Float64: return field.Float() < topField.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(field.Len()) < int64(topField.Len()) case reflect.Struct: // 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: return field.String() < topField.String() } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsGteCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { topField, topKind, ok := v.GetStructFieldOK(topStruct, param) if !ok || topKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() >= topField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() >= topField.Uint() case reflect.Float32, reflect.Float64: return field.Float() >= topField.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(field.Len()) >= int64(topField.Len()) case reflect.Struct: // 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: return field.String() >= topField.String() } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsGtCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { topField, topKind, ok := v.GetStructFieldOK(topStruct, param) if !ok || topKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() > topField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() > topField.Uint() case reflect.Float32, reflect.Float64: return field.Float() > topField.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(field.Len()) > int64(topField.Len()) case reflect.Struct: // 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: return field.String() > topField.String() } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsNeCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { topField, currentKind, ok := v.GetStructFieldOK(topStruct, param) if !ok || currentKind != fieldKind { return true } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return topField.Int() != field.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return topField.Uint() != field.Uint() case reflect.Float32, reflect.Float64: return topField.Float() != field.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(topField.Len()) != int64(field.Len()) case reflect.Struct: // 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: return topField.String() != field.String() } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsEqCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { topField, topKind, ok := v.GetStructFieldOK(topStruct, param) if !ok || topKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return topField.Int() == field.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return topField.Uint() == field.Uint() case reflect.Float32, reflect.Float64: return topField.Float() == field.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(topField.Len()) == int64(field.Len()) case reflect.Struct: // 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: return topField.String() == field.String() } // IsEqField is the validation function for validating if the current field's value is equal to the field 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 IsEqField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) if !ok || currentKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() == currentField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() == currentField.Uint() case reflect.Float32, reflect.Float64: return field.Float() == currentField.Float() case reflect.Slice, reflect.Map, reflect.Array: return int64(field.Len()) == int64(currentField.Len()) case reflect.Struct: // 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.Equal(t) } } // default reflect.String: return field.String() == currentField.String() } // IsEq is the validation function for validating if the current field's value is equal to the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsEq(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: return field.String() == param case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) return int64(field.Len()) == p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) return field.Int() == p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) return field.Uint() == p case reflect.Float32, reflect.Float64: p := asFloat(param) return field.Float() == p } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // IsBase64 is the validation function for validating if the current field's value is a valid base 64. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsBase64(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return base64Regex.MatchString(field.String()) } // IsURI is the validation function for validating if the current field's value is a valid URI. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: s := field.String() // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 // emulate browser and strip the '#' suffix prior to validation. see issue-#237 if i := strings.Index(s, "#"); i > -1 { s = s[:i] } if s == blank { return false } _, err := url.ParseRequestURI(s) return err == nil } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // IsURL is the validation function for validating if the current field's value is a valid URL. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsURL(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: var i int s := field.String() // checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195 // emulate browser and strip the '#' suffix prior to validation. see issue-#237 if i = strings.Index(s, "#"); i > -1 { s = s[:i] } if s == blank { return false } url, err := url.ParseRequestURI(s) if err != nil || url.Scheme == blank { return false } return err == nil } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // IsEmail is the validation function for validating if the current field's value is a valid email address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsEmail(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return emailRegex.MatchString(field.String()) } // IsHSLA is the validation function for validating if the current field's value is a valid HSLA color. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsHSLA(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return hslaRegex.MatchString(field.String()) } // IsHSL is the validation function for validating if the current field's value is a valid HSL color. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsHSL(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return hslRegex.MatchString(field.String()) } // IsRGBA is the validation function for validating if the current field's value is a valid RGBA color. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsRGBA(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return rgbaRegex.MatchString(field.String()) } // IsRGB is the validation function for validating if the current field's value is a valid RGB color. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsRGB(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return rgbRegex.MatchString(field.String()) } // IsHEXColor is the validation function for validating if the current field's value is a valid HEX color. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsHEXColor(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return hexcolorRegex.MatchString(field.String()) } // IsHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsHexadecimal(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return hexadecimalRegex.MatchString(field.String()) } // IsNumber is the validation function for validating if the current field's value is a valid number. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsNumber(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return numberRegex.MatchString(field.String()) } // IsNumeric is the validation function for validating if the current field's value is a valid numeric value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsNumeric(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return numericRegex.MatchString(field.String()) } // IsAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsAlphanum(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return alphaNumericRegex.MatchString(field.String()) } // IsAlpha is the validation function for validating if the current field's value is a valid alpha value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsAlpha(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return alphaRegex.MatchString(field.String()) } // HasValue is the validation function for validating if the current field's value is not the default static value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func HasValue(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: return !field.IsNil() default: return field.IsValid() && field.Interface() != reflect.Zero(fieldType).Interface() } } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsGteField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) if !ok || currentKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() >= currentField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() >= currentField.Uint() case reflect.Float32, reflect.Float64: return field.Float() >= currentField.Float() case reflect.Struct: // 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 return len(field.String()) >= len(currentField.String()) } // IsGtField is the validation function for validating if the current field's value is greater than the field 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 IsGtField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) if !ok || currentKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() > currentField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() > currentField.Uint() case reflect.Float32, reflect.Float64: return field.Float() > currentField.Float() case reflect.Struct: // 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 return len(field.String()) > len(currentField.String()) } // IsGte is the validation function for validating if the current field's value is greater than or equal to the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsGte(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: p := asInt(param) return int64(utf8.RuneCountInString(field.String())) >= p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) return int64(field.Len()) >= p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) return field.Int() >= p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) return field.Uint() >= p case reflect.Float32, reflect.Float64: p := asFloat(param) return field.Float() >= p case reflect.Struct: if fieldType == timeType || fieldType == timePtrType { now := time.Now().UTC() t := field.Interface().(time.Time) return t.After(now) || t.Equal(now) } } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // IsGt is the validation function for validating if the current field's value is greater than the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsGt(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: p := asInt(param) return int64(utf8.RuneCountInString(field.String())) > p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) return int64(field.Len()) > p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) return field.Int() > p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) return field.Uint() > p case reflect.Float32, reflect.Float64: p := asFloat(param) return field.Float() > p case reflect.Struct: if fieldType == timeType || fieldType == timePtrType { return field.Interface().(time.Time).After(time.Now().UTC()) } } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // HasLengthOf is the validation function for validating if the current field's value is equal to the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func HasLengthOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: p := asInt(param) return int64(utf8.RuneCountInString(field.String())) == p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) return int64(field.Len()) == p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) return field.Int() == p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) return field.Uint() == p case reflect.Float32, reflect.Float64: p := asFloat(param) return field.Float() == p } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // HasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func HasMinOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return IsGte(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) } // 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. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsLteField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) if !ok || currentKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() <= currentField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() <= currentField.Uint() case reflect.Float32, reflect.Float64: return field.Float() <= currentField.Float() case reflect.Struct: // 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 return len(field.String()) <= len(currentField.String()) } // IsLtField is the validation function for validating if the current field's value is less than the field 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 IsLtField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param) if !ok || currentKind != fieldKind { return false } switch fieldKind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return field.Int() < currentField.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return field.Uint() < currentField.Uint() case reflect.Float32, reflect.Float64: return field.Float() < currentField.Float() case reflect.Struct: // 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 return len(field.String()) < len(currentField.String()) } // IsLte is the validation function for validating if the current field's value is less than or equal to the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsLte(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: p := asInt(param) return int64(utf8.RuneCountInString(field.String())) <= p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) return int64(field.Len()) <= p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) return field.Int() <= p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) return field.Uint() <= p case reflect.Float32, reflect.Float64: p := asFloat(param) return field.Float() <= p case reflect.Struct: if fieldType == timeType || fieldType == timePtrType { now := time.Now().UTC() t := field.Interface().(time.Time) return t.Before(now) || t.Equal(now) } } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // IsLt is the validation function for validating if the current field's value is less than the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsLt(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { switch fieldKind { case reflect.String: p := asInt(param) return int64(utf8.RuneCountInString(field.String())) < p case reflect.Slice, reflect.Map, reflect.Array: p := asInt(param) return int64(field.Len()) < p case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p := asInt(param) return field.Int() < p case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: p := asUint(param) return field.Uint() < p case reflect.Float32, reflect.Float64: p := asFloat(param) return field.Float() < p case reflect.Struct: if fieldType == timeType || fieldType == timePtrType { return field.Interface().(time.Time).Before(time.Now().UTC()) } } panic(fmt.Sprintf("Bad field type %T", field.Interface())) } // HasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func HasMaxOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return IsLte(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) } // IsTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsTCP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveTCPAddr("tcp4", field.String()) return err == nil } // IsTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsTCP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveTCPAddr("tcp6", field.String()) return err == nil } // IsTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsTCPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) && !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveTCPAddr("tcp", field.String()) return err == nil } // IsUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUDP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveUDPAddr("udp4", field.String()) return err == nil } // IsUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUDP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveUDPAddr("udp6", field.String()) return err == nil } // IsUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUDPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) && !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveUDPAddr("udp", field.String()) return err == nil } // IsIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsIP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !IsIPv4(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveIPAddr("ip4", field.String()) return err == nil } // IsIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsIP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !IsIPv6(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveIPAddr("ip6", field.String()) return err == nil } // IsIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsIPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if !IsIP(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) { return false } _, err := net.ResolveIPAddr("ip", field.String()) return err == nil } // IsUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address. // NOTE: This is exposed for use within your own custom functions and not intended to be called directly. func IsUnixAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { _, err := net.ResolveUnixAddr("unix", field.String()) return err == nil } func isIP4Addr(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { val := field.String() if idx := strings.LastIndex(val, ":"); idx != -1 { val = val[0:idx] } if !IsIPv4(v, topStruct, currentStructOrField, reflect.ValueOf(val), fieldType, fieldKind, param) { return false } return true } func isIP6Addr(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { val := field.String() if idx := strings.LastIndex(val, ":"); idx != -1 { if idx != 0 && val[idx-1:idx] == "]" { val = val[1 : idx-1] } } if !IsIPv6(v, topStruct, currentStructOrField, reflect.ValueOf(val), fieldType, fieldKind, param) { return false } return true } validator-8.18.1/benchmarks_test.go000066400000000000000000000212531274315620500173110ustar00rootroot00000000000000package validator import ( sql "database/sql/driver" "testing" "time" ) func BenchmarkFieldSuccess(b *testing.B) { var s *string tmp := "1" s = &tmp for n := 0; n < b.N; n++ { validate.Field(s, "len=1") } } func BenchmarkFieldFailure(b *testing.B) { var s *string tmp := "12" s = &tmp for n := 0; n < b.N; n++ { validate.Field(s, "len=1") } } func BenchmarkFieldDiveSuccess(b *testing.B) { m := make([]*string, 3) t1 := "val1" t2 := "val2" t3 := "val3" m[0] = &t1 m[1] = &t2 m[2] = &t3 for n := 0; n < b.N; n++ { validate.Field(m, "required,dive,required") } } func BenchmarkFieldDiveFailure(b *testing.B) { m := make([]*string, 3) t1 := "val1" t2 := "" t3 := "val3" m[0] = &t1 m[1] = &t2 m[2] = &t3 for n := 0; n < b.N; n++ { validate.Field(m, "required,dive,required") } } func BenchmarkFieldCustomTypeSuccess(b *testing.B) { validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) val := valuer{ Name: "1", } for n := 0; n < b.N; n++ { validate.Field(val, "len=1") } } func BenchmarkFieldCustomTypeFailure(b *testing.B) { validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) val := valuer{} for n := 0; n < b.N; n++ { validate.Field(val, "len=1") } } func BenchmarkFieldOrTagSuccess(b *testing.B) { var s *string tmp := "rgba(0,0,0,1)" s = &tmp for n := 0; n < b.N; n++ { validate.Field(s, "rgb|rgba") } } func BenchmarkFieldOrTagFailure(b *testing.B) { var s *string tmp := "#000" s = &tmp for n := 0; n < b.N; n++ { validate.Field(s, "rgb|rgba") } } func BenchmarkStructLevelValidationSuccess(b *testing.B) { validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) tst := &TestStruct{ String: "good value", } for n := 0; n < b.N; n++ { validate.Struct(tst) } } func BenchmarkStructLevelValidationFailure(b *testing.B) { validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{}) tst := &TestStruct{ String: "good value", } for n := 0; n < b.N; n++ { validate.Struct(tst) } } func BenchmarkStructSimpleCustomTypeSuccess(b *testing.B) { validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) val := valuer{ Name: "1", } type Foo struct { Valuer valuer `validate:"len=1"` IntValue int `validate:"min=5,max=10"` } validFoo := &Foo{Valuer: val, IntValue: 7} for n := 0; n < b.N; n++ { validate.Struct(validFoo) } } func BenchmarkStructSimpleCustomTypeFailure(b *testing.B) { validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) val := valuer{} type Foo struct { Valuer valuer `validate:"len=1"` IntValue int `validate:"min=5,max=10"` } validFoo := &Foo{Valuer: val, IntValue: 3} for n := 0; n < b.N; n++ { validate.Struct(validFoo) } } func BenchmarkStructPartialSuccess(b *testing.B) { type Test struct { Name string `validate:"required"` NickName string `validate:"required"` } test := &Test{ Name: "Joey Bloggs", } for n := 0; n < b.N; n++ { validate.StructPartial(test, "Name") } } func BenchmarkStructPartialFailure(b *testing.B) { type Test struct { Name string `validate:"required"` NickName string `validate:"required"` } test := &Test{ Name: "Joey Bloggs", } for n := 0; n < b.N; n++ { validate.StructPartial(test, "NickName") } } func BenchmarkStructExceptSuccess(b *testing.B) { type Test struct { Name string `validate:"required"` NickName string `validate:"required"` } test := &Test{ Name: "Joey Bloggs", } for n := 0; n < b.N; n++ { validate.StructPartial(test, "Nickname") } } func BenchmarkStructExceptFailure(b *testing.B) { type Test struct { Name string `validate:"required"` NickName string `validate:"required"` } test := &Test{ Name: "Joey Bloggs", } for n := 0; n < b.N; n++ { validate.StructPartial(test, "Name") } } func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) { type Test struct { Start time.Time End time.Time `validate:"gtfield=Start"` } now := time.Now().UTC() then := now.Add(time.Hour * 5) test := &Test{ Start: now, End: then, } for n := 0; n < b.N; n++ { validate.Struct(test) } } func BenchmarkStructSimpleCrossFieldFailure(b *testing.B) { type Test struct { Start time.Time End time.Time `validate:"gtfield=Start"` } now := time.Now().UTC() then := now.Add(time.Hour * -5) test := &Test{ Start: now, End: then, } for n := 0; n < b.N; n++ { validate.Struct(test) } } func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) { type Inner struct { Start time.Time } type Outer struct { Inner *Inner CreatedAt time.Time `validate:"eqcsfield=Inner.Start"` } now := time.Now().UTC() inner := &Inner{ Start: now, } outer := &Outer{ Inner: inner, CreatedAt: now, } for n := 0; n < b.N; n++ { validate.Struct(outer) } } func BenchmarkStructSimpleCrossStructCrossFieldFailure(b *testing.B) { type Inner struct { Start time.Time } type Outer struct { Inner *Inner CreatedAt time.Time `validate:"eqcsfield=Inner.Start"` } now := time.Now().UTC() then := now.Add(time.Hour * 5) inner := &Inner{ Start: then, } outer := &Outer{ Inner: inner, CreatedAt: now, } for n := 0; n < b.N; n++ { validate.Struct(outer) } } func BenchmarkStructSimpleSuccess(b *testing.B) { type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` } validFoo := &Foo{StringValue: "Foobar", IntValue: 7} for n := 0; n < b.N; n++ { validate.Struct(validFoo) } } func BenchmarkStructSimpleFailure(b *testing.B) { type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` } invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} for n := 0; n < b.N; n++ { validate.Struct(invalidFoo) } } func BenchmarkStructSimpleSuccessParallel(b *testing.B) { type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` } validFoo := &Foo{StringValue: "Foobar", IntValue: 7} b.RunParallel(func(pb *testing.PB) { for pb.Next() { validate.Struct(validFoo) } }) } func BenchmarkStructSimpleFailureParallel(b *testing.B) { type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` } invalidFoo := &Foo{StringValue: "Fo", IntValue: 3} b.RunParallel(func(pb *testing.PB) { for pb.Next() { validate.Struct(invalidFoo) } }) } func BenchmarkStructComplexSuccess(b *testing.B) { tSuccess := &TestString{ Required: "Required", Len: "length==10", Min: "min=1", Max: "1234567890", MinMax: "12345", Lt: "012345678", Lte: "0123456789", Gt: "01234567890", Gte: "0123456789", OmitEmpty: "", Sub: &SubTest{ Test: "1", }, SubIgnore: &SubTest{ Test: "", }, Anonymous: struct { A string `validate:"required"` }{ A: "1", }, Iface: &Impl{ F: "123", }, } for n := 0; n < b.N; n++ { validate.Struct(tSuccess) } } func BenchmarkStructComplexFailure(b *testing.B) { tFail := &TestString{ Required: "", Len: "", Min: "", Max: "12345678901", MinMax: "", Lt: "0123456789", Lte: "01234567890", Gt: "1", Gte: "1", OmitEmpty: "12345678901", Sub: &SubTest{ Test: "", }, Anonymous: struct { A string `validate:"required"` }{ A: "", }, Iface: &Impl{ F: "12", }, } for n := 0; n < b.N; n++ { validate.Struct(tFail) } } func BenchmarkStructComplexSuccessParallel(b *testing.B) { tSuccess := &TestString{ Required: "Required", Len: "length==10", Min: "min=1", Max: "1234567890", MinMax: "12345", Lt: "012345678", Lte: "0123456789", Gt: "01234567890", Gte: "0123456789", OmitEmpty: "", Sub: &SubTest{ Test: "1", }, SubIgnore: &SubTest{ Test: "", }, Anonymous: struct { A string `validate:"required"` }{ A: "1", }, Iface: &Impl{ F: "123", }, } b.RunParallel(func(pb *testing.PB) { for pb.Next() { validate.Struct(tSuccess) } }) } func BenchmarkStructComplexFailureParallel(b *testing.B) { tFail := &TestString{ Required: "", Len: "", Min: "", Max: "12345678901", MinMax: "", Lt: "0123456789", Lte: "01234567890", Gt: "1", Gte: "1", OmitEmpty: "12345678901", Sub: &SubTest{ Test: "", }, Anonymous: struct { A string `validate:"required"` }{ A: "", }, Iface: &Impl{ F: "12", }, } b.RunParallel(func(pb *testing.PB) { for pb.Next() { validate.Struct(tFail) } }) } validator-8.18.1/cache.go000066400000000000000000000126411274315620500152010ustar00rootroot00000000000000package validator import ( "fmt" "reflect" "strings" "sync" "sync/atomic" ) type tagType uint8 const ( typeDefault tagType = iota typeOmitEmpty typeNoStructLevel typeStructOnly typeDive typeOr typeExists ) type structCache struct { lock sync.Mutex m atomic.Value // map[reflect.Type]*cStruct } func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) { c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key] return } func (sc *structCache) Set(key reflect.Type, value *cStruct) { m := sc.m.Load().(map[reflect.Type]*cStruct) nm := make(map[reflect.Type]*cStruct, len(m)+1) for k, v := range m { nm[k] = v } nm[key] = value sc.m.Store(nm) } type tagCache struct { lock sync.Mutex m atomic.Value // map[string]*cTag } func (tc *tagCache) Get(key string) (c *cTag, found bool) { c, found = tc.m.Load().(map[string]*cTag)[key] return } func (tc *tagCache) Set(key string, value *cTag) { m := tc.m.Load().(map[string]*cTag) nm := make(map[string]*cTag, len(m)+1) for k, v := range m { nm[k] = v } nm[key] = value tc.m.Store(nm) } type cStruct struct { Name string fields map[int]*cField fn StructLevelFunc } type cField struct { Idx int Name string AltName string cTags *cTag } type cTag struct { tag string aliasTag string actualAliasTag string param string hasAlias bool typeof tagType hasTag bool fn Func next *cTag } func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { v.structCache.lock.Lock() defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise! typ := current.Type() // could have been multiple trying to access, but once first is done this ensures struct // isn't parsed again. cs, ok := v.structCache.Get(typ) if ok { return cs } cs = &cStruct{Name: sName, fields: make(map[int]*cField), fn: v.structLevelFuncs[typ]} numFields := current.NumField() var ctag *cTag var fld reflect.StructField var tag string var customName string for i := 0; i < numFields; i++ { fld = typ.Field(i) if !fld.Anonymous && fld.PkgPath != blank { continue } tag = fld.Tag.Get(v.tagName) if tag == skipValidationTag { continue } customName = fld.Name if v.fieldNameTag != blank { name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0] // dash check is for json "-" (aka skipValidationTag) means don't output in json if name != "" && name != skipValidationTag { customName = name } } // NOTE: cannot use shared tag cache, because tags may be equal, but things like alias may be different // and so only struct level caching can be used instead of combined with Field tag caching if len(tag) > 0 { ctag, _ = v.parseFieldTagsRecursive(tag, fld.Name, blank, false) } else { // even if field doesn't have validations need cTag for traversing to potential inner/nested // elements of the field. ctag = new(cTag) } cs.fields[i] = &cField{Idx: i, Name: fld.Name, AltName: customName, cTags: ctag} } v.structCache.Set(typ, cs) return cs } func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { var t string var ok bool noAlias := len(alias) == 0 tags := strings.Split(tag, tagSeparator) for i := 0; i < len(tags); i++ { t = tags[i] if noAlias { alias = t } if v.hasAliasValidators { // check map for alias and process new tags, otherwise process as usual if tagsVal, found := v.aliasValidators[t]; found { if i == 0 { firstCtag, current = v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) } else { next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true) current.next, current = next, curr } continue } } if i == 0 { current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} firstCtag = current } else { current.next = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} current = current.next } switch t { case diveTag: current.typeof = typeDive continue case omitempty: current.typeof = typeOmitEmpty continue case structOnlyTag: current.typeof = typeStructOnly continue case noStructLevelTag: current.typeof = typeNoStructLevel continue case existsTag: current.typeof = typeExists continue default: // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" orVals := strings.Split(t, orSeparator) for j := 0; j < len(orVals); j++ { vals := strings.SplitN(orVals[j], tagKeySeparator, 2) if noAlias { alias = vals[0] current.aliasTag = alias } else { current.actualAliasTag = t } if j > 0 { current.next = &cTag{aliasTag: alias, actualAliasTag: current.actualAliasTag, hasAlias: hasAlias, hasTag: true} current = current.next } current.tag = vals[0] if len(current.tag) == 0 { panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) } if current.fn, ok = v.validationFuncs[current.tag]; !ok { panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, fieldName))) } if len(orVals) > 1 { current.typeof = typeOr } if len(vals) > 1 { current.param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) } } } } return } validator-8.18.1/doc.go000066400000000000000000000507511274315620500147070ustar00rootroot00000000000000/* Package validator implements value validations for structs and individual fields based on tags. It can also handle Cross-Field and Cross-Struct validation for nested structs and has the ability to dive into arrays and maps of any type. Why not a better error message? Because this library intends for you to handle your own error messages. Why should I handle my own errors? Many reasons. We built an internationalized application and needed to know the field, and what validation failed so we could provide a localized error. if fieldErr.Field == "Name" { switch fieldErr.ErrorTag case "required": return "Translated string based on field + error" default: return "Translated string based on field" } Validation Functions Return Type error Doing things this way is actually the way the standard library does, see the file.Open method here: https://golang.org/pkg/os/#Open. The authors return type "error" to avoid the issue discussed in the following, where err is always != nil: http://stackoverflow.com/a/29138676/3158232 https://github.com/go-playground/validator/issues/134 Validator only returns nil or ValidationErrors as type error; so, in your code all you need to do is check if the error returned is not nil, and if it's not type cast it to type ValidationErrors like so err.(validator.ValidationErrors). Custom Functions Custom functions can be added. Example: // Structure func customFunc(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { if whatever { return false } return true } validate.RegisterValidation("custom tag name", customFunc) // NOTES: using the same tag name as an existing function // will overwrite the existing one Cross-Field Validation Cross-Field Validation can be done via the following tags: - eqfield - nefield - gtfield - gtefield - ltfield - ltefield - eqcsfield - necsfield - gtcsfield - ftecsfield - ltcsfield - ltecsfield If, however, some custom cross-field validation is required, it can be done using a custom validation. Why not just have cross-fields validation tags (i.e. only eqcsfield and not eqfield)? The reason is efficiency. If you want to check a field within the same struct "eqfield" only has to find the field on the same struct (1 level). But, if we used "eqcsfield" it could be multiple levels down. Example: type Inner struct { StartDate time.Time } type Outer struct { InnerStructField *Inner CreatedAt time.Time `validate:"ltecsfield=InnerStructField.StartDate"` } now := time.Now() inner := &Inner{ StartDate: now, } outer := &Outer{ InnerStructField: inner, CreatedAt: now, } errs := validate.Struct(outer) // NOTE: when calling validate.Struct(val) topStruct will be the top level struct passed // into the function // when calling validate.FieldWithValue(val, field, tag) val will be // whatever you pass, struct, field... // when calling validate.Field(field, tag) val will be nil Multiple Validators Multiple validators on a field will process in the order defined. Example: type Test struct { Field `validate:"max=10,min=1"` } // max will be checked then min Bad Validator definitions are not handled by the library. Example: type Test struct { Field `validate:"min=10,max=0"` } // this definition of min max will never succeed Using Validator Tags Baked In Cross-Field validation only compares fields on the same struct. If Cross-Field + Cross-Struct validation is needed you should implement your own custom validator. Comma (",") is the default separator of validation tags. If you wish to have a comma included within the parameter (i.e. excludesall=,) you will need to use the UTF-8 hex representation 0x2C, which is replaced in the code as a comma, so the above will become excludesall=0x2C. type Test struct { Field `validate:"excludesall=,"` // BAD! Do not include a comma. Field `validate:"excludesall=0x2C"` // GOOD! Use the UTF-8 hex representation. } Pipe ("|") is the default separator of validation tags. If you wish to have a pipe included within the parameter i.e. excludesall=| you will need to use the UTF-8 hex representation 0x7C, which is replaced in the code as a pipe, so the above will become excludesall=0x7C type Test struct { Field `validate:"excludesall=|"` // BAD! Do not include a a pipe! Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation. } Baked In Validators and Tags Here is a list of the current built in validators: Skip Field Tells the validation to skip this struct field; this is particularly handy in ignoring embedded structs from being validated. (Usage: -) Usage: - Or Operator This is the 'or' operator allowing multiple validators to be used and accepted. (Usage: rbg|rgba) <-- this would allow either rgb or rgba colors to be accepted. This can also be combined with 'and' for example ( Usage: omitempty,rgb|rgba) Usage: | StructOnly When a field that is a nested struct is encountered, and contains this flag any validation on the nested struct will be run, but none of the nested struct fields will be validated. This is usefull if inside of you program you know the struct will be valid, but need to verify it has been assigned. NOTE: only "required" and "omitempty" can be used on a struct itself. Usage: structonly NoStructLevel Same as structonly tag except that any struct level validations will not run. Usage: nostructlevel Exists Is a special tag without a validation function attached. It is used when a field is a Pointer, Interface or Invalid and you wish to validate that it exists. Example: want to ensure a bool exists if you define the bool as a pointer and use exists it will ensure there is a value; couldn't use required as it would fail when the bool was false. exists will fail is the value is a Pointer, Interface or Invalid and is nil. Usage: exists Omit Empty Allows conditional validation, for example if a field is not set with a value (Determined by the "required" validator) then other validation such as min or max won't run, but if a value is set validation will run. Usage: omitempty Dive This tells the validator to dive into a slice, array or map and validate that level of the slice, array or map with the validation tags that follow. Multidimensional nesting is also supported, each level you wish to dive will require another dive tag. Usage: dive Example #1 [][]string with validation tag "gt=0,dive,len=1,dive,required" // gt=0 will be applied to [] // len=1 will be applied to []string // required will be applied to string Example #2 [][]string with validation tag "gt=0,dive,dive,required" // gt=0 will be applied to [] // []string will be spared validation // required will be applied to string Required This validates that the value is not the data types default zero value. For numbers ensures value is not zero. For strings ensures value is not "". For slices, maps, pointers, interfaces, channels and functions ensures the value is not nil. Usage: required Length For numbers, max will ensure that the value is equal to the parameter given. For strings, it checks that the string length is exactly that number of characters. For slices, arrays, and maps, validates the number of items. Usage: len=10 Maximum For numbers, max will ensure that the value is less than or equal to the parameter given. For strings, it checks that the string length is at most that number of characters. For slices, arrays, and maps, validates the number of items. Usage: max=10 Mininum For numbers, min will ensure that the value is greater or equal to the parameter given. For strings, it checks that the string length is at least that number of characters. For slices, arrays, and maps, validates the number of items. Usage: min=10 Equals For strings & numbers, eq will ensure that the value is equal to the parameter given. For slices, arrays, and maps, validates the number of items. Usage: eq=10 Not Equal For strings & numbers, ne will ensure that the value is not equal to the parameter given. For slices, arrays, and maps, validates the number of items. Usage: ne=10 Greater Than For numbers, this will ensure that the value is greater than the parameter given. For strings, it checks that the string length is greater than that number of characters. For slices, arrays and maps it validates the number of items. Example #1 Usage: gt=10 Example #2 (time.Time) For time.Time ensures the time value is greater than time.Now.UTC(). Usage: gt Greater Than or Equal Same as 'min' above. Kept both to make terminology with 'len' easier. Example #1 Usage: gte=10 Example #2 (time.Time) For time.Time ensures the time value is greater than or equal to time.Now.UTC(). Usage: gte Less Than For numbers, this will ensure that the value is less than the parameter given. For strings, it checks that the string length is less than that number of characters. For slices, arrays, and maps it validates the number of items. Example #1 Usage: lt=10 Example #2 (time.Time) For time.Time ensures the time value is less than time.Now.UTC(). Usage: lt Less Than or Equal Same as 'max' above. Kept both to make terminology with 'len' easier. Example #1 Usage: lte=10 Example #2 (time.Time) For time.Time ensures the time value is less than or equal to time.Now.UTC(). Usage: lte Field Equals Another Field This will validate the field value against another fields value either within a struct or passed in field. Example #1: // Validation on Password field using: Usage: eqfield=ConfirmPassword Example #2: // Validating by field: validate.FieldWithValue(password, confirmpassword, "eqfield") Field Equals Another Field (relative) This does the same as eqfield except that it validates the field provided relative to the top level struct. Usage: eqcsfield=InnerStructField.Field) Field Does Not Equal Another Field This will validate the field value against another fields value either within a struct or passed in field. Examples: // Confirm two colors are not the same: // // Validation on Color field: Usage: nefield=Color2 // Validating by field: validate.FieldWithValue(color1, color2, "nefield") Field Does Not Equal Another Field (relative) This does the same as nefield except that it validates the field provided relative to the top level struct. Usage: necsfield=InnerStructField.Field Field Greater Than Another Field Only valid for Numbers and time.Time types, this will validate the field value against another fields value either within a struct or passed in field. usage examples are for validation of a Start and End date: Example #1: // Validation on End field using: validate.Struct Usage(gtfield=Start) Example #2: // Validating by field: validate.FieldWithValue(start, end, "gtfield") Field Greater Than Another Relative Field This does the same as gtfield except that it validates the field provided relative to the top level struct. Usage: gtcsfield=InnerStructField.Field Field Greater Than or Equal To Another Field Only valid for Numbers and time.Time types, this will validate the field value against another fields value either within a struct or passed in field. usage examples are for validation of a Start and End date: Example #1: // Validation on End field using: validate.Struct Usage(gtefield=Start) Example #2: // Validating by field: validate.FieldWithValue(start, end, "gtefield") Field Greater Than or Equal To Another Relative Field This does the same as gtefield except that it validates the field provided relative to the top level struct. Usage: gtecsfield=InnerStructField.Field Less Than Another Field Only valid for Numbers and time.Time types, this will validate the field value against another fields value either within a struct or passed in field. usage examples are for validation of a Start and End date: Example #1: // Validation on End field using: validate.Struct Usage(ltfield=Start) Example #2: // Validating by field: validate.FieldWithValue(start, end, "ltfield") Less Than Another Relative Field This does the same as ltfield except that it validates the field provided relative to the top level struct. Usage: ltcsfield=InnerStructField.Field Less Than or Equal To Another Field Only valid for Numbers and time.Time types, this will validate the field value against another fields value either within a struct or passed in field. usage examples are for validation of a Start and End date: Example #1: // Validation on End field using: validate.Struct Usage(ltefield=Start) Example #2: // Validating by field: validate.FieldWithValue(start, end, "ltefield") Less Than or Equal To Another Relative Field This does the same as ltefield except that it validates the field provided relative to the top level struct. Usage: ltecsfield=InnerStructField.Field Alpha Only This validates that a string value contains alpha characters only Usage: alpha Alphanumeric This validates that a string value contains alphanumeric characters only Usage: alphanum Numeric This validates that a string value contains a basic numeric value. basic excludes exponents etc... Usage: numeric Hexadecimal String This validates that a string value contains a valid hexadecimal. Usage: hexadecimal Hexcolor String This validates that a string value contains a valid hex color including hashtag (#) Usage: hexcolor RGB String This validates that a string value contains a valid rgb color Usage: rgb RGBA String This validates that a string value contains a valid rgba color Usage: rgba HSL String This validates that a string value contains a valid hsl color Usage: hsl HSLA String This validates that a string value contains a valid hsla color Usage: hsla E-mail String This validates that a string value contains a valid email This may not conform to all possibilities of any rfc standard, but neither does any email provider accept all posibilities. Usage: email URL String This validates that a string value contains a valid url This will accept any url the golang request uri accepts but must contain a schema for example http:// or rtmp:// Usage: url URI String This validates that a string value contains a valid uri This will accept any uri the golang request uri accepts Usage: uri Base64 String This validates that a string value contains a valid base64 value. Although an empty string is valid base64 this will report an empty string as an error, if you wish to accept an empty string as valid you can use this with the omitempty tag. Usage: base64 Contains This validates that a string value contains the substring value. Usage: contains=@ Contains Any This validates that a string value contains any Unicode code points in the substring value. Usage: containsany=!@#? Contains Rune This validates that a string value contains the supplied rune value. Usage: containsrune=@ Excludes This validates that a string value does not contain the substring value. Usage: excludes=@ Excludes All This validates that a string value does not contain any Unicode code points in the substring value. Usage: excludesall=!@#? Excludes Rune This validates that a string value does not contain the supplied rune value. Usage: excludesrune=@ International Standard Book Number This validates that a string value contains a valid isbn10 or isbn13 value. Usage: isbn International Standard Book Number 10 This validates that a string value contains a valid isbn10 value. Usage: isbn10 International Standard Book Number 13 This validates that a string value contains a valid isbn13 value. Usage: isbn13 Universally Unique Identifier UUID This validates that a string value contains a valid UUID. Usage: uuid Universally Unique Identifier UUID v3 This validates that a string value contains a valid version 3 UUID. Usage: uuid3 Universally Unique Identifier UUID v4 This validates that a string value contains a valid version 4 UUID. Usage: uuid4 Universally Unique Identifier UUID v5 This validates that a string value contains a valid version 5 UUID. Usage: uuid5 ASCII This validates that a string value contains only ASCII characters. NOTE: if the string is blank, this validates as true. Usage: ascii Printable ASCII This validates that a string value contains only printable ASCII characters. NOTE: if the string is blank, this validates as true. Usage: asciiprint Multi-Byte Characters This validates that a string value contains one or more multibyte characters. NOTE: if the string is blank, this validates as true. Usage: multibyte Data URL This validates that a string value contains a valid DataURI. NOTE: this will also validate that the data portion is valid base64 Usage: datauri Latitude This validates that a string value contains a valid latitude. Usage: latitude Longitude This validates that a string value contains a valid longitude. Usage: longitude Social Security Number SSN This validates that a string value contains a valid U.S. Social Security Number. Usage: ssn Internet Protocol Address IP This validates that a string value contains a valid IP Adress. Usage: ip Internet Protocol Address IPv4 This validates that a string value contains a valid v4 IP Adress. Usage: ipv4 Internet Protocol Address IPv6 This validates that a string value contains a valid v6 IP Adress. Usage: ipv6 Classless Inter-Domain Routing CIDR This validates that a string value contains a valid CIDR Adress. Usage: cidr Classless Inter-Domain Routing CIDRv4 This validates that a string value contains a valid v4 CIDR Adress. Usage: cidrv4 Classless Inter-Domain Routing CIDRv6 This validates that a string value contains a valid v6 CIDR Adress. Usage: cidrv6 Transmission Control Protocol Address TCP This validates that a string value contains a valid resolvable TCP Adress. Usage: tcp_addr Transmission Control Protocol Address TCPv4 This validates that a string value contains a valid resolvable v4 TCP Adress. Usage: tcp4_addr Transmission Control Protocol Address TCPv6 This validates that a string value contains a valid resolvable v6 TCP Adress. Usage: tcp6_addr User Datagram Protocol Address UDP This validates that a string value contains a valid resolvable UDP Adress. Usage: udp_addr User Datagram Protocol Address UDPv4 This validates that a string value contains a valid resolvable v4 UDP Adress. Usage: udp4_addr User Datagram Protocol Address UDPv6 This validates that a string value contains a valid resolvable v6 UDP Adress. Usage: udp6_addr Internet Protocol Address IP This validates that a string value contains a valid resolvable IP Adress. Usage: ip_addr Internet Protocol Address IPv4 This validates that a string value contains a valid resolvable v4 IP Adress. Usage: ip4_addr Internet Protocol Address IPv6 This validates that a string value contains a valid resolvable v6 IP Adress. Usage: ip6_addr Unix domain socket end point Address This validates that a string value contains a valid Unix Adress. Usage: unix_addr Media Access Control Address MAC This validates that a string value contains a valid MAC Adress. Usage: mac Note: See Go's ParseMAC for accepted formats and types: http://golang.org/src/net/mac.go?s=866:918#L29 Alias Validators and Tags NOTE: When returning an error, the tag returned in "FieldError" will be the alias tag unless the dive tag is part of the alias. Everything after the dive tag is not reported as the alias tag. Also, the "ActualTag" in the before case will be the actual tag within the alias that failed. Here is a list of the current built in alias tags: "iscolor" alias is "hexcolor|rgb|rgba|hsl|hsla" (Usage: iscolor) Validator notes: regex a regex validator won't be added because commas and = signs can be part of a regex which conflict with the validation definitions. Although workarounds can be made, they take away from using pure regex's. Furthermore it's quick and dirty but the regex's become harder to maintain and are not reusable, so it's as much a programming philosiphy as anything. In place of this new validator functions should be created; a regex can be used within the validator function and even be precompiled for better efficiency within regexes.go. And the best reason, you can submit a pull request and we can keep on adding to the validation library of this package! Panics This package panics when bad input is provided, this is by design, bad code like that should not make it to production. type Test struct { TestField string `validate:"nonexistantfunction=1"` } t := &Test{ TestField: "Test" } validate.Struct(t) // this will panic */ package validator validator-8.18.1/examples/000077500000000000000000000000001274315620500154215ustar00rootroot00000000000000validator-8.18.1/examples/custom/000077500000000000000000000000001274315620500167335ustar00rootroot00000000000000validator-8.18.1/examples/custom/custom.go000066400000000000000000000020251274315620500205730ustar00rootroot00000000000000package main import ( "database/sql" "database/sql/driver" "fmt" "reflect" "gopkg.in/go-playground/validator.v8" ) // DbBackedUser User struct type DbBackedUser struct { Name sql.NullString `validate:"required"` Age sql.NullInt64 `validate:"required"` } func main() { config := &validator.Config{TagName: "validate"} validate := validator.New(config) // register all sql.Null* types to use the ValidateValuer CustomTypeFunc validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} errs := validate.Struct(x) if errs != nil { fmt.Printf("Errs:\n%+v\n", errs) } } // ValidateValuer implements validator.CustomTypeFunc func ValidateValuer(field reflect.Value) interface{} { if valuer, ok := field.Interface().(driver.Valuer); ok { val, err := valuer.Value() if err == nil { return val } // handle the error how you want } return nil } validator-8.18.1/examples/simple/000077500000000000000000000000001274315620500167125ustar00rootroot00000000000000validator-8.18.1/examples/simple/simple.go000066400000000000000000000065111274315620500205350ustar00rootroot00000000000000package main import ( "errors" "fmt" "reflect" sql "database/sql/driver" "gopkg.in/go-playground/validator.v8" ) // User contains user information type User struct { FirstName string `validate:"required"` LastName string `validate:"required"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"hexcolor|rgb|rgba"` Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... } // Address houses a users address information type Address struct { Street string `validate:"required"` City string `validate:"required"` Planet string `validate:"required"` Phone string `validate:"required"` } var validate *validator.Validate func main() { config := &validator.Config{TagName: "validate"} validate = validator.New(config) validateStruct() validateField() } func validateStruct() { address := &Address{ Street: "Eavesdown Docks", Planet: "Persphone", Phone: "none", } user := &User{ FirstName: "Badger", LastName: "Smith", Age: 135, Email: "Badger.Smith@gmail.com", FavouriteColor: "#000", Addresses: []*Address{address}, } // returns nil or ValidationErrors ( map[string]*FieldError ) errs := validate.Struct(user) if errs != nil { fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag // Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag err := errs.(validator.ValidationErrors)["User.Addresses[0].City"] fmt.Println(err.Field) // output: City fmt.Println(err.Tag) // output: required fmt.Println(err.Kind) // output: string fmt.Println(err.Type) // output: string fmt.Println(err.Param) // output: fmt.Println(err.Value) // output: // from here you can create your own error messages in whatever language you wish return } // save user to database } func validateField() { myEmail := "joeybloggs.gmail.com" errs := validate.Field(myEmail, "required,email") if errs != nil { fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "email" tag return } // email ok, move on } var validate2 *validator.Validate type valuer struct { Name string } func (v valuer) Value() (sql.Value, error) { if v.Name == "errorme" { return nil, errors.New("some kind of error") } if v.Name == "blankme" { return "", nil } if len(v.Name) == 0 { return nil, nil } return v.Name, nil } // ValidateValuerType implements validator.CustomTypeFunc func ValidateValuerType(field reflect.Value) interface{} { if valuer, ok := field.Interface().(sql.Valuer); ok { val, err := valuer.Value() if err != nil { // handle the error how you want return nil } return val } return nil } func main2() { config := &validator.Config{TagName: "validate"} validate2 = validator.New(config) validate2.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) validateCustomFieldType() } func validateCustomFieldType() { val := valuer{ Name: "blankme", } errs := validate2.Field(val, "required") if errs != nil { fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag return } // all ok } validator-8.18.1/examples/struct-level/000077500000000000000000000000001274315620500200525ustar00rootroot00000000000000validator-8.18.1/examples/struct-level/struct_level.go000066400000000000000000000061241274315620500231170ustar00rootroot00000000000000package main import ( "fmt" "reflect" "gopkg.in/go-playground/validator.v8" ) // User contains user information type User struct { FirstName string `json:"fname"` LastName string `json:"lname"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"hexcolor|rgb|rgba"` Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage... } // Address houses a users address information type Address struct { Street string `validate:"required"` City string `validate:"required"` Planet string `validate:"required"` Phone string `validate:"required"` } var validate *validator.Validate func main() { config := &validator.Config{TagName: "validate"} validate = validator.New(config) validate.RegisterStructValidation(UserStructLevelValidation, User{}) validateStruct() } // UserStructLevelValidation contains custom struct level validations that don't always // make sense at the field validation level. For Example this function validates that either // FirstName or LastName exist; could have done that with a custom field validation but then // would have had to add it to both fields duplicating the logic + overhead, this way it's // only validated once. // // NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way // hooks right into validator and you can combine with validation tags and still have a // common error output format. func UserStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) { user := structLevel.CurrentStruct.Interface().(User) if len(user.FirstName) == 0 && len(user.LastName) == 0 { structLevel.ReportError(reflect.ValueOf(user.FirstName), "FirstName", "fname", "fnameorlname") structLevel.ReportError(reflect.ValueOf(user.LastName), "LastName", "lname", "fnameorlname") } // plus can to more, even with different tag than "fnameorlname" } func validateStruct() { address := &Address{ Street: "Eavesdown Docks", Planet: "Persphone", Phone: "none", City: "Unknown", } user := &User{ FirstName: "", LastName: "", Age: 45, Email: "Badger.Smith@gmail.com", FavouriteColor: "#000", Addresses: []*Address{address}, } // returns nil or ValidationErrors ( map[string]*FieldError ) errs := validate.Struct(user) if errs != nil { fmt.Println(errs) // output: Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag // Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag err := errs.(validator.ValidationErrors)["User.FirstName"] fmt.Println(err.Field) // output: FirstName fmt.Println(err.Tag) // output: fnameorlname fmt.Println(err.Kind) // output: string fmt.Println(err.Type) // output: string fmt.Println(err.Param) // output: fmt.Println(err.Value) // output: // from here you can create your own error messages in whatever language you wish return } // save user to database } validator-8.18.1/examples_test.go000066400000000000000000000035211274315620500170100ustar00rootroot00000000000000package validator_test import ( "fmt" "gopkg.in/go-playground/validator.v8" ) func ExampleValidate_new() { config := &validator.Config{TagName: "validate"} validator.New(config) } func ExampleValidate_field() { // This should be stored somewhere globally var validate *validator.Validate config := &validator.Config{TagName: "validate"} validate = validator.New(config) i := 0 errs := validate.Field(i, "gt=1,lte=10") err := errs.(validator.ValidationErrors)[""] fmt.Println(err.Field) fmt.Println(err.Tag) fmt.Println(err.Kind) // NOTE: Kind and Type can be different i.e. time Kind=struct and Type=time.Time fmt.Println(err.Type) fmt.Println(err.Param) fmt.Println(err.Value) //Output: // //gt //int //int //1 //0 } func ExampleValidate_struct() { // This should be stored somewhere globally var validate *validator.Validate config := &validator.Config{TagName: "validate"} validate = validator.New(config) type ContactInformation struct { Phone string `validate:"required"` Street string `validate:"required"` City string `validate:"required"` } type User struct { Name string `validate:"required,excludesall=!@#$%^&*()_+-=:;?/0x2C"` // 0x2C = comma (,) Age int8 `validate:"required,gt=0,lt=150"` Email string `validate:"email"` ContactInformation []*ContactInformation } contactInfo := &ContactInformation{ Street: "26 Here Blvd.", City: "Paradeso", } user := &User{ Name: "Joey Bloggs", Age: 31, Email: "joeybloggs@gmail.com", ContactInformation: []*ContactInformation{contactInfo}, } errs := validate.Struct(user) for _, v := range errs.(validator.ValidationErrors) { fmt.Println(v.Field) // Phone fmt.Println(v.Tag) // required //... and so forth //Output: //Phone //required } } validator-8.18.1/logo.png000066400000000000000000000322031274315620500152510ustar00rootroot00000000000000PNG  IHDR<qsBIT|d pHYs  ~tEXtSoftwareAdobe Fireworks CS6輲 IDATxwEǿ=yg6/,yQEATD1+S99' ;Sp(&T(y 8?gwfمe}>=5]ݿyz)!hkhcXhtbu]A :ՁvA:. V@Xhtbu]A :Ձvmww Y n H2, g€A_5P߀ŷTⱄ  >ޜSdurgd32]4\TcD0a jk"@xkAMi`;(Rw= Pl gWV~=3x{ڥ+ޜN2px9]GhBD@H R"M#% &PVJu*6t*Y E+~[X?C˒JFGdxul %#/Lt)%f4b&HEZlA4m6R,-b׬w?*PĊ#T8ov)}?!m݆ [wt#" # cICw:9]DA,_ƪ9ۧ.+_F? ,u ٳEtGCNAM 0]4]Chz3 CXlN6 ,}-5Ғ{O1eݓ(")e"v{ k4붤 s~>'N!WoZpȪ{Ӊ.^͵ls:ќ.TQS^:L2:uI! `CeKGg)tB׽'{,ᤦxk`ekW!:RJ+~)BRKȲ0ΙJȈRMB\AG{!Z63 tu5`Mqy}x|>tM#`D#먔\nE \Etrt"AqE[B9]7(zJÈ0|]_:uv76}\4 #BMVV_y~]Y[w\IE,K؀!>H7χSYX?X>}5꼋@$MQ/ث*M'jTv\={`D 4려F,U5|>`:uw 7/͏?SOzNH fqgo9dꍿ$͆ӗJ||ߢ?|{)03ɕ4IJHe&d̛vn5hv;`i*Eddr0cWcw: B,h+1vpƏ9!CpP[QNڵ|?>n<8k*]e;}հ3q/͗_!hލJsO<γ} Sg"?RR2X9S{Q{i2zAFF8>3k . I6?%C: ޟy.н V+_PvG|~߭z@eeX2~gn~G\4&q)qgfGa[01<Y (K35ysy/~:YJYN#XIϾ2@Fw80#ay_u!/ PV+5?cgvϝ3a5{UrF!T] 2t޺'M۩~FM>"P^pjg<ſ^*Rtӗ7{oHt$M/Kq,Ǟx|ozy3DY\.uRgc'd*>Zehv{]@ GdB]Nz TYڔy㜓xC՛@: ?7@׼;Չ=zrEy< vm e,.>s |fwԝReܵݧ?e)\]ןh(HV~x; mҡ6Bo#dÈ(OŦ |vH0KԆ~!3=~|t专N{ͫVl1рfy zÛi72` [ 4: dfsU7xFP&vO N_DM$ uNr:(Y;rl'a:x/f$ ncˏsr mQbyrŌo Ecs5uk  y =ň3ϧD:F$PKd`+flRA4&@<D!hOϘqj`-٦}pԛM? Apf2{>ݺRPIUHD)3N+Y@&IJF2EP{rOȳ/ii'ur\6SnpCsN9saiiDm]B%1l] %ER0 |gLvbYXq᏿Z jO<xJ}G~!ZR-!NIAmO r7Ҍۄ4Q#!GORٸG.7Bdd+/O>Z9/>Hbm OjH-F)Y~MtI*vBXx7;0N>TV\i`&TtaS_ tS/ /Tgz&4W{Ql\# )^4_>|" TjmO$ ~w>ft/HOϋy!hu񛘦'-Zl&>c>Lkw8m%؝FI7o)}?`Mb l4EVZբyM>Ss cTo̊_2?C75 IJ87y9FK)_;#~B}Yl_^{utoj:kW3op.Ezy _.[N0%O9ȣ$UU4K~uay}xDU*+GOt?~G)eeۅH bH)Co7>4;t/n7$NѽnP]X'f͚`|\zM4SI0ԉpALź^r1O@ݑd! FO=C#H?Tu(45T@+2)e4Doʿ΃ ' 3Dm_tT¡ gذa6vl=|`D#N^/T\ }Qx|׌{= ӬP(i ~a@4 w kҠt̺_W8FJٍ$3F 8|'<ψF-}۠`!ۮ#pE,yW0ؼl >(k~I\ AJv#8^' sZ_qÏ3W\&e3 j-U7T3 ޣvcTU RJh 76qM7 FJƌ<οxth Yʬ Nr&4MpZ`P[RO<z<{N0q#/Zu .> t8M Z[aD\dʋ- VKa^i](h F$ag]/pGrĉtֽ6n_|۟~FAj6xNHÌܼ ӝЖjфYxUs뽬]ů#˦f0M*2o4Mf#\ȏe;P!{/BlQHJb=|sUH .µyeg}^UUM Fnq1 E5]V,DH 41~yM~= AC!JK)+0759x^pH0H8;.K 5+B^RJI6M͘2yf/Ēn௭`U[ kA^i]Khe {9k4_}}7D t[ΡH.FhSMSn2> TdXNwjZ[ܔ*lP`D#ՕBйGOS7y+ PKCU]#ߟ{1y ^^ݲ3bǏߏ4GdyT5Q-IMIJvN@e V =TNjrʷr9*CG{;>|ɽut0R߻/fD5px)"P$yx9W(Cy,A4qz}2_AR9HND!ub'Bᵿ1՝ B+k #D p8B`H  ѭ39fo6F!ˍwV-ugP9*s3&ʹ*l;,ƽ!XVmʱø+KϠy{{~u,+Wo%k=` 3Cn;h(=@8.v셸ehcU*ޙs&-XT/oHπzO}ˌum !->ߡiq1sA܌7ToC]5h"v{ 18^PhL, % Mi0׳d3\0(^s CW+W =-G#!C'˿R9u1hiU!ovH$s!yVn'UhͨC>?|ygFZ/).w^^ݱ>NJn^0"D$azT!l*y! :* FF>RG5ꬺw-ȠHm2ڷD[¦bʜ;J^; H؈D$@a'ʒˀfGzRؐ>36 MK|Tv]ǴM^RhJZ2P  Z+ޙ2MW>c>˄TR]Q:[j;l2fl ' J,#FC!* :>Y?pm ? KBGm9K}W3o54X7ILdѰ&ܡ;M 0py<M\S>,NUP$i}yTu$R"Ub+&vv*QϮWqhd )%4?X]4SjSe0 d >S&N]TbS9NEIdHJ$%,k[ D%h*iZ-i IKwj YN;vM#b"OeTv8Ma뷏*v$Dd%N $ XPuU nbyZxU_=[9w7؛.A0wSs6l%V8H:r9`w8}i|0dB:ǹv Gq35?/F{:rbe`Jh8ed.[Ɵʷ𷊍ͥps]FHӄwis$ e-BtGVѝW+=+WCϞٸ "NvnW`ƎLk UŁ5ipbV*;7Oh*2uLjDӣB04ܬt;d vU~z x !H)6}J,kI&p.ӑFOntJoPQ(Ι٪V45?|C&&mh`^|qΦwlXN})bTJMqi5TT NƩS&0i>|!FH)]F,k}xO=3ě@h$næiІ4)^LQ,{CՙwϞXMraj۽`n(ᨁtxyP&3S#ic9l6^{~^@i%`HT%*vhF@'=##dCUQj `2/ǐ60G;S/miDpfyx!c,}.,|;ݰ;UajnNƤ)ǎ_?d=+p8M!# Q%FZ4ӨwoM+ƍPPuDzel8ajP *ۅi,e.ye6́#aHE\̩SQ!ķR%;~Vm)EwCrHOTB?)m 띀4һps{C׮0|M))EꏇW~ѽ'a*fM>5=O}7w-'~IVt<}KۤmXUp t͖*^y ?}Eg{<.+dmT4Mz8~=4y/~>6 >*F-U!]0<)݁ˀǁ')@LwRAM>vn#6UT?~>J=^KV^ <jvկmcmq)֭Kzǟ}78EMfC> !z46n%T EjrCl=XD0`#22s+ϙ?~7ܐy VtƹUFm<*ݶ=xFP;;v@JiJ) wZp^-iIDATx}.ۓhpP ޤqƌIzӝw1Xvr;RTD~l|g"'8!u۩&H b5!Dbw[/(+9>xNnpW#AsԻ"@yޟx"jvB쳝;%בͰ\HPZjEJ0f[_O k ?bY$:D8\jM_}Y&vw@*%xNBoHfbe8}n :js;SU'0-U0dH㗼Dm*w?c=Oƨn)DU.:PZ¨ZJ~NVŽ{Hfb|izEB*FG :?w:/,YAAe2xO1 7q,ԇaRFB*:nIt(֡DQ {\#F҄4TSjk}Y WUD"!R33Y1Xu΢GIz~@gQzݬ\r6ز.'/ک,t&f"6ؖ4/J0bBHJbYdJsz}/ G- Lt~qοpN'+ 4*$atY&A7 Kxóet@a.DNQoo5j / !2*$+$p{6öB,QIJT˨amEE1L]sHVbP( &;TalHag0&dWК|.U,Z&YFH[?*B#! 7nKԊ=20>nR=T԰NG*x71Ӛ'("Ş`̇F__kmx}knq.pF,P? Gu1d(+Y\.LgL±OĿtup8JuuM%J ^E7568v,l4+F,0eߡ}zrѝQ_W.-X./?g<]')6Wr%sO9TXPQ Tpu_sG0OB2 %(mraKK6C~ֳIsS/5 >rH PJ4GŖB4'X*I릠Sfʊ|Ơ@.(?S;;wIA,.,l-&=,[ρh@P9u {%rKYLj3/ǰ0"~n`qmN'vw XӜrpo~B MT_~+7u],~AԢlD9JQp򕝹=X:pǾWB(D[ou'?6xO>#FpmnL$ k>ϢǺ58Yٜua˕•f1`\gHNE&v5<ڟ@lsZJUd);{I52;dNɠ\LtDRtd=f}+ͭڼ,Fw^LvRӐRF4R1T44݆pl"Jlx|~ *t`缴4.OC ݦSVY#3>Wn䘉xOֵ~HRAvd(Ig9Yh RA ܋W^͖JF4V*Emm o͞ ڵ\?Ó2˰}t{ܡ{wܮRvGa" )Mo(хfh]GuCH%E䯥dENmIR| |4C9j&w0j _YHZJ}Ai`l|-|~;U[=AOˣ7Lg¡0t/o랋~1Pqk )!4Eʶ E7W^yYi{Ss;=/ۍfuLJcDD TVP[RLM7m$~-ӣ!aBqP/DGJY`ݧܣkG{oG0"Q,\ɳo͡?Q cP PXwe6f1JFT}KwmcO _^[?0nL(/^¥TbͦGQqkQd̍"Z*JKtB͖tfvGÓ⳻n鰹\sLJS`8 @ Wp!8aJ =ԌK)?hfMgw9b0z,{ *I=L&s5QNT5U)9McO 2'k,㉙/ BOf 퀛sMP? ֹ툢fd7 5e uF\Q_8( ϊmF[<R9}k7KC ]@jǛ+N\KCIN}ڒ|<+AnYo,IjgGHxXS!wCBRmG]GgƢjb+a)8b%3Ĥkdwr_w[8>b sV)O֏b1a-$^cQ")voXh)1A :ՁvA:. V@Xhtbu]A :Ձv0IENDB`validator-8.18.1/regexes.go000066400000000000000000000123011274315620500155710ustar00rootroot00000000000000package validator import "regexp" const ( alphaRegexString = "^[a-zA-Z]+$" alphaNumericRegexString = "^[a-zA-Z0-9]+$" numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$" numberRegexString = "^[0-9]+$" hexadecimalRegexString = "^[0-9a-fA-F]+$" hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" 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*\\)$" hslaRegexString = "^hsla\\(\\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*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$" emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:\\(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22)))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$" iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$" uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" aSCIIRegexString = "^[\x00-\x7F]*$" printableASCIIRegexString = "^[\x20-\x7E]*$" multibyteRegexString = "[^\x00-\x7F]" dataURIRegexString = "^data:.+\\/(.+);base64$" 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}$` ) var ( alphaRegex = regexp.MustCompile(alphaRegexString) alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString) numericRegex = regexp.MustCompile(numericRegexString) numberRegex = regexp.MustCompile(numberRegexString) hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString) hexcolorRegex = regexp.MustCompile(hexcolorRegexString) rgbRegex = regexp.MustCompile(rgbRegexString) rgbaRegex = regexp.MustCompile(rgbaRegexString) hslRegex = regexp.MustCompile(hslRegexString) hslaRegex = regexp.MustCompile(hslaRegexString) emailRegex = regexp.MustCompile(emailRegexString) base64Regex = regexp.MustCompile(base64RegexString) iSBN10Regex = regexp.MustCompile(iSBN10RegexString) iSBN13Regex = regexp.MustCompile(iSBN13RegexString) uUID3Regex = regexp.MustCompile(uUID3RegexString) uUID4Regex = regexp.MustCompile(uUID4RegexString) uUID5Regex = regexp.MustCompile(uUID5RegexString) uUIDRegex = regexp.MustCompile(uUIDRegexString) aSCIIRegex = regexp.MustCompile(aSCIIRegexString) printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString) multibyteRegex = regexp.MustCompile(multibyteRegexString) dataURIRegex = regexp.MustCompile(dataURIRegexString) latitudeRegex = regexp.MustCompile(latitudeRegexString) longitudeRegex = regexp.MustCompile(longitudeRegexString) sSNRegex = regexp.MustCompile(sSNRegexString) ) validator-8.18.1/util.go000066400000000000000000000154051274315620500151140ustar00rootroot00000000000000package validator import ( "reflect" "strconv" "strings" ) const ( blank = "" namespaceSeparator = "." leftBracket = "[" rightBracket = "]" restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" ) var ( restrictedTags = map[string]struct{}{ diveTag: {}, existsTag: {}, structOnlyTag: {}, omitempty: {}, skipValidationTag: {}, utf8HexComma: {}, utf8Pipe: {}, noStructLevelTag: {}, } ) // ExtractType gets the actual underlying type of field value. // It will dive into pointers, customTypes and return you the // underlying value and it's kind. // it is exposed for use within you Custom Functions func (v *Validate) ExtractType(current reflect.Value) (reflect.Value, reflect.Kind) { val, k, _ := v.extractTypeInternal(current, false) return val, k } // only exists to not break backward compatibility, needed to return the third param for a bug fix internally func (v *Validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) { switch current.Kind() { case reflect.Ptr: nullable = true if current.IsNil() { return current, reflect.Ptr, nullable } return v.extractTypeInternal(current.Elem(), nullable) case reflect.Interface: nullable = true if current.IsNil() { return current, reflect.Interface, nullable } return v.extractTypeInternal(current.Elem(), nullable) case reflect.Invalid: return current, reflect.Invalid, nullable default: if v.hasCustomFuncs { if fn, ok := v.customTypeFuncs[current.Type()]; ok { return v.extractTypeInternal(reflect.ValueOf(fn(current)), nullable) } } return current, current.Kind(), nullable } } // GetStructFieldOK traverses a struct to retrieve a specific field denoted by the provided namespace and // returns the field, field kind and whether is was successful in retrieving the field at all. // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // could not be retrieved because it didn't exist. func (v *Validate) GetStructFieldOK(current reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) { current, kind := v.ExtractType(current) if kind == reflect.Invalid { return current, kind, false } if namespace == blank { return current, kind, true } switch kind { case reflect.Ptr, reflect.Interface: return current, kind, false case reflect.Struct: typ := current.Type() fld := namespace ns := namespace if typ != timeType && typ != timePtrType { idx := strings.Index(namespace, namespaceSeparator) if idx != -1 { fld = namespace[:idx] ns = namespace[idx+1:] } else { ns = blank } bracketIdx := strings.Index(fld, leftBracket) if bracketIdx != -1 { fld = fld[:bracketIdx] ns = namespace[bracketIdx:] } current = current.FieldByName(fld) return v.GetStructFieldOK(current, ns) } case reflect.Array, reflect.Slice: idx := strings.Index(namespace, leftBracket) idx2 := strings.Index(namespace, rightBracket) arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2]) if arrIdx >= current.Len() { return current, kind, false } startIdx := idx2 + 1 if startIdx < len(namespace) { if namespace[startIdx:startIdx+1] == namespaceSeparator { startIdx++ } } return v.GetStructFieldOK(current.Index(arrIdx), namespace[startIdx:]) case reflect.Map: idx := strings.Index(namespace, leftBracket) + 1 idx2 := strings.Index(namespace, rightBracket) endIdx := idx2 if endIdx+1 < len(namespace) { if namespace[endIdx+1:endIdx+2] == namespaceSeparator { endIdx++ } } key := namespace[idx:idx2] switch current.Type().Key().Kind() { case reflect.Int: i, _ := strconv.Atoi(key) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) case reflect.Int8: i, _ := strconv.ParseInt(key, 10, 8) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(int8(i))), namespace[endIdx+1:]) case reflect.Int16: i, _ := strconv.ParseInt(key, 10, 16) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(int16(i))), namespace[endIdx+1:]) case reflect.Int32: i, _ := strconv.ParseInt(key, 10, 32) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(int32(i))), namespace[endIdx+1:]) case reflect.Int64: i, _ := strconv.ParseInt(key, 10, 64) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) case reflect.Uint: i, _ := strconv.ParseUint(key, 10, 0) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint(i))), namespace[endIdx+1:]) case reflect.Uint8: i, _ := strconv.ParseUint(key, 10, 8) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint8(i))), namespace[endIdx+1:]) case reflect.Uint16: i, _ := strconv.ParseUint(key, 10, 16) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint16(i))), namespace[endIdx+1:]) case reflect.Uint32: i, _ := strconv.ParseUint(key, 10, 32) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(uint32(i))), namespace[endIdx+1:]) case reflect.Uint64: i, _ := strconv.ParseUint(key, 10, 64) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) case reflect.Float32: f, _ := strconv.ParseFloat(key, 32) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(float32(f))), namespace[endIdx+1:]) case reflect.Float64: f, _ := strconv.ParseFloat(key, 64) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(f)), namespace[endIdx+1:]) case reflect.Bool: b, _ := strconv.ParseBool(key) return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(b)), namespace[endIdx+1:]) // reflect.Type = string default: return v.GetStructFieldOK(current.MapIndex(reflect.ValueOf(key)), namespace[endIdx+1:]) } } // if got here there was more namespace, cannot go any deeper panic("Invalid field namespace") } // asInt returns the parameter as a int64 // or panics if it can't convert func asInt(param string) int64 { i, err := strconv.ParseInt(param, 0, 64) panicIf(err) return i } // asUint returns the parameter as a uint64 // or panics if it can't convert func asUint(param string) uint64 { i, err := strconv.ParseUint(param, 0, 64) panicIf(err) return i } // asFloat returns the parameter as a float64 // or panics if it can't convert func asFloat(param string) float64 { i, err := strconv.ParseFloat(param, 64) panicIf(err) return i } func panicIf(err error) { if err != nil { panic(err.Error()) } } validator-8.18.1/validator.go000066400000000000000000000542451274315620500161310ustar00rootroot00000000000000/** * Package validator * * MISC: * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank * */ package validator import ( "bytes" "errors" "fmt" "reflect" "strings" "sync" "time" ) const ( utf8HexComma = "0x2C" utf8Pipe = "0x7C" tagSeparator = "," orSeparator = "|" tagKeySeparator = "=" structOnlyTag = "structonly" noStructLevelTag = "nostructlevel" omitempty = "omitempty" skipValidationTag = "-" diveTag = "dive" existsTag = "exists" fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag" arrayIndexFieldName = "%s" + leftBracket + "%d" + rightBracket mapIndexFieldName = "%s" + leftBracket + "%v" + rightBracket invalidValidation = "Invalid validation tag on field %s" undefinedValidation = "Undefined validation function on field %s" validatorNotInitialized = "Validator instance not initialized" fieldNameRequired = "Field Name Required" tagRequired = "Tag Required" ) var ( timeType = reflect.TypeOf(time.Time{}) timePtrType = reflect.TypeOf(&time.Time{}) defaultCField = new(cField) ) // StructLevel contains all of the information and helper methods // for reporting errors during struct level validation type StructLevel struct { TopStruct reflect.Value CurrentStruct reflect.Value errPrefix string nsPrefix string errs ValidationErrors v *Validate } // ReportValidationErrors accepts the key relative to the top level struct and validatin errors. // Example: had a triple nested struct User, ContactInfo, Country and ran errs := validate.Struct(country) // from within a User struct level validation would call this method like so: // ReportValidationErrors("ContactInfo.", errs) // NOTE: relativeKey can contain both the Field Relative and Custom name relative paths // i.e. ReportValidationErrors("ContactInfo.|cInfo", errs) where cInfo represents say the JSON name of // the relative path; this will be split into 2 variables in the next valiator version. func (sl *StructLevel) ReportValidationErrors(relativeKey string, errs ValidationErrors) { for _, e := range errs { idx := strings.Index(relativeKey, "|") var rel string var cRel string if idx != -1 { rel = relativeKey[:idx] cRel = relativeKey[idx+1:] } else { rel = relativeKey } key := sl.errPrefix + rel + e.Field e.FieldNamespace = key e.NameNamespace = sl.nsPrefix + cRel + e.Name sl.errs[key] = e } } // ReportError reports an error just by passing the field and tag information // NOTE: tag can be an existing validation tag or just something you make up // and precess on the flip side it's up to you. func (sl *StructLevel) ReportError(field reflect.Value, fieldName string, customName string, tag string) { field, kind := sl.v.ExtractType(field) if fieldName == blank { panic(fieldNameRequired) } if customName == blank { customName = fieldName } if tag == blank { panic(tagRequired) } ns := sl.errPrefix + fieldName switch kind { case reflect.Invalid: sl.errs[ns] = &FieldError{ FieldNamespace: ns, NameNamespace: sl.nsPrefix + customName, Name: customName, Field: fieldName, Tag: tag, ActualTag: tag, Param: blank, Kind: kind, } default: sl.errs[ns] = &FieldError{ FieldNamespace: ns, NameNamespace: sl.nsPrefix + customName, Name: customName, Field: fieldName, Tag: tag, ActualTag: tag, Param: blank, Value: field.Interface(), Kind: kind, Type: field.Type(), } } } // Validate contains the validator settings passed in using the Config struct type Validate struct { tagName string fieldNameTag string validationFuncs map[string]Func structLevelFuncs map[reflect.Type]StructLevelFunc customTypeFuncs map[reflect.Type]CustomTypeFunc aliasValidators map[string]string hasCustomFuncs bool hasAliasValidators bool hasStructLevelFuncs bool tagCache *tagCache structCache *structCache errsPool *sync.Pool } func (v *Validate) initCheck() { if v == nil { panic(validatorNotInitialized) } } // Config contains the options that a Validator instance will use. // It is passed to the New() function type Config struct { TagName string FieldNameTag string } // CustomTypeFunc allows for overriding or adding custom field type handler functions // field = field value of the type to return a value to be validated // example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29 type CustomTypeFunc func(field reflect.Value) interface{} // Func accepts all values needed for file and cross field validation // v = validator instance, needed but some built in functions for it's custom types // topStruct = top level struct when validating by struct otherwise nil // currentStruct = current level struct when validating by struct otherwise optional comparison value // field = field value for validation // param = parameter used in validation i.e. gt=0 param would be 0 type Func func(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool // StructLevelFunc accepts all values needed for struct level validation type StructLevelFunc func(v *Validate, structLevel *StructLevel) // ValidationErrors is a type of map[string]*FieldError // it exists to allow for multiple errors to be passed from this library // and yet still subscribe to the error interface type ValidationErrors map[string]*FieldError // Error is intended for use in development + debugging and not intended to be a production error message. // It allows ValidationErrors to subscribe to the Error interface. // All information to create an error message specific to your application is contained within // the FieldError found within the ValidationErrors map func (ve ValidationErrors) Error() string { buff := bytes.NewBufferString(blank) for key, err := range ve { buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag)) buff.WriteString("\n") } return strings.TrimSpace(buff.String()) } // FieldError contains a single field's validation error along // with other properties that may be needed for error message creation type FieldError struct { FieldNamespace string NameNamespace string Field string Name string Tag string ActualTag string Kind reflect.Kind Type reflect.Type Param string Value interface{} } // New creates a new Validate instance for use. func New(config *Config) *Validate { tc := new(tagCache) tc.m.Store(make(map[string]*cTag)) sc := new(structCache) sc.m.Store(make(map[reflect.Type]*cStruct)) v := &Validate{ tagName: config.TagName, fieldNameTag: config.FieldNameTag, tagCache: tc, structCache: sc, errsPool: &sync.Pool{New: func() interface{} { return ValidationErrors{} }}} if len(v.aliasValidators) == 0 { // must copy alias validators for separate validations to be used in each validator instance v.aliasValidators = map[string]string{} for k, val := range bakedInAliasValidators { v.RegisterAliasValidation(k, val) } } if len(v.validationFuncs) == 0 { // must copy validators for separate validations to be used in each instance v.validationFuncs = map[string]Func{} for k, val := range bakedInValidators { v.RegisterValidation(k, val) } } return v } // RegisterStructValidation registers a StructLevelFunc against a number of types // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) { v.initCheck() if v.structLevelFuncs == nil { v.structLevelFuncs = map[reflect.Type]StructLevelFunc{} } for _, t := range types { v.structLevelFuncs[reflect.TypeOf(t)] = fn } v.hasStructLevelFuncs = true } // RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key // NOTE: if the key already exists, the previous validation function will be replaced. // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation func (v *Validate) RegisterValidation(key string, fn Func) error { v.initCheck() if key == blank { return errors.New("Function Key cannot be empty") } if fn == nil { return errors.New("Function cannot be empty") } _, ok := restrictedTags[key] if ok || strings.ContainsAny(key, restrictedTagChars) { panic(fmt.Sprintf(restrictedTagErr, key)) } v.validationFuncs[key] = fn return nil } // 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 func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { v.initCheck() if v.customTypeFuncs == nil { v.customTypeFuncs = map[reflect.Type]CustomTypeFunc{} } for _, t := range types { v.customTypeFuncs[reflect.TypeOf(t)] = fn } v.hasCustomFuncs = true } // RegisterAliasValidation registers a mapping of a single validationstag that // defines a common or complex set of validation(s) to simplify adding validation // to structs. NOTE: when returning an error the tag returned in FieldError will be // the alias tag unless the dive tag is part of the alias; everything after the // dive tag is not reported as the alias tag. Also the ActualTag in the before case // will be the actual tag within the alias that failed. // NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation func (v *Validate) RegisterAliasValidation(alias, tags string) { v.initCheck() _, ok := restrictedTags[alias] if ok || strings.ContainsAny(alias, restrictedTagChars) { panic(fmt.Sprintf(restrictedAliasErr, alias)) } v.aliasValidators[alias] = tags v.hasAliasValidators = true } // Field validates a single field using tag style validation and returns nil or ValidationErrors as type error. // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. // NOTE: it returns ValidationErrors instead of a single FieldError because this can also // validate Array, Slice and maps fields which may contain more than one error func (v *Validate) Field(field interface{}, tag string) error { v.initCheck() if len(tag) == 0 || tag == skipValidationTag { return nil } errs := v.errsPool.Get().(ValidationErrors) fieldVal := reflect.ValueOf(field) ctag, ok := v.tagCache.Get(tag) if !ok { v.tagCache.lock.Lock() defer v.tagCache.lock.Unlock() // could have been multiple trying to access, but once first is done this ensures tag // isn't parsed again. ctag, ok = v.tagCache.Get(tag) if !ok { ctag, _ = v.parseFieldTagsRecursive(tag, blank, blank, false) v.tagCache.Set(tag, ctag) } } v.traverseField(fieldVal, fieldVal, fieldVal, blank, blank, errs, false, false, nil, nil, defaultCField, ctag) if len(errs) == 0 { v.errsPool.Put(errs) return nil } return errs } // FieldWithValue validates a single field, against another fields value using tag style validation and returns nil or ValidationErrors. // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. // NOTE: it returns ValidationErrors instead of a single FieldError because this can also // validate Array, Slice and maps fields which may contain more than one error func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) error { v.initCheck() if len(tag) == 0 || tag == skipValidationTag { return nil } errs := v.errsPool.Get().(ValidationErrors) topVal := reflect.ValueOf(val) ctag, ok := v.tagCache.Get(tag) if !ok { v.tagCache.lock.Lock() defer v.tagCache.lock.Unlock() // could have been multiple trying to access, but once first is done this ensures tag // isn't parsed again. ctag, ok = v.tagCache.Get(tag) if !ok { ctag, _ = v.parseFieldTagsRecursive(tag, blank, blank, false) v.tagCache.Set(tag, ctag) } } v.traverseField(topVal, topVal, reflect.ValueOf(field), blank, blank, errs, false, false, nil, nil, defaultCField, ctag) if len(errs) == 0 { v.errsPool.Put(errs) return nil } return errs } // StructPartial validates the fields passed in only, ignoring all others. // Fields may be provided in a namespaced fashion relative to the struct provided // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name and returns nil or ValidationErrors as error // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. func (v *Validate) StructPartial(current interface{}, fields ...string) error { v.initCheck() sv, _ := v.ExtractType(reflect.ValueOf(current)) name := sv.Type().Name() m := map[string]struct{}{} if fields != nil { for _, k := range fields { flds := strings.Split(k, namespaceSeparator) if len(flds) > 0 { key := name + namespaceSeparator for _, s := range flds { idx := strings.Index(s, leftBracket) if idx != -1 { for idx != -1 { key += s[:idx] m[key] = struct{}{} idx2 := strings.Index(s, rightBracket) idx2++ key += s[idx:idx2] m[key] = struct{}{} s = s[idx2:] idx = strings.Index(s, leftBracket) } } else { key += s m[key] = struct{}{} } key += namespaceSeparator } } } } errs := v.errsPool.Get().(ValidationErrors) v.ensureValidStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, false, m, false) if len(errs) == 0 { v.errsPool.Put(errs) return nil } return errs } // StructExcept validates all fields except the ones passed in. // Fields may be provided in a namespaced fashion relative to the struct provided // i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name and returns nil or ValidationErrors as error // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. func (v *Validate) StructExcept(current interface{}, fields ...string) error { v.initCheck() sv, _ := v.ExtractType(reflect.ValueOf(current)) name := sv.Type().Name() m := map[string]struct{}{} for _, key := range fields { m[name+namespaceSeparator+key] = struct{}{} } errs := v.errsPool.Get().(ValidationErrors) v.ensureValidStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, true, m, false) if len(errs) == 0 { v.errsPool.Put(errs) return nil } return errs } // Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified. // it returns nil or ValidationErrors as error. // You will need to assert the error if it's not nil i.e. err.(validator.ValidationErrors) to access the map of errors. func (v *Validate) Struct(current interface{}) error { v.initCheck() errs := v.errsPool.Get().(ValidationErrors) sv := reflect.ValueOf(current) v.ensureValidStruct(sv, sv, sv, blank, blank, errs, true, false, false, nil, false) if len(errs) == 0 { v.errsPool.Put(errs) return nil } return errs } func (v *Validate) ensureValidStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]struct{}, isStructOnly bool) { if current.Kind() == reflect.Ptr && !current.IsNil() { current = current.Elem() } if current.Kind() != reflect.Struct && current.Kind() != reflect.Interface { panic("value passed for validation is not a struct") } v.tranverseStruct(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, useStructName, partial, exclude, includeExclude, nil, nil) } // tranverseStruct traverses a structs fields and then passes them to be validated by traverseField func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]struct{}, cs *cStruct, ct *cTag) { var ok bool first := len(nsPrefix) == 0 typ := current.Type() cs, ok = v.structCache.Get(typ) if !ok { cs = v.extractStructCache(current, typ.Name()) } if useStructName { errPrefix += cs.Name + namespaceSeparator if len(v.fieldNameTag) != 0 { nsPrefix += cs.Name + namespaceSeparator } } // structonly tag present don't tranverseFields // but must still check and run below struct level validation // if present if first || ct == nil || ct.typeof != typeStructOnly { for _, f := range cs.fields { if partial { _, ok = includeExclude[errPrefix+f.Name] if (ok && exclude) || (!ok && !exclude) { continue } } v.traverseField(topStruct, currentStruct, current.Field(f.Idx), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, f, f.cTags) } } // check if any struct level validations, after all field validations already checked. if cs.fn != nil { cs.fn(v, &StructLevel{v: v, TopStruct: topStruct, CurrentStruct: current, errPrefix: errPrefix, nsPrefix: nsPrefix, errs: errs}) } } // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, partial bool, exclude bool, includeExclude map[string]struct{}, cs *cStruct, cf *cField, ct *cTag) { current, kind, nullable := v.extractTypeInternal(current, false) var typ reflect.Type switch kind { case reflect.Ptr, reflect.Interface, reflect.Invalid: if ct == nil { return } if ct.typeof == typeOmitEmpty { return } if ct.hasTag { ns := errPrefix + cf.Name if kind == reflect.Invalid { errs[ns] = &FieldError{ FieldNamespace: ns, NameNamespace: nsPrefix + cf.AltName, Name: cf.AltName, Field: cf.Name, Tag: ct.aliasTag, ActualTag: ct.tag, Param: ct.param, Kind: kind, } return } errs[ns] = &FieldError{ FieldNamespace: ns, NameNamespace: nsPrefix + cf.AltName, Name: cf.AltName, Field: cf.Name, Tag: ct.aliasTag, ActualTag: ct.tag, Param: ct.param, Value: current.Interface(), Kind: kind, Type: current.Type(), } return } case reflect.Struct: typ = current.Type() if typ != timeType { if ct != nil { ct = ct.next } if ct != nil && ct.typeof == typeNoStructLevel { return } v.tranverseStruct(topStruct, current, current, errPrefix+cf.Name+namespaceSeparator, nsPrefix+cf.AltName+namespaceSeparator, errs, false, partial, exclude, includeExclude, cs, ct) return } } if !ct.hasTag { return } typ = current.Type() OUTER: for { if ct == nil { return } switch ct.typeof { case typeExists: ct = ct.next continue case typeOmitEmpty: if !nullable && !HasValue(v, topStruct, currentStruct, current, typ, kind, blank) { return } ct = ct.next continue case typeDive: ct = ct.next // traverse slice or map here // or panic ;) switch kind { case reflect.Slice, reflect.Array: for i := 0; i < current.Len(); i++ { v.traverseField(topStruct, currentStruct, current.Index(i), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(arrayIndexFieldName, cf.Name, i), AltName: fmt.Sprintf(arrayIndexFieldName, cf.AltName, i)}, ct) } case reflect.Map: for _, key := range current.MapKeys() { v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, nsPrefix, errs, partial, exclude, includeExclude, cs, &cField{Name: fmt.Sprintf(mapIndexFieldName, cf.Name, key.Interface()), AltName: fmt.Sprintf(mapIndexFieldName, cf.AltName, key.Interface())}, ct) } default: // throw error, if not a slice or map then should not have gotten here // bad dive tag panic("dive error! can't dive on a non slice or map") } return case typeOr: errTag := blank for { if ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) { // drain rest of the 'or' values, then continue or leave for { ct = ct.next if ct == nil { return } if ct.typeof != typeOr { continue OUTER } } } errTag += orSeparator + ct.tag if ct.next == nil { // if we get here, no valid 'or' value and no more tags ns := errPrefix + cf.Name if ct.hasAlias { errs[ns] = &FieldError{ FieldNamespace: ns, NameNamespace: nsPrefix + cf.AltName, Name: cf.AltName, Field: cf.Name, Tag: ct.aliasTag, ActualTag: ct.actualAliasTag, Value: current.Interface(), Type: typ, Kind: kind, } } else { errs[errPrefix+cf.Name] = &FieldError{ FieldNamespace: ns, NameNamespace: nsPrefix + cf.AltName, Name: cf.AltName, Field: cf.Name, Tag: errTag[1:], ActualTag: errTag[1:], Value: current.Interface(), Type: typ, Kind: kind, } } return } ct = ct.next } default: if !ct.fn(v, topStruct, currentStruct, current, typ, kind, ct.param) { ns := errPrefix + cf.Name errs[ns] = &FieldError{ FieldNamespace: ns, NameNamespace: nsPrefix + cf.AltName, Name: cf.AltName, Field: cf.Name, Tag: ct.aliasTag, ActualTag: ct.tag, Value: current.Interface(), Param: ct.param, Type: typ, Kind: kind, } return } ct = ct.next } } } validator-8.18.1/validator_test.go000066400000000000000000004260201274315620500171620ustar00rootroot00000000000000package validator import ( "database/sql" "database/sql/driver" "encoding/json" "fmt" "reflect" "testing" "time" . "gopkg.in/go-playground/assert.v1" ) // NOTES: // - Run "go test" to run tests // - Run "gocov test | gocov report" to report on test converage by file // - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called // // or // // -- may be a good idea to change to output path to somewherelike /tmp // go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html // // // go test -cpuprofile cpu.out // ./validator.test -test.bench=. -test.cpuprofile=cpu.prof // go tool pprof validator.test cpu.prof // // // go test -memprofile mem.out type I interface { Foo() string } type Impl struct { F string `validate:"len=3"` } func (i *Impl) Foo() string { return i.F } type SubTest struct { Test string `validate:"required"` } type TestInterface struct { Iface I } type TestString struct { BlankTag string `validate:""` Required string `validate:"required"` Len string `validate:"len=10"` Min string `validate:"min=1"` Max string `validate:"max=10"` MinMax string `validate:"min=1,max=10"` Lt string `validate:"lt=10"` Lte string `validate:"lte=10"` Gt string `validate:"gt=10"` Gte string `validate:"gte=10"` OmitEmpty string `validate:"omitempty,min=1,max=10"` Sub *SubTest SubIgnore *SubTest `validate:"-"` Anonymous struct { A string `validate:"required"` } Iface I } type TestInt32 struct { Required int `validate:"required"` Len int `validate:"len=10"` Min int `validate:"min=1"` Max int `validate:"max=10"` MinMax int `validate:"min=1,max=10"` Lt int `validate:"lt=10"` Lte int `validate:"lte=10"` Gt int `validate:"gt=10"` Gte int `validate:"gte=10"` OmitEmpty int `validate:"omitempty,min=1,max=10"` } type TestUint64 struct { Required uint64 `validate:"required"` Len uint64 `validate:"len=10"` Min uint64 `validate:"min=1"` Max uint64 `validate:"max=10"` MinMax uint64 `validate:"min=1,max=10"` OmitEmpty uint64 `validate:"omitempty,min=1,max=10"` } type TestFloat64 struct { Required float64 `validate:"required"` Len float64 `validate:"len=10"` Min float64 `validate:"min=1"` Max float64 `validate:"max=10"` MinMax float64 `validate:"min=1,max=10"` Lte float64 `validate:"lte=10"` OmitEmpty float64 `validate:"omitempty,min=1,max=10"` } type TestSlice struct { Required []int `validate:"required"` Len []int `validate:"len=10"` Min []int `validate:"min=1"` Max []int `validate:"max=10"` MinMax []int `validate:"min=1,max=10"` OmitEmpty []int `validate:"omitempty,min=1,max=10"` } var validate = New(&Config{TagName: "validate"}) func AssertError(t *testing.T, err error, key, field, expectedTag string) { errs := err.(ValidationErrors) val, ok := errs[key] EqualSkip(t, 2, ok, true) NotEqualSkip(t, 2, val, nil) EqualSkip(t, 2, val.Field, field) EqualSkip(t, 2, val.Tag, expectedTag) } type valuer struct { Name string } func (v valuer) Value() (driver.Value, error) { if v.Name == "errorme" { panic("SQL Driver Valuer error: some kind of error") // return nil, errors.New("some kind of error") } if len(v.Name) == 0 { return nil, nil } return v.Name, nil } type MadeUpCustomType struct { FirstName string LastName string } func ValidateCustomType(field reflect.Value) interface{} { if cust, ok := field.Interface().(MadeUpCustomType); ok { if len(cust.FirstName) == 0 || len(cust.LastName) == 0 { return "" } return cust.FirstName + " " + cust.LastName } return "" } func OverrideIntTypeForSomeReason(field reflect.Value) interface{} { if i, ok := field.Interface().(int); ok { if i == 1 { return "1" } if i == 2 { return "12" } } return "" } type CustomMadeUpStruct struct { MadeUp MadeUpCustomType `validate:"required"` OverriddenInt int `validate:"gt=1"` } func ValidateValuerType(field reflect.Value) interface{} { if valuer, ok := field.Interface().(driver.Valuer); ok { val, err := valuer.Value() if err != nil { // handle the error how you want return nil } return val } return nil } type TestPartial struct { NoTag string BlankTag string `validate:""` Required string `validate:"required"` SubSlice []*SubTest `validate:"required,dive"` Sub *SubTest SubIgnore *SubTest `validate:"-"` Anonymous struct { A string `validate:"required"` ASubSlice []*SubTest `validate:"required,dive"` SubAnonStruct []struct { Test string `validate:"required"` OtherTest string `validate:"required"` } `validate:"required,dive"` } } type TestStruct struct { String string `validate:"required" json:"StringVal"` } func StructValidationTestStructSuccess(v *Validate, structLevel *StructLevel) { st := structLevel.CurrentStruct.Interface().(TestStruct) if st.String != "good value" { structLevel.ReportError(reflect.ValueOf(st.String), "String", "StringVal", "badvalueteststruct") } } func StructValidationTestStruct(v *Validate, structLevel *StructLevel) { st := structLevel.CurrentStruct.Interface().(TestStruct) if st.String != "bad value" { structLevel.ReportError(reflect.ValueOf(st.String), "String", "StringVal", "badvalueteststruct") } } func StructValidationBadTestStructFieldName(v *Validate, structLevel *StructLevel) { st := structLevel.CurrentStruct.Interface().(TestStruct) if st.String != "bad value" { structLevel.ReportError(reflect.ValueOf(st.String), "", "StringVal", "badvalueteststruct") } } func StructValidationBadTestStructTag(v *Validate, structLevel *StructLevel) { st := structLevel.CurrentStruct.Interface().(TestStruct) if st.String != "bad value" { structLevel.ReportError(reflect.ValueOf(st.String), "String", "StringVal", "") } } func StructValidationNoTestStructCustomName(v *Validate, structLevel *StructLevel) { st := structLevel.CurrentStruct.Interface().(TestStruct) if st.String != "bad value" { structLevel.ReportError(reflect.ValueOf(st.String), "String", "", "badvalueteststruct") } } func StructValidationTestStructInvalid(v *Validate, structLevel *StructLevel) { st := structLevel.CurrentStruct.Interface().(TestStruct) if st.String != "bad value" { structLevel.ReportError(reflect.ValueOf(nil), "String", "StringVal", "badvalueteststruct") } } func StructValidationTestStructReturnValidationErrors(v *Validate, structLevel *StructLevel) { s := structLevel.CurrentStruct.Interface().(TestStructReturnValidationErrors) errs := v.Struct(s.Inner1.Inner2) if errs == nil { return } structLevel.ReportValidationErrors("Inner1.", errs.(ValidationErrors)) } func StructValidationTestStructReturnValidationErrors2(v *Validate, structLevel *StructLevel) { s := structLevel.CurrentStruct.Interface().(TestStructReturnValidationErrors) errs := v.Struct(s.Inner1.Inner2) if errs == nil { return } structLevel.ReportValidationErrors("Inner1.|Inner1JSON.", errs.(ValidationErrors)) } type TestStructReturnValidationErrorsInner2 struct { String string `validate:"required" json:"JSONString"` } type TestStructReturnValidationErrorsInner1 struct { Inner2 *TestStructReturnValidationErrorsInner2 } type TestStructReturnValidationErrors struct { Inner1 *TestStructReturnValidationErrorsInner1 `json:"Inner1JSON"` } type Inner2Namespace struct { String []string `validate:"dive,required" json:"JSONString"` } type Inner1Namespace struct { Inner2 *Inner2Namespace `json:"Inner2JSON"` } type Namespace struct { Inner1 *Inner1Namespace `json:"Inner1JSON"` } func TestNameNamespace(t *testing.T) { config := &Config{ TagName: "validate", FieldNameTag: "json", } v1 := New(config) i2 := &Inner2Namespace{String: []string{"ok", "ok", "ok"}} i1 := &Inner1Namespace{Inner2: i2} ns := &Namespace{Inner1: i1} errs := v1.Struct(ns) Equal(t, errs, nil) i2.String[1] = "" errs = v1.Struct(ns) NotEqual(t, errs, nil) ve := errs.(ValidationErrors) Equal(t, len(ve), 1) AssertError(t, errs, "Namespace.Inner1.Inner2.String[1]", "String[1]", "required") fe, ok := ve["Namespace.Inner1.Inner2.String[1]"] Equal(t, ok, true) Equal(t, fe.Field, "String[1]") Equal(t, fe.FieldNamespace, "Namespace.Inner1.Inner2.String[1]") Equal(t, fe.Name, "JSONString[1]") Equal(t, fe.NameNamespace, "Namespace.Inner1JSON.Inner2JSON.JSONString[1]") } func TestAnonymous(t *testing.T) { v2 := New(&Config{TagName: "validate", FieldNameTag: "json"}) type Test struct { Anonymous struct { A string `validate:"required" json:"EH"` } AnonymousB struct { B string `validate:"required" json:"BEE"` } anonymousC struct { c string `validate:"required"` } } tst := &Test{ Anonymous: struct { A string `validate:"required" json:"EH"` }{ A: "1", }, AnonymousB: struct { B string `validate:"required" json:"BEE"` }{ B: "", }, anonymousC: struct { c string `validate:"required"` }{ c: "", }, } err := v2.Struct(tst) NotEqual(t, err, nil) errs := err.(ValidationErrors) Equal(t, len(errs), 1) AssertError(t, errs, "Test.AnonymousB.B", "B", "required") Equal(t, errs["Test.AnonymousB.B"].Field, "B") Equal(t, errs["Test.AnonymousB.B"].Name, "BEE") s := struct { c string `validate:"required"` }{ c: "", } err = v2.Struct(s) Equal(t, err, nil) } func TestAnonymousSameStructDifferentTags(t *testing.T) { v2 := New(&Config{TagName: "validate", FieldNameTag: "json"}) type Test struct { A interface{} } tst := &Test{ A: struct { A string `validate:"required"` }{ A: "", }, } err := v2.Struct(tst) NotEqual(t, err, nil) errs := err.(ValidationErrors) Equal(t, len(errs), 1) AssertError(t, errs, "Test.A.A", "A", "required") tst = &Test{ A: struct { A string `validate:"omitempty,required"` }{ A: "", }, } err = v2.Struct(tst) Equal(t, err, nil) } func TestStructLevelReturnValidationErrors(t *testing.T) { config := &Config{ TagName: "validate", } v1 := New(config) v1.RegisterStructValidation(StructValidationTestStructReturnValidationErrors, TestStructReturnValidationErrors{}) inner2 := &TestStructReturnValidationErrorsInner2{ String: "I'm HERE", } inner1 := &TestStructReturnValidationErrorsInner1{ Inner2: inner2, } val := &TestStructReturnValidationErrors{ Inner1: inner1, } errs := v1.Struct(val) Equal(t, errs, nil) inner2.String = "" errs = v1.Struct(val) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 2) AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.Inner2.String", "String", "required") // this is an extra error reported from struct validation AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.String", "String", "required") } func TestStructLevelReturnValidationErrorsWithJSON(t *testing.T) { config := &Config{ TagName: "validate", FieldNameTag: "json", } v1 := New(config) v1.RegisterStructValidation(StructValidationTestStructReturnValidationErrors2, TestStructReturnValidationErrors{}) inner2 := &TestStructReturnValidationErrorsInner2{ String: "I'm HERE", } inner1 := &TestStructReturnValidationErrorsInner1{ Inner2: inner2, } val := &TestStructReturnValidationErrors{ Inner1: inner1, } errs := v1.Struct(val) Equal(t, errs, nil) inner2.String = "" errs = v1.Struct(val) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 2) AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.Inner2.String", "String", "required") // this is an extra error reported from struct validation, it's a badly formatted one, but on purpose AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.String", "String", "required") fe, ok := errs.(ValidationErrors)["TestStructReturnValidationErrors.Inner1.Inner2.String"] Equal(t, ok, true) // check for proper JSON namespace Equal(t, fe.Field, "String") Equal(t, fe.Name, "JSONString") Equal(t, fe.FieldNamespace, "TestStructReturnValidationErrors.Inner1.Inner2.String") Equal(t, fe.NameNamespace, "TestStructReturnValidationErrors.Inner1JSON.Inner2.JSONString") fe, ok = errs.(ValidationErrors)["TestStructReturnValidationErrors.Inner1.String"] Equal(t, ok, true) // check for proper JSON namespace Equal(t, fe.Field, "String") Equal(t, fe.Name, "JSONString") Equal(t, fe.FieldNamespace, "TestStructReturnValidationErrors.Inner1.String") Equal(t, fe.NameNamespace, "TestStructReturnValidationErrors.Inner1JSON.JSONString") } func TestStructLevelValidations(t *testing.T) { config := &Config{ TagName: "validate", } v1 := New(config) v1.RegisterStructValidation(StructValidationTestStruct, TestStruct{}) tst := &TestStruct{ String: "good value", } errs := v1.Struct(tst) NotEqual(t, errs, nil) AssertError(t, errs, "TestStruct.String", "String", "badvalueteststruct") v2 := New(config) v2.RegisterStructValidation(StructValidationBadTestStructFieldName, TestStruct{}) PanicMatches(t, func() { v2.Struct(tst) }, fieldNameRequired) v3 := New(config) v3.RegisterStructValidation(StructValidationBadTestStructTag, TestStruct{}) PanicMatches(t, func() { v3.Struct(tst) }, tagRequired) v4 := New(config) v4.RegisterStructValidation(StructValidationNoTestStructCustomName, TestStruct{}) errs = v4.Struct(tst) NotEqual(t, errs, nil) AssertError(t, errs, "TestStruct.String", "String", "badvalueteststruct") v5 := New(config) v5.RegisterStructValidation(StructValidationTestStructInvalid, TestStruct{}) errs = v5.Struct(tst) NotEqual(t, errs, nil) AssertError(t, errs, "TestStruct.String", "String", "badvalueteststruct") v6 := New(config) v6.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) errs = v6.Struct(tst) Equal(t, errs, nil) } func TestAliasTags(t *testing.T) { validate.RegisterAliasValidation("iscolor", "hexcolor|rgb|rgba|hsl|hsla") s := "rgb(255,255,255)" errs := validate.Field(s, "iscolor") Equal(t, errs, nil) s = "" errs = validate.Field(s, "omitempty,iscolor") Equal(t, errs, nil) s = "rgb(255,255,0)" errs = validate.Field(s, "iscolor,len=5") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "len") type Test struct { Color string `validate:"iscolor"` } tst := &Test{ Color: "#000", } errs = validate.Struct(tst) Equal(t, errs, nil) tst.Color = "cfvre" errs = validate.Struct(tst) NotEqual(t, errs, nil) AssertError(t, errs, "Test.Color", "Color", "iscolor") Equal(t, errs.(ValidationErrors)["Test.Color"].ActualTag, "hexcolor|rgb|rgba|hsl|hsla") validate.RegisterAliasValidation("req", "required,dive,iscolor") arr := []string{"val1", "#fff", "#000"} errs = validate.Field(arr, "req") NotEqual(t, errs, nil) AssertError(t, errs, "[0]", "[0]", "iscolor") PanicMatches(t, func() { validate.RegisterAliasValidation("exists", "gt=5,lt=10") }, "Alias 'exists' either contains restricted characters or is the same as a restricted tag needed for normal operation") } func TestNilValidator(t *testing.T) { type TestStruct struct { Test string `validate:"required"` } ts := TestStruct{} var val *Validate fn := func(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return current.String() == field.String() } PanicMatches(t, func() { val.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) }, validatorNotInitialized) PanicMatches(t, func() { val.RegisterValidation("something", fn) }, validatorNotInitialized) PanicMatches(t, func() { val.Field(ts.Test, "required") }, validatorNotInitialized) PanicMatches(t, func() { val.FieldWithValue("test", ts.Test, "required") }, validatorNotInitialized) PanicMatches(t, func() { val.Struct(ts) }, validatorNotInitialized) PanicMatches(t, func() { val.StructExcept(ts, "Test") }, validatorNotInitialized) PanicMatches(t, func() { val.StructPartial(ts, "Test") }, validatorNotInitialized) } func TestStructPartial(t *testing.T) { p1 := []string{ "NoTag", "Required", } p2 := []string{ "SubSlice[0].Test", "Sub", "SubIgnore", "Anonymous.A", } p3 := []string{ "SubTest.Test", } p4 := []string{ "A", } tPartial := &TestPartial{ NoTag: "NoTag", Required: "Required", SubSlice: []*SubTest{ { Test: "Required", }, { Test: "Required", }, }, Sub: &SubTest{ Test: "1", }, SubIgnore: &SubTest{ Test: "", }, Anonymous: struct { A string `validate:"required"` ASubSlice []*SubTest `validate:"required,dive"` SubAnonStruct []struct { Test string `validate:"required"` OtherTest string `validate:"required"` } `validate:"required,dive"` }{ A: "1", ASubSlice: []*SubTest{ { Test: "Required", }, { Test: "Required", }, }, SubAnonStruct: []struct { Test string `validate:"required"` OtherTest string `validate:"required"` }{ {"Required", "RequiredOther"}, {"Required", "RequiredOther"}, }, }, } // the following should all return no errors as everything is valid in // the default state errs := validate.StructPartial(tPartial, p1...) Equal(t, errs, nil) errs = validate.StructPartial(tPartial, p2...) Equal(t, errs, nil) // this isn't really a robust test, but is ment to illustrate the ANON CASE below errs = validate.StructPartial(tPartial.SubSlice[0], p3...) Equal(t, errs, nil) errs = validate.StructExcept(tPartial, p1...) Equal(t, errs, nil) errs = validate.StructExcept(tPartial, p2...) Equal(t, errs, nil) // mod tParial for required feild and re-test making sure invalid fields are NOT required: tPartial.Required = "" errs = validate.StructExcept(tPartial, p1...) Equal(t, errs, nil) errs = validate.StructPartial(tPartial, p2...) Equal(t, errs, nil) // inversion and retesting Partial to generate failures: errs = validate.StructPartial(tPartial, p1...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.Required", "Required", "required") errs = validate.StructExcept(tPartial, p2...) AssertError(t, errs, "TestPartial.Required", "Required", "required") // reset Required field, and set nested struct tPartial.Required = "Required" tPartial.Anonymous.A = "" // will pass as unset feilds is not going to be tested errs = validate.StructPartial(tPartial, p1...) Equal(t, errs, nil) errs = validate.StructExcept(tPartial, p2...) Equal(t, errs, nil) // ANON CASE the response here is strange, it clearly does what it is being told to errs = validate.StructExcept(tPartial.Anonymous, p4...) Equal(t, errs, nil) // will fail as unset feild is tested errs = validate.StructPartial(tPartial, p2...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.Anonymous.A", "A", "required") errs = validate.StructExcept(tPartial, p1...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.Anonymous.A", "A", "required") // reset nested struct and unset struct in slice tPartial.Anonymous.A = "Required" tPartial.SubSlice[0].Test = "" // these will pass as unset item is NOT tested errs = validate.StructPartial(tPartial, p1...) Equal(t, errs, nil) errs = validate.StructExcept(tPartial, p2...) Equal(t, errs, nil) // these will fail as unset item IS tested errs = validate.StructExcept(tPartial, p1...) AssertError(t, errs, "TestPartial.SubSlice[0].Test", "Test", "required") Equal(t, len(errs.(ValidationErrors)), 1) errs = validate.StructPartial(tPartial, p2...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.SubSlice[0].Test", "Test", "required") Equal(t, len(errs.(ValidationErrors)), 1) // Unset second slice member concurrently to test dive behavior: tPartial.SubSlice[1].Test = "" errs = validate.StructPartial(tPartial, p1...) Equal(t, errs, nil) // NOTE: When specifying nested items, it is still the users responsibility // to specify the dive tag, the library does not override this. errs = validate.StructExcept(tPartial, p2...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.SubSlice[1].Test", "Test", "required") errs = validate.StructExcept(tPartial, p1...) Equal(t, len(errs.(ValidationErrors)), 2) AssertError(t, errs, "TestPartial.SubSlice[0].Test", "Test", "required") AssertError(t, errs, "TestPartial.SubSlice[1].Test", "Test", "required") errs = validate.StructPartial(tPartial, p2...) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "TestPartial.SubSlice[0].Test", "Test", "required") // reset struct in slice, and unset struct in slice in unset posistion tPartial.SubSlice[0].Test = "Required" // these will pass as the unset item is NOT tested errs = validate.StructPartial(tPartial, p1...) Equal(t, errs, nil) errs = validate.StructPartial(tPartial, p2...) Equal(t, errs, nil) // testing for missing item by exception, yes it dives and fails errs = validate.StructExcept(tPartial, p1...) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "TestPartial.SubSlice[1].Test", "Test", "required") errs = validate.StructExcept(tPartial, p2...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.SubSlice[1].Test", "Test", "required") tPartial.SubSlice[1].Test = "Required" tPartial.Anonymous.SubAnonStruct[0].Test = "" // these will pass as the unset item is NOT tested errs = validate.StructPartial(tPartial, p1...) Equal(t, errs, nil) errs = validate.StructPartial(tPartial, p2...) Equal(t, errs, nil) errs = validate.StructExcept(tPartial, p1...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.Anonymous.SubAnonStruct[0].Test", "Test", "required") errs = validate.StructExcept(tPartial, p2...) NotEqual(t, errs, nil) AssertError(t, errs, "TestPartial.Anonymous.SubAnonStruct[0].Test", "Test", "required") } func TestCrossStructLteFieldValidation(t *testing.T) { type Inner struct { CreatedAt *time.Time String string Int int Uint uint Float float64 Array []string } type Test struct { Inner *Inner CreatedAt *time.Time `validate:"ltecsfield=Inner.CreatedAt"` String string `validate:"ltecsfield=Inner.String"` Int int `validate:"ltecsfield=Inner.Int"` Uint uint `validate:"ltecsfield=Inner.Uint"` Float float64 `validate:"ltecsfield=Inner.Float"` Array []string `validate:"ltecsfield=Inner.Array"` } now := time.Now().UTC() then := now.Add(time.Hour * 5) inner := &Inner{ CreatedAt: &then, String: "abcd", Int: 13, Uint: 13, Float: 1.13, Array: []string{"val1", "val2"}, } test := &Test{ Inner: inner, CreatedAt: &now, String: "abc", Int: 12, Uint: 12, Float: 1.12, Array: []string{"val1"}, } errs := validate.Struct(test) Equal(t, errs, nil) test.CreatedAt = &then test.String = "abcd" test.Int = 13 test.Uint = 13 test.Float = 1.13 test.Array = []string{"val1", "val2"} errs = validate.Struct(test) Equal(t, errs, nil) after := now.Add(time.Hour * 10) test.CreatedAt = &after test.String = "abce" test.Int = 14 test.Uint = 14 test.Float = 1.14 test.Array = []string{"val1", "val2", "val3"} errs = validate.Struct(test) NotEqual(t, errs, nil) AssertError(t, errs, "Test.CreatedAt", "CreatedAt", "ltecsfield") AssertError(t, errs, "Test.String", "String", "ltecsfield") AssertError(t, errs, "Test.Int", "Int", "ltecsfield") AssertError(t, errs, "Test.Uint", "Uint", "ltecsfield") AssertError(t, errs, "Test.Float", "Float", "ltecsfield") AssertError(t, errs, "Test.Array", "Array", "ltecsfield") errs = validate.FieldWithValue(1, "", "ltecsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltecsfield") errs = validate.FieldWithValue(test, now, "ltecsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltecsfield") } func TestCrossStructLtFieldValidation(t *testing.T) { type Inner struct { CreatedAt *time.Time String string Int int Uint uint Float float64 Array []string } type Test struct { Inner *Inner CreatedAt *time.Time `validate:"ltcsfield=Inner.CreatedAt"` String string `validate:"ltcsfield=Inner.String"` Int int `validate:"ltcsfield=Inner.Int"` Uint uint `validate:"ltcsfield=Inner.Uint"` Float float64 `validate:"ltcsfield=Inner.Float"` Array []string `validate:"ltcsfield=Inner.Array"` } now := time.Now().UTC() then := now.Add(time.Hour * 5) inner := &Inner{ CreatedAt: &then, String: "abcd", Int: 13, Uint: 13, Float: 1.13, Array: []string{"val1", "val2"}, } test := &Test{ Inner: inner, CreatedAt: &now, String: "abc", Int: 12, Uint: 12, Float: 1.12, Array: []string{"val1"}, } errs := validate.Struct(test) Equal(t, errs, nil) test.CreatedAt = &then test.String = "abcd" test.Int = 13 test.Uint = 13 test.Float = 1.13 test.Array = []string{"val1", "val2"} errs = validate.Struct(test) NotEqual(t, errs, nil) AssertError(t, errs, "Test.CreatedAt", "CreatedAt", "ltcsfield") AssertError(t, errs, "Test.String", "String", "ltcsfield") AssertError(t, errs, "Test.Int", "Int", "ltcsfield") AssertError(t, errs, "Test.Uint", "Uint", "ltcsfield") AssertError(t, errs, "Test.Float", "Float", "ltcsfield") AssertError(t, errs, "Test.Array", "Array", "ltcsfield") errs = validate.FieldWithValue(1, "", "ltcsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltcsfield") errs = validate.FieldWithValue(test, now, "ltcsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltcsfield") } func TestCrossStructGteFieldValidation(t *testing.T) { type Inner struct { CreatedAt *time.Time String string Int int Uint uint Float float64 Array []string } type Test struct { Inner *Inner CreatedAt *time.Time `validate:"gtecsfield=Inner.CreatedAt"` String string `validate:"gtecsfield=Inner.String"` Int int `validate:"gtecsfield=Inner.Int"` Uint uint `validate:"gtecsfield=Inner.Uint"` Float float64 `validate:"gtecsfield=Inner.Float"` Array []string `validate:"gtecsfield=Inner.Array"` } now := time.Now().UTC() then := now.Add(time.Hour * -5) inner := &Inner{ CreatedAt: &then, String: "abcd", Int: 13, Uint: 13, Float: 1.13, Array: []string{"val1", "val2"}, } test := &Test{ Inner: inner, CreatedAt: &now, String: "abcde", Int: 14, Uint: 14, Float: 1.14, Array: []string{"val1", "val2", "val3"}, } errs := validate.Struct(test) Equal(t, errs, nil) test.CreatedAt = &then test.String = "abcd" test.Int = 13 test.Uint = 13 test.Float = 1.13 test.Array = []string{"val1", "val2"} errs = validate.Struct(test) Equal(t, errs, nil) before := now.Add(time.Hour * -10) test.CreatedAt = &before test.String = "abc" test.Int = 12 test.Uint = 12 test.Float = 1.12 test.Array = []string{"val1"} errs = validate.Struct(test) NotEqual(t, errs, nil) AssertError(t, errs, "Test.CreatedAt", "CreatedAt", "gtecsfield") AssertError(t, errs, "Test.String", "String", "gtecsfield") AssertError(t, errs, "Test.Int", "Int", "gtecsfield") AssertError(t, errs, "Test.Uint", "Uint", "gtecsfield") AssertError(t, errs, "Test.Float", "Float", "gtecsfield") AssertError(t, errs, "Test.Array", "Array", "gtecsfield") errs = validate.FieldWithValue(1, "", "gtecsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtecsfield") errs = validate.FieldWithValue(test, now, "gtecsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtecsfield") } func TestCrossStructGtFieldValidation(t *testing.T) { type Inner struct { CreatedAt *time.Time String string Int int Uint uint Float float64 Array []string } type Test struct { Inner *Inner CreatedAt *time.Time `validate:"gtcsfield=Inner.CreatedAt"` String string `validate:"gtcsfield=Inner.String"` Int int `validate:"gtcsfield=Inner.Int"` Uint uint `validate:"gtcsfield=Inner.Uint"` Float float64 `validate:"gtcsfield=Inner.Float"` Array []string `validate:"gtcsfield=Inner.Array"` } now := time.Now().UTC() then := now.Add(time.Hour * -5) inner := &Inner{ CreatedAt: &then, String: "abcd", Int: 13, Uint: 13, Float: 1.13, Array: []string{"val1", "val2"}, } test := &Test{ Inner: inner, CreatedAt: &now, String: "abcde", Int: 14, Uint: 14, Float: 1.14, Array: []string{"val1", "val2", "val3"}, } errs := validate.Struct(test) Equal(t, errs, nil) test.CreatedAt = &then test.String = "abcd" test.Int = 13 test.Uint = 13 test.Float = 1.13 test.Array = []string{"val1", "val2"} errs = validate.Struct(test) NotEqual(t, errs, nil) AssertError(t, errs, "Test.CreatedAt", "CreatedAt", "gtcsfield") AssertError(t, errs, "Test.String", "String", "gtcsfield") AssertError(t, errs, "Test.Int", "Int", "gtcsfield") AssertError(t, errs, "Test.Uint", "Uint", "gtcsfield") AssertError(t, errs, "Test.Float", "Float", "gtcsfield") AssertError(t, errs, "Test.Array", "Array", "gtcsfield") errs = validate.FieldWithValue(1, "", "gtcsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtcsfield") errs = validate.FieldWithValue(test, now, "gtcsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtcsfield") } func TestCrossStructNeFieldValidation(t *testing.T) { type Inner struct { CreatedAt *time.Time } type Test struct { Inner *Inner CreatedAt *time.Time `validate:"necsfield=Inner.CreatedAt"` } now := time.Now().UTC() then := now.Add(time.Hour * 5) inner := &Inner{ CreatedAt: &then, } test := &Test{ Inner: inner, CreatedAt: &now, } errs := validate.Struct(test) Equal(t, errs, nil) test.CreatedAt = &then errs = validate.Struct(test) NotEqual(t, errs, nil) AssertError(t, errs, "Test.CreatedAt", "CreatedAt", "necsfield") var j uint64 var k float64 var j2 uint64 var k2 float64 s := "abcd" i := 1 j = 1 k = 1.543 arr := []string{"test"} s2 := "abcd" i2 := 1 j2 = 1 k2 = 1.543 arr2 := []string{"test"} arr3 := []string{"test", "test2"} now2 := now errs = validate.FieldWithValue(s, s2, "necsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "necsfield") errs = validate.FieldWithValue(i2, i, "necsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "necsfield") errs = validate.FieldWithValue(j2, j, "necsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "necsfield") errs = validate.FieldWithValue(k2, k, "necsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "necsfield") errs = validate.FieldWithValue(arr2, arr, "necsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "necsfield") errs = validate.FieldWithValue(now2, now, "necsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "necsfield") errs = validate.FieldWithValue(arr3, arr, "necsfield") Equal(t, errs, nil) type SInner struct { Name string } type TStruct struct { Inner *SInner CreatedAt *time.Time `validate:"necsfield=Inner"` } sinner := &SInner{ Name: "NAME", } test2 := &TStruct{ Inner: sinner, CreatedAt: &now, } errs = validate.Struct(test2) Equal(t, errs, nil) test2.Inner = nil errs = validate.Struct(test2) Equal(t, errs, nil) errs = validate.FieldWithValue(nil, 1, "necsfield") Equal(t, errs, nil) } func TestCrossStructEqFieldValidation(t *testing.T) { type Inner struct { CreatedAt *time.Time } type Test struct { Inner *Inner CreatedAt *time.Time `validate:"eqcsfield=Inner.CreatedAt"` } now := time.Now().UTC() inner := &Inner{ CreatedAt: &now, } test := &Test{ Inner: inner, CreatedAt: &now, } errs := validate.Struct(test) Equal(t, errs, nil) newTime := time.Now().UTC() test.CreatedAt = &newTime errs = validate.Struct(test) NotEqual(t, errs, nil) AssertError(t, errs, "Test.CreatedAt", "CreatedAt", "eqcsfield") var j uint64 var k float64 s := "abcd" i := 1 j = 1 k = 1.543 arr := []string{"test"} var j2 uint64 var k2 float64 s2 := "abcd" i2 := 1 j2 = 1 k2 = 1.543 arr2 := []string{"test"} arr3 := []string{"test", "test2"} now2 := now errs = validate.FieldWithValue(s, s2, "eqcsfield") Equal(t, errs, nil) errs = validate.FieldWithValue(i2, i, "eqcsfield") Equal(t, errs, nil) errs = validate.FieldWithValue(j2, j, "eqcsfield") Equal(t, errs, nil) errs = validate.FieldWithValue(k2, k, "eqcsfield") Equal(t, errs, nil) errs = validate.FieldWithValue(arr2, arr, "eqcsfield") Equal(t, errs, nil) errs = validate.FieldWithValue(now2, now, "eqcsfield") Equal(t, errs, nil) errs = validate.FieldWithValue(arr3, arr, "eqcsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "eqcsfield") type SInner struct { Name string } type TStruct struct { Inner *SInner CreatedAt *time.Time `validate:"eqcsfield=Inner"` } sinner := &SInner{ Name: "NAME", } test2 := &TStruct{ Inner: sinner, CreatedAt: &now, } errs = validate.Struct(test2) NotEqual(t, errs, nil) AssertError(t, errs, "TStruct.CreatedAt", "CreatedAt", "eqcsfield") test2.Inner = nil errs = validate.Struct(test2) NotEqual(t, errs, nil) AssertError(t, errs, "TStruct.CreatedAt", "CreatedAt", "eqcsfield") errs = validate.FieldWithValue(nil, 1, "eqcsfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "eqcsfield") } func TestCrossNamespaceFieldValidation(t *testing.T) { type SliceStruct struct { Name string } type MapStruct struct { Name string } type Inner struct { CreatedAt *time.Time Slice []string SliceStructs []*SliceStruct SliceSlice [][]string SliceSliceStruct [][]*SliceStruct SliceMap []map[string]string Map map[string]string MapMap map[string]map[string]string MapStructs map[string]*SliceStruct MapMapStruct map[string]map[string]*SliceStruct MapSlice map[string][]string MapInt map[int]string MapInt8 map[int8]string MapInt16 map[int16]string MapInt32 map[int32]string MapInt64 map[int64]string MapUint map[uint]string MapUint8 map[uint8]string MapUint16 map[uint16]string MapUint32 map[uint32]string MapUint64 map[uint64]string MapFloat32 map[float32]string MapFloat64 map[float64]string MapBool map[bool]string } type Test struct { Inner *Inner CreatedAt *time.Time } now := time.Now() inner := &Inner{ CreatedAt: &now, Slice: []string{"val1", "val2", "val3"}, SliceStructs: []*SliceStruct{{Name: "name1"}, {Name: "name2"}, {Name: "name3"}}, SliceSlice: [][]string{{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}}, SliceSliceStruct: [][]*SliceStruct{{{Name: "name1"}, {Name: "name2"}, {Name: "name3"}}, {{Name: "name4"}, {Name: "name5"}, {Name: "name6"}}, {{Name: "name7"}, {Name: "name8"}, {Name: "name9"}}}, SliceMap: []map[string]string{{"key1": "val1", "key2": "val2", "key3": "val3"}, {"key4": "val4", "key5": "val5", "key6": "val6"}}, Map: map[string]string{"key1": "val1", "key2": "val2", "key3": "val3"}, MapStructs: map[string]*SliceStruct{"key1": {Name: "name1"}, "key2": {Name: "name2"}, "key3": {Name: "name3"}}, MapMap: map[string]map[string]string{"key1": {"key1-1": "val1"}, "key2": {"key2-1": "val2"}, "key3": {"key3-1": "val3"}}, MapMapStruct: map[string]map[string]*SliceStruct{"key1": {"key1-1": {Name: "name1"}}, "key2": {"key2-1": {Name: "name2"}}, "key3": {"key3-1": {Name: "name3"}}}, MapSlice: map[string][]string{"key1": {"1", "2", "3"}, "key2": {"4", "5", "6"}, "key3": {"7", "8", "9"}}, MapInt: map[int]string{1: "val1", 2: "val2", 3: "val3"}, MapInt8: map[int8]string{1: "val1", 2: "val2", 3: "val3"}, MapInt16: map[int16]string{1: "val1", 2: "val2", 3: "val3"}, MapInt32: map[int32]string{1: "val1", 2: "val2", 3: "val3"}, MapInt64: map[int64]string{1: "val1", 2: "val2", 3: "val3"}, MapUint: map[uint]string{1: "val1", 2: "val2", 3: "val3"}, MapUint8: map[uint8]string{1: "val1", 2: "val2", 3: "val3"}, MapUint16: map[uint16]string{1: "val1", 2: "val2", 3: "val3"}, MapUint32: map[uint32]string{1: "val1", 2: "val2", 3: "val3"}, MapUint64: map[uint64]string{1: "val1", 2: "val2", 3: "val3"}, MapFloat32: map[float32]string{1.01: "val1", 2.02: "val2", 3.03: "val3"}, MapFloat64: map[float64]string{1.01: "val1", 2.02: "val2", 3.03: "val3"}, MapBool: map[bool]string{true: "val1", false: "val2"}, } test := &Test{ Inner: inner, CreatedAt: &now, } val := reflect.ValueOf(test) current, kind, ok := validate.GetStructFieldOK(val, "Inner.CreatedAt") Equal(t, ok, true) Equal(t, kind, reflect.Struct) tm, ok := current.Interface().(time.Time) Equal(t, ok, true) Equal(t, tm, now) current, kind, ok = validate.GetStructFieldOK(val, "Inner.Slice[1]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.CrazyNonExistantField") Equal(t, ok, false) current, kind, ok = validate.GetStructFieldOK(val, "Inner.Slice[101]") Equal(t, ok, false) current, kind, ok = validate.GetStructFieldOK(val, "Inner.Map[key3]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val3") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapMap[key2][key2-1]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapStructs[key2].Name") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "name2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapMapStruct[key3][key3-1].Name") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "name3") current, kind, ok = validate.GetStructFieldOK(val, "Inner.SliceSlice[2][0]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "7") current, kind, ok = validate.GetStructFieldOK(val, "Inner.SliceSliceStruct[2][1].Name") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "name8") current, kind, ok = validate.GetStructFieldOK(val, "Inner.SliceMap[1][key5]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val5") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapSlice[key3][2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "9") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapInt[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapInt8[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapInt16[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapInt32[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapInt64[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapUint[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapUint8[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapUint16[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapUint32[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapUint64[2]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapFloat32[3.03]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val3") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapFloat64[2.02]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val2") current, kind, ok = validate.GetStructFieldOK(val, "Inner.MapBool[true]") Equal(t, ok, true) Equal(t, kind, reflect.String) Equal(t, current.String(), "val1") inner = &Inner{ CreatedAt: &now, Slice: []string{"val1", "val2", "val3"}, SliceStructs: []*SliceStruct{{Name: "name1"}, {Name: "name2"}, nil}, SliceSlice: [][]string{{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}}, SliceSliceStruct: [][]*SliceStruct{{{Name: "name1"}, {Name: "name2"}, {Name: "name3"}}, {{Name: "name4"}, {Name: "name5"}, {Name: "name6"}}, {{Name: "name7"}, {Name: "name8"}, {Name: "name9"}}}, SliceMap: []map[string]string{{"key1": "val1", "key2": "val2", "key3": "val3"}, {"key4": "val4", "key5": "val5", "key6": "val6"}}, Map: map[string]string{"key1": "val1", "key2": "val2", "key3": "val3"}, MapStructs: map[string]*SliceStruct{"key1": {Name: "name1"}, "key2": {Name: "name2"}, "key3": {Name: "name3"}}, MapMap: map[string]map[string]string{"key1": {"key1-1": "val1"}, "key2": {"key2-1": "val2"}, "key3": {"key3-1": "val3"}}, MapMapStruct: map[string]map[string]*SliceStruct{"key1": {"key1-1": {Name: "name1"}}, "key2": {"key2-1": {Name: "name2"}}, "key3": {"key3-1": {Name: "name3"}}}, MapSlice: map[string][]string{"key1": {"1", "2", "3"}, "key2": {"4", "5", "6"}, "key3": {"7", "8", "9"}}, } test = &Test{ Inner: inner, CreatedAt: nil, } val = reflect.ValueOf(test) current, kind, ok = validate.GetStructFieldOK(val, "Inner.SliceStructs[2]") Equal(t, ok, true) Equal(t, kind, reflect.Ptr) Equal(t, current.String(), "<*validator.SliceStruct Value>") Equal(t, current.IsNil(), true) current, kind, ok = validate.GetStructFieldOK(val, "Inner.SliceStructs[2].Name") Equal(t, ok, false) Equal(t, kind, reflect.Ptr) Equal(t, current.String(), "<*validator.SliceStruct Value>") Equal(t, current.IsNil(), true) PanicMatches(t, func() { validate.GetStructFieldOK(reflect.ValueOf(1), "crazyinput") }, "Invalid field namespace") } func TestExistsValidation(t *testing.T) { jsonText := "{ \"truthiness2\": true }" type Thing struct { Truthiness *bool `json:"truthiness" validate:"exists,required"` } var ting Thing err := json.Unmarshal([]byte(jsonText), &ting) Equal(t, err, nil) NotEqual(t, ting, nil) Equal(t, ting.Truthiness, nil) errs := validate.Struct(ting) NotEqual(t, errs, nil) AssertError(t, errs, "Thing.Truthiness", "Truthiness", "exists") jsonText = "{ \"truthiness\": true }" err = json.Unmarshal([]byte(jsonText), &ting) Equal(t, err, nil) NotEqual(t, ting, nil) Equal(t, ting.Truthiness, true) errs = validate.Struct(ting) Equal(t, errs, nil) } func TestSQLValue2Validation(t *testing.T) { config := &Config{ TagName: "validate", } validate := New(config) validate.RegisterCustomTypeFunc(ValidateValuerType, valuer{}, (*driver.Valuer)(nil), sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1) val := valuer{ Name: "", } errs := validate.Field(val, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") val.Name = "Valid Name" errs = validate.Field(val, "required") Equal(t, errs, nil) val.Name = "errorme" PanicMatches(t, func() { validate.Field(val, "required") }, "SQL Driver Valuer error: some kind of error") type myValuer valuer myVal := valuer{ Name: "", } errs = validate.Field(myVal, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") cust := MadeUpCustomType{ FirstName: "Joey", LastName: "Bloggs", } c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2} errs = validate.Struct(c) Equal(t, errs, nil) c.MadeUp.FirstName = "" c.OverriddenInt = 1 errs = validate.Struct(c) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 2) AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "MadeUp", "required") AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "gt") } func TestSQLValueValidation(t *testing.T) { validate := New(&Config{TagName: "validate"}) validate.RegisterCustomTypeFunc(ValidateValuerType, (*driver.Valuer)(nil), valuer{}) validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1) val := valuer{ Name: "", } errs := validate.Field(val, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") val.Name = "Valid Name" errs = validate.Field(val, "required") Equal(t, errs, nil) val.Name = "errorme" PanicMatches(t, func() { errs = validate.Field(val, "required") }, "SQL Driver Valuer error: some kind of error") type myValuer valuer myVal := valuer{ Name: "", } errs = validate.Field(myVal, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") cust := MadeUpCustomType{ FirstName: "Joey", LastName: "Bloggs", } c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2} errs = validate.Struct(c) Equal(t, errs, nil) c.MadeUp.FirstName = "" c.OverriddenInt = 1 errs = validate.Struct(c) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 2) AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "MadeUp", "required") AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "gt") } func TestMACValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"3D:F2:C9:A6:B3:4F", true}, {"3D-F2-C9-A6-B3:4F", false}, {"123", false}, {"", false}, {"abacaba", false}, {"00:25:96:FF:FE:12:34:56", true}, {"0025:96FF:FE12:3456", false}, } for i, test := range tests { errs := validate.Field(test.param, "mac") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d mac failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d mac failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "mac" { t.Fatalf("Index: %d mac failed Error: %s", i, errs) } } } } } func TestIPValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"10.0.0.1", true}, {"172.16.0.1", true}, {"192.168.0.1", true}, {"192.168.255.254", true}, {"192.168.255.256", false}, {"172.16.255.254", true}, {"172.16.256.255", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652", true}, {"2001:cdba:0:0:0:0:3257:9652", true}, {"2001:cdba::3257:9652", true}, } for i, test := range tests { errs := validate.Field(test.param, "ip") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ip failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ip failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ip" { t.Fatalf("Index: %d ip failed Error: %s", i, errs) } } } } } func TestIPv6Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"10.0.0.1", false}, {"172.16.0.1", false}, {"192.168.0.1", false}, {"192.168.255.254", false}, {"192.168.255.256", false}, {"172.16.255.254", false}, {"172.16.256.255", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652", true}, {"2001:cdba:0:0:0:0:3257:9652", true}, {"2001:cdba::3257:9652", true}, } for i, test := range tests { errs := validate.Field(test.param, "ipv6") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ipv6 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ipv6 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ipv6" { t.Fatalf("Index: %d ipv6 failed Error: %s", i, errs) } } } } } func TestIPv4Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"10.0.0.1", true}, {"172.16.0.1", true}, {"192.168.0.1", true}, {"192.168.255.254", true}, {"192.168.255.256", false}, {"172.16.255.254", true}, {"172.16.256.255", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652", false}, {"2001:cdba:0:0:0:0:3257:9652", false}, {"2001:cdba::3257:9652", false}, } for i, test := range tests { errs := validate.Field(test.param, "ipv4") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ipv4 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ipv4 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ipv4" { t.Fatalf("Index: %d ipv4 failed Error: %s", i, errs) } } } } } func TestCIDRValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"10.0.0.0/0", true}, {"10.0.0.1/8", true}, {"172.16.0.1/16", true}, {"192.168.0.1/24", true}, {"192.168.255.254/24", true}, {"192.168.255.254/48", false}, {"192.168.255.256/24", false}, {"172.16.255.254/16", true}, {"172.16.256.255/16", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652/64", true}, {"2001:cdba:0000:0000:0000:0000:3257:9652/256", false}, {"2001:cdba:0:0:0:0:3257:9652/32", true}, {"2001:cdba::3257:9652/16", true}, } for i, test := range tests { errs := validate.Field(test.param, "cidr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d cidr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d cidr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "cidr" { t.Fatalf("Index: %d cidr failed Error: %s", i, errs) } } } } } func TestCIDRv6Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"10.0.0.0/0", false}, {"10.0.0.1/8", false}, {"172.16.0.1/16", false}, {"192.168.0.1/24", false}, {"192.168.255.254/24", false}, {"192.168.255.254/48", false}, {"192.168.255.256/24", false}, {"172.16.255.254/16", false}, {"172.16.256.255/16", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652/64", true}, {"2001:cdba:0000:0000:0000:0000:3257:9652/256", false}, {"2001:cdba:0:0:0:0:3257:9652/32", true}, {"2001:cdba::3257:9652/16", true}, } for i, test := range tests { errs := validate.Field(test.param, "cidrv6") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d cidrv6 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d cidrv6 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "cidrv6" { t.Fatalf("Index: %d cidrv6 failed Error: %s", i, errs) } } } } } func TestCIDRv4Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"10.0.0.0/0", true}, {"10.0.0.1/8", true}, {"172.16.0.1/16", true}, {"192.168.0.1/24", true}, {"192.168.255.254/24", true}, {"192.168.255.254/48", false}, {"192.168.255.256/24", false}, {"172.16.255.254/16", true}, {"172.16.256.255/16", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652/64", false}, {"2001:cdba:0000:0000:0000:0000:3257:9652/256", false}, {"2001:cdba:0:0:0:0:3257:9652/32", false}, {"2001:cdba::3257:9652/16", false}, } for i, test := range tests { errs := validate.Field(test.param, "cidrv4") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d cidrv4 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d cidrv4 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "cidrv4" { t.Fatalf("Index: %d cidrv4 failed Error: %s", i, errs) } } } } } func TestTCPAddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {":80", false}, {"127.0.0.1:80", true}, {"[::1]:80", true}, {"256.0.0.0:1", false}, {"[::1]", false}, } for i, test := range tests { errs := validate.Field(test.param, "tcp_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d tcp_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d tcp_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "tcp_addr" { t.Fatalf("Index: %d tcp_addr failed Error: %s", i, errs) } } } } } func TestTCP6AddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {":80", false}, {"127.0.0.1:80", false}, {"[::1]:80", true}, {"256.0.0.0:1", false}, {"[::1]", false}, } for i, test := range tests { errs := validate.Field(test.param, "tcp6_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d tcp6_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d tcp6_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "tcp6_addr" { t.Fatalf("Index: %d tcp6_addr failed Error: %s", i, errs) } } } } } func TestTCP4AddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {":80", false}, {"127.0.0.1:80", true}, {"[::1]:80", false}, // https://github.com/golang/go/issues/14037 {"256.0.0.0:1", false}, {"[::1]", false}, } for i, test := range tests { errs := validate.Field(test.param, "tcp4_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d tcp4_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Log(test.param, IsEqual(errs, nil)) t.Fatalf("Index: %d tcp4_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "tcp4_addr" { t.Fatalf("Index: %d tcp4_addr failed Error: %s", i, errs) } } } } } func TestUDPAddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {":80", false}, {"127.0.0.1:80", true}, {"[::1]:80", true}, {"256.0.0.0:1", false}, {"[::1]", false}, } for i, test := range tests { errs := validate.Field(test.param, "udp_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d udp_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d udp_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "udp_addr" { t.Fatalf("Index: %d udp_addr failed Error: %s", i, errs) } } } } } func TestUDP6AddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {":80", false}, {"127.0.0.1:80", false}, {"[::1]:80", true}, {"256.0.0.0:1", false}, {"[::1]", false}, } for i, test := range tests { errs := validate.Field(test.param, "udp6_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d udp6_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d udp6_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "udp6_addr" { t.Fatalf("Index: %d udp6_addr failed Error: %s", i, errs) } } } } } func TestUDP4AddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {":80", false}, {"127.0.0.1:80", true}, {"[::1]:80", false}, // https://github.com/golang/go/issues/14037 {"256.0.0.0:1", false}, {"[::1]", false}, } for i, test := range tests { errs := validate.Field(test.param, "udp4_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d udp4_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Log(test.param, IsEqual(errs, nil)) t.Fatalf("Index: %d udp4_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "udp4_addr" { t.Fatalf("Index: %d udp4_addr failed Error: %s", i, errs) } } } } } func TestIPAddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"127.0.0.1", true}, {"127.0.0.1:80", false}, {"::1", true}, {"256.0.0.0", false}, {"localhost", false}, } for i, test := range tests { errs := validate.Field(test.param, "ip_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ip_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ip_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ip_addr" { t.Fatalf("Index: %d ip_addr failed Error: %s", i, errs) } } } } } func TestIP6AddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"127.0.0.1", false}, // https://github.com/golang/go/issues/14037 {"127.0.0.1:80", false}, {"::1", true}, {"0:0:0:0:0:0:0:1", true}, {"256.0.0.0", false}, } for i, test := range tests { errs := validate.Field(test.param, "ip6_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ip6_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ip6_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ip6_addr" { t.Fatalf("Index: %d ip6_addr failed Error: %s", i, errs) } } } } } func TestIP4AddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"127.0.0.1", true}, {"127.0.0.1:80", false}, {"::1", false}, // https://github.com/golang/go/issues/14037 {"256.0.0.0", false}, {"localhost", false}, } for i, test := range tests { errs := validate.Field(test.param, "ip4_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ip4_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Log(test.param, IsEqual(errs, nil)) t.Fatalf("Index: %d ip4_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ip4_addr" { t.Fatalf("Index: %d ip4_addr failed Error: %s", i, errs) } } } } } func TestUnixAddrValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", true}, {"v.sock", true}, } for i, test := range tests { errs := validate.Field(test.param, "unix_addr") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d unix_addr failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Log(test.param, IsEqual(errs, nil)) t.Fatalf("Index: %d unix_addr failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "unix_addr" { t.Fatalf("Index: %d unix_addr failed Error: %s", i, errs) } } } } } func TestSliceMapArrayChanFuncPtrInterfaceRequiredValidation(t *testing.T) { var m map[string]string errs := validate.Field(m, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") m = map[string]string{} errs = validate.Field(m, "required") Equal(t, errs, nil) var arr [5]string errs = validate.Field(arr, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") arr[0] = "ok" errs = validate.Field(arr, "required") Equal(t, errs, nil) var s []string errs = validate.Field(s, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") s = []string{} errs = validate.Field(s, "required") Equal(t, errs, nil) var c chan string errs = validate.Field(c, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") c = make(chan string) errs = validate.Field(c, "required") Equal(t, errs, nil) var tst *int errs = validate.Field(tst, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") one := 1 tst = &one errs = validate.Field(tst, "required") Equal(t, errs, nil) var iface interface{} errs = validate.Field(iface, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") errs = validate.Field(iface, "omitempty,required") Equal(t, errs, nil) errs = validate.Field(iface, "") Equal(t, errs, nil) errs = validate.FieldWithValue(nil, iface, "") Equal(t, errs, nil) var f func(string) errs = validate.Field(f, "required") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "required") f = func(name string) {} errs = validate.Field(f, "required") Equal(t, errs, nil) } func TestDatePtrValidationIssueValidation(t *testing.T) { type Test struct { LastViewed *time.Time Reminder *time.Time } test := &Test{} errs := validate.Struct(test) Equal(t, errs, nil) } func TestCommaAndPipeObfuscationValidation(t *testing.T) { s := "My Name Is, |joeybloggs|" errs := validate.Field(s, "excludesall=0x2C") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "excludesall") errs = validate.Field(s, "excludesall=0x7C") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "excludesall") } func TestBadKeyValidation(t *testing.T) { type Test struct { Name string `validate:"required, "` } tst := &Test{ Name: "test", } PanicMatches(t, func() { validate.Struct(tst) }, "Undefined validation function on field Name") type Test2 struct { Name string `validate:"required,,len=2"` } tst2 := &Test2{ Name: "test", } PanicMatches(t, func() { validate.Struct(tst2) }, "Invalid validation tag on field Name") } func TestInterfaceErrValidation(t *testing.T) { var v1 interface{} var v2 interface{} v2 = 1 v1 = v2 errs := validate.Field(v1, "len=1") Equal(t, errs, nil) errs = validate.Field(v2, "len=1") Equal(t, errs, nil) type ExternalCMD struct { Userid string `json:"userid"` Action uint32 `json:"action"` Data interface{} `json:"data,omitempty" validate:"required"` } s := &ExternalCMD{ Userid: "123456", Action: 10000, // Data: 1, } errs = validate.Struct(s) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "ExternalCMD.Data", "Data", "required") type ExternalCMD2 struct { Userid string `json:"userid"` Action uint32 `json:"action"` Data interface{} `json:"data,omitempty" validate:"len=1"` } s2 := &ExternalCMD2{ Userid: "123456", Action: 10000, // Data: 1, } errs = validate.Struct(s2) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "ExternalCMD2.Data", "Data", "len") s3 := &ExternalCMD2{ Userid: "123456", Action: 10000, Data: 2, } errs = validate.Struct(s3) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "ExternalCMD2.Data", "Data", "len") type Inner struct { Name string `validate:"required"` } inner := &Inner{ Name: "", } s4 := &ExternalCMD{ Userid: "123456", Action: 10000, Data: inner, } errs = validate.Struct(s4) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "ExternalCMD.Data.Name", "Name", "required") type TestMapStructPtr struct { Errs map[int]interface{} `validate:"gt=0,dive,len=2"` } mip := map[int]interface{}{0: &Inner{"ok"}, 3: nil, 4: &Inner{"ok"}} msp := &TestMapStructPtr{ Errs: mip, } errs = validate.Struct(msp) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "TestMapStructPtr.Errs[3]", "Errs[3]", "len") type TestMultiDimensionalStructs struct { Errs [][]interface{} `validate:"gt=0,dive,dive"` } var errStructArray [][]interface{} errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) errStructArray = append(errStructArray, []interface{}{&Inner{"ok"}, &Inner{""}, &Inner{""}}) tms := &TestMultiDimensionalStructs{ Errs: errStructArray, } errs = validate.Struct(tms) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 4) AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][2].Name", "Name", "required") type TestMultiDimensionalStructsPtr2 struct { Errs [][]*Inner `validate:"gt=0,dive,dive,required"` } var errStructPtr2Array [][]*Inner errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}}) errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}}) errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, nil}) tmsp2 := &TestMultiDimensionalStructsPtr2{ Errs: errStructPtr2Array, } errs = validate.Struct(tmsp2) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 6) AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][2]", "Errs[2][2]", "required") m := map[int]interface{}{0: "ok", 3: "", 4: "ok"} errs = validate.Field(m, "len=3,dive,len=2") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "[3]", "[3]", "len") errs = validate.Field(m, "len=2,dive,required") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "", "", "len") arr := []interface{}{"ok", "", "ok"} errs = validate.Field(arr, "len=3,dive,len=2") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "[1]", "[1]", "len") errs = validate.Field(arr, "len=2,dive,required") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "", "", "len") type MyStruct struct { A, B string C interface{} } var a MyStruct a.A = "value" a.C = "nu" errs = validate.Struct(a) Equal(t, errs, nil) } func TestMapDiveValidation(t *testing.T) { n := map[int]interface{}{0: nil} errs := validate.Field(n, "omitempty,required") Equal(t, errs, nil) m := map[int]string{0: "ok", 3: "", 4: "ok"} errs = validate.Field(m, "len=3,dive,required") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "[3]", "[3]", "required") errs = validate.Field(m, "len=2,dive,required") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "", "", "len") type Inner struct { Name string `validate:"required"` } type TestMapStruct struct { Errs map[int]Inner `validate:"gt=0,dive"` } mi := map[int]Inner{0: {"ok"}, 3: {""}, 4: {"ok"}} ms := &TestMapStruct{ Errs: mi, } errs = validate.Struct(ms) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "TestMapStruct.Errs[3].Name", "Name", "required") // for full test coverage s := fmt.Sprint(errs.Error()) NotEqual(t, s, "") type TestMapTimeStruct struct { Errs map[int]*time.Time `validate:"gt=0,dive,required"` } t1 := time.Now().UTC() mta := map[int]*time.Time{0: &t1, 3: nil, 4: nil} mt := &TestMapTimeStruct{ Errs: mta, } errs = validate.Struct(mt) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 2) AssertError(t, errs, "TestMapTimeStruct.Errs[3]", "Errs[3]", "required") AssertError(t, errs, "TestMapTimeStruct.Errs[4]", "Errs[4]", "required") type TestMapStructPtr struct { Errs map[int]*Inner `validate:"gt=0,dive,required"` } mip := map[int]*Inner{0: {"ok"}, 3: nil, 4: {"ok"}} msp := &TestMapStructPtr{ Errs: mip, } errs = validate.Struct(msp) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "TestMapStructPtr.Errs[3]", "Errs[3]", "required") type TestMapStructPtr2 struct { Errs map[int]*Inner `validate:"gt=0,dive,omitempty,required"` } mip2 := map[int]*Inner{0: {"ok"}, 3: nil, 4: {"ok"}} msp2 := &TestMapStructPtr2{ Errs: mip2, } errs = validate.Struct(msp2) Equal(t, errs, nil) } func TestArrayDiveValidation(t *testing.T) { arr := []string{"ok", "", "ok"} errs := validate.Field(arr, "len=3,dive,required") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "[1]", "[1]", "required") errs = validate.Field(arr, "len=2,dive,required") NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "", "", "len") type BadDive struct { Name string `validate:"dive"` } bd := &BadDive{ Name: "TEST", } PanicMatches(t, func() { validate.Struct(bd) }, "dive error! can't dive on a non slice or map") type Test struct { Errs []string `validate:"gt=0,dive,required"` } test := &Test{ Errs: []string{"ok", "", "ok"}, } errs = validate.Struct(test) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "Test.Errs[1]", "Errs[1]", "required") test = &Test{ Errs: []string{"ok", "ok", ""}, } errs = validate.Struct(test) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 1) AssertError(t, errs, "Test.Errs[2]", "Errs[2]", "required") type TestMultiDimensional struct { Errs [][]string `validate:"gt=0,dive,dive,required"` } var errArray [][]string errArray = append(errArray, []string{"ok", "", ""}) errArray = append(errArray, []string{"ok", "", ""}) tm := &TestMultiDimensional{ Errs: errArray, } errs = validate.Struct(tm) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 4) AssertError(t, errs, "TestMultiDimensional.Errs[0][1]", "Errs[0][1]", "required") AssertError(t, errs, "TestMultiDimensional.Errs[0][2]", "Errs[0][2]", "required") AssertError(t, errs, "TestMultiDimensional.Errs[1][1]", "Errs[1][1]", "required") AssertError(t, errs, "TestMultiDimensional.Errs[1][2]", "Errs[1][2]", "required") type Inner struct { Name string `validate:"required"` } type TestMultiDimensionalStructs struct { Errs [][]Inner `validate:"gt=0,dive,dive"` } var errStructArray [][]Inner errStructArray = append(errStructArray, []Inner{{"ok"}, {""}, {""}}) errStructArray = append(errStructArray, []Inner{{"ok"}, {""}, {""}}) tms := &TestMultiDimensionalStructs{ Errs: errStructArray, } errs = validate.Struct(tms) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 4) AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructs.Errs[0][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructs.Errs[1][2].Name", "Name", "required") type TestMultiDimensionalStructsPtr struct { Errs [][]*Inner `validate:"gt=0,dive,dive"` } var errStructPtrArray [][]*Inner errStructPtrArray = append(errStructPtrArray, []*Inner{{"ok"}, {""}, {""}}) errStructPtrArray = append(errStructPtrArray, []*Inner{{"ok"}, {""}, {""}}) errStructPtrArray = append(errStructPtrArray, []*Inner{{"ok"}, {""}, nil}) tmsp := &TestMultiDimensionalStructsPtr{ Errs: errStructPtrArray, } errs = validate.Struct(tmsp) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 5) AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[0][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[0][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[1][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[1][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr.Errs[2][1].Name", "Name", "required") // for full test coverage s := fmt.Sprint(errs.Error()) NotEqual(t, s, "") type TestMultiDimensionalStructsPtr2 struct { Errs [][]*Inner `validate:"gt=0,dive,dive,required"` } var errStructPtr2Array [][]*Inner errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}}) errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, {""}}) errStructPtr2Array = append(errStructPtr2Array, []*Inner{{"ok"}, {""}, nil}) tmsp2 := &TestMultiDimensionalStructsPtr2{ Errs: errStructPtr2Array, } errs = validate.Struct(tmsp2) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 6) AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[0][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[1][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr2.Errs[2][2]", "Errs[2][2]", "required") type TestMultiDimensionalStructsPtr3 struct { Errs [][]*Inner `validate:"gt=0,dive,dive,omitempty"` } var errStructPtr3Array [][]*Inner errStructPtr3Array = append(errStructPtr3Array, []*Inner{{"ok"}, {""}, {""}}) errStructPtr3Array = append(errStructPtr3Array, []*Inner{{"ok"}, {""}, {""}}) errStructPtr3Array = append(errStructPtr3Array, []*Inner{{"ok"}, {""}, nil}) tmsp3 := &TestMultiDimensionalStructsPtr3{ Errs: errStructPtr3Array, } errs = validate.Struct(tmsp3) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 5) AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[0][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[0][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[1][1].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[1][2].Name", "Name", "required") AssertError(t, errs, "TestMultiDimensionalStructsPtr3.Errs[2][1].Name", "Name", "required") type TestMultiDimensionalTimeTime struct { Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` } var errTimePtr3Array [][]*time.Time t1 := time.Now().UTC() t2 := time.Now().UTC() t3 := time.Now().UTC().Add(time.Hour * 24) errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, &t3}) errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, &t2, nil}) errTimePtr3Array = append(errTimePtr3Array, []*time.Time{&t1, nil, nil}) tmtp3 := &TestMultiDimensionalTimeTime{ Errs: errTimePtr3Array, } errs = validate.Struct(tmtp3) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 3) AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[1][2]", "Errs[1][2]", "required") AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[2][1]", "Errs[2][1]", "required") AssertError(t, errs, "TestMultiDimensionalTimeTime.Errs[2][2]", "Errs[2][2]", "required") type TestMultiDimensionalTimeTime2 struct { Errs [][]*time.Time `validate:"gt=0,dive,dive,required"` } var errTimeArray [][]*time.Time t1 = time.Now().UTC() t2 = time.Now().UTC() t3 = time.Now().UTC().Add(time.Hour * 24) errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, &t3}) errTimeArray = append(errTimeArray, []*time.Time{&t1, &t2, nil}) errTimeArray = append(errTimeArray, []*time.Time{&t1, nil, nil}) tmtp := &TestMultiDimensionalTimeTime2{ Errs: errTimeArray, } errs = validate.Struct(tmtp) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 3) AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[1][2]", "Errs[1][2]", "required") AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[2][1]", "Errs[2][1]", "required") AssertError(t, errs, "TestMultiDimensionalTimeTime2.Errs[2][2]", "Errs[2][2]", "required") } func TestNilStructPointerValidation(t *testing.T) { type Inner struct { Data string } type Outer struct { Inner *Inner `validate:"omitempty"` } inner := &Inner{ Data: "test", } outer := &Outer{ Inner: inner, } errs := validate.Struct(outer) Equal(t, errs, nil) outer = &Outer{ Inner: nil, } errs = validate.Struct(outer) Equal(t, errs, nil) type Inner2 struct { Data string } type Outer2 struct { Inner2 *Inner2 `validate:"required"` } inner2 := &Inner2{ Data: "test", } outer2 := &Outer2{ Inner2: inner2, } errs = validate.Struct(outer2) Equal(t, errs, nil) outer2 = &Outer2{ Inner2: nil, } errs = validate.Struct(outer2) NotEqual(t, errs, nil) AssertError(t, errs, "Outer2.Inner2", "Inner2", "required") type Inner3 struct { Data string } type Outer3 struct { Inner3 *Inner3 } inner3 := &Inner3{ Data: "test", } outer3 := &Outer3{ Inner3: inner3, } errs = validate.Struct(outer3) Equal(t, errs, nil) type Inner4 struct { Data string } type Outer4 struct { Inner4 *Inner4 `validate:"-"` } inner4 := &Inner4{ Data: "test", } outer4 := &Outer4{ Inner4: inner4, } errs = validate.Struct(outer4) Equal(t, errs, nil) } func TestSSNValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"00-90-8787", false}, {"66690-76", false}, {"191 60 2869", true}, {"191-60-2869", true}, } for i, test := range tests { errs := validate.Field(test.param, "ssn") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d SSN failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d SSN failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ssn" { t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) } } } } } func TestLongitudeValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"-180.000", true}, {"180.1", false}, {"+73.234", true}, {"+382.3811", false}, {"23.11111111", true}, } for i, test := range tests { errs := validate.Field(test.param, "longitude") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d Longitude failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d Longitude failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "longitude" { t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) } } } } } func TestLatitudeValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"-90.000", true}, {"+90", true}, {"47.1231231", true}, {"+99.9", false}, {"108", false}, } for i, test := range tests { errs := validate.Field(test.param, "latitude") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "latitude" { t.Fatalf("Index: %d Latitude failed Error: %s", i, errs) } } } } } func TestDataURIValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"data:image/png;base64,TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, {"data:text/plain;base64,Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, {"image/gif;base64,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, {"data:image/gif;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, {"data:image/png;base64,12345", false}, {"", false}, {"data:text,:;base85,U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", false}, } for i, test := range tests { errs := validate.Field(test.param, "datauri") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d DataURI failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d DataURI failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "datauri" { t.Fatalf("Index: %d DataURI failed Error: %s", i, errs) } } } } } func TestMultibyteValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", true}, {"abc", false}, {"123", false}, {"<>@;.-=", false}, {"ひらがな・カタカナ、.漢字", true}, {"あいうえお foobar", true}, {"test@example.com", true}, {"test@example.com", true}, {"1234abcDExyz", true}, {"カタカナ", true}, } for i, test := range tests { errs := validate.Field(test.param, "multibyte") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "multibyte" { t.Fatalf("Index: %d Multibyte failed Error: %s", i, errs) } } } } } func TestPrintableASCIIValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", true}, {"foobar", false}, {"xyz098", false}, {"123456", false}, {"カタカナ", false}, {"foobar", true}, {"0987654321", true}, {"test@example.com", true}, {"1234abcDEF", true}, {"newline\n", false}, {"\x19test\x7F", false}, } for i, test := range tests { errs := validate.Field(test.param, "printascii") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "printascii" { t.Fatalf("Index: %d Printable ASCII failed Error: %s", i, errs) } } } } } func TestASCIIValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", true}, {"foobar", false}, {"xyz098", false}, {"123456", false}, {"カタカナ", false}, {"foobar", true}, {"0987654321", true}, {"test@example.com", true}, {"1234abcDEF", true}, {"", true}, } for i, test := range tests { errs := validate.Field(test.param, "ascii") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ASCII failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ASCII failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "ascii" { t.Fatalf("Index: %d ASCII failed Error: %s", i, errs) } } } } } func TestUUID5Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, {"9c858901-8a57-4791-81fe-4c455b099bc9", false}, {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, {"987fbc97-4bed-5078-af07-9141ba07c9f3", true}, {"987fbc97-4bed-5078-9f07-9141ba07c9f3", true}, } for i, test := range tests { errs := validate.Field(test.param, "uuid5") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "uuid5" { t.Fatalf("Index: %d UUID5 failed Error: %s", i, errs) } } } } } func TestUUID4Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, {"a987fbc9-4bed-5078-af07-9141ba07c9f3", false}, {"934859", false}, {"57b73598-8764-4ad0-a76a-679bb6640eb1", true}, {"625e63f3-58f5-40b7-83a1-a72ad31acffb", true}, } for i, test := range tests { errs := validate.Field(test.param, "uuid4") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "uuid4" { t.Fatalf("Index: %d UUID4 failed Error: %s", i, errs) } } } } } func TestUUID3Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"412452646", false}, {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, {"a987fbc9-4bed-4078-8f07-9141ba07c9f3", false}, {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, } for i, test := range tests { errs := validate.Field(test.param, "uuid3") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "uuid3" { t.Fatalf("Index: %d UUID3 failed Error: %s", i, errs) } } } } } func TestUUIDValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"xxxa987fbc9-4bed-3078-cf07-9141ba07c9f3", false}, {"a987fbc9-4bed-3078-cf07-9141ba07c9f3xxx", false}, {"a987fbc94bed3078cf079141ba07c9f3", false}, {"934859", false}, {"987fbc9-4bed-3078-cf07a-9141ba07c9f3", false}, {"aaaaaaaa-1111-1111-aaag-111111111111", false}, {"a987fbc9-4bed-3078-cf07-9141ba07c9f3", true}, } for i, test := range tests { errs := validate.Field(test.param, "uuid") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d UUID failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d UUID failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "uuid" { t.Fatalf("Index: %d UUID failed Error: %s", i, errs) } } } } } func TestISBNValidation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"foo", false}, {"3836221195", true}, {"1-61729-085-8", true}, {"3 423 21412 0", true}, {"3 401 01319 X", true}, {"9784873113685", true}, {"978-4-87311-368-5", true}, {"978 3401013190", true}, {"978-3-8362-2119-1", true}, } for i, test := range tests { errs := validate.Field(test.param, "isbn") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ISBN failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ISBN failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "isbn" { t.Fatalf("Index: %d ISBN failed Error: %s", i, errs) } } } } } func TestISBN13Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"foo", false}, {"3-8362-2119-5", false}, {"01234567890ab", false}, {"978 3 8362 2119 0", false}, {"9784873113685", true}, {"978-4-87311-368-5", true}, {"978 3401013190", true}, {"978-3-8362-2119-1", true}, } for i, test := range tests { errs := validate.Field(test.param, "isbn13") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "isbn13" { t.Fatalf("Index: %d ISBN13 failed Error: %s", i, errs) } } } } } func TestISBN10Validation(t *testing.T) { tests := []struct { param string expected bool }{ {"", false}, {"foo", false}, {"3423214121", false}, {"978-3836221191", false}, {"3-423-21412-1", false}, {"3 423 21412 1", false}, {"3836221195", true}, {"1-61729-085-8", true}, {"3 423 21412 0", true}, {"3 401 01319 X", true}, } for i, test := range tests { errs := validate.Field(test.param, "isbn10") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "isbn10" { t.Fatalf("Index: %d ISBN10 failed Error: %s", i, errs) } } } } } func TestExcludesRuneValidation(t *testing.T) { tests := []struct { Value string `validate:"excludesrune=☻"` Tag string ExpectedNil bool }{ {Value: "a☺b☻c☹d", Tag: "excludesrune=☻", ExpectedNil: false}, {Value: "abcd", Tag: "excludesrune=☻", ExpectedNil: true}, } for i, s := range tests { errs := validate.Field(s.Value, s.Tag) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } errs = validate.Struct(s) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } } } func TestExcludesAllValidation(t *testing.T) { tests := []struct { Value string `validate:"excludesall=@!{}[]"` Tag string ExpectedNil bool }{ {Value: "abcd@!jfk", Tag: "excludesall=@!{}[]", ExpectedNil: false}, {Value: "abcdefg", Tag: "excludesall=@!{}[]", ExpectedNil: true}, } for i, s := range tests { errs := validate.Field(s.Value, s.Tag) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } errs = validate.Struct(s) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } } username := "joeybloggs " errs := validate.Field(username, "excludesall=@ ") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "excludesall") excluded := "," errs = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "excludesall") excluded = "=" errs = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "excludesall") } func TestExcludesValidation(t *testing.T) { tests := []struct { Value string `validate:"excludes=@"` Tag string ExpectedNil bool }{ {Value: "abcd@!jfk", Tag: "excludes=@", ExpectedNil: false}, {Value: "abcdq!jfk", Tag: "excludes=@", ExpectedNil: true}, } for i, s := range tests { errs := validate.Field(s.Value, s.Tag) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } errs = validate.Struct(s) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } } } func TestContainsRuneValidation(t *testing.T) { tests := []struct { Value string `validate:"containsrune=☻"` Tag string ExpectedNil bool }{ {Value: "a☺b☻c☹d", Tag: "containsrune=☻", ExpectedNil: true}, {Value: "abcd", Tag: "containsrune=☻", ExpectedNil: false}, } for i, s := range tests { errs := validate.Field(s.Value, s.Tag) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } errs = validate.Struct(s) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } } } func TestContainsAnyValidation(t *testing.T) { tests := []struct { Value string `validate:"containsany=@!{}[]"` Tag string ExpectedNil bool }{ {Value: "abcd@!jfk", Tag: "containsany=@!{}[]", ExpectedNil: true}, {Value: "abcdefg", Tag: "containsany=@!{}[]", ExpectedNil: false}, } for i, s := range tests { errs := validate.Field(s.Value, s.Tag) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } errs = validate.Struct(s) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } } } func TestContainsValidation(t *testing.T) { tests := []struct { Value string `validate:"contains=@"` Tag string ExpectedNil bool }{ {Value: "abcd@!jfk", Tag: "contains=@", ExpectedNil: true}, {Value: "abcdq!jfk", Tag: "contains=@", ExpectedNil: false}, } for i, s := range tests { errs := validate.Field(s.Value, s.Tag) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } errs = validate.Struct(s) if (s.ExpectedNil && errs != nil) || (!s.ExpectedNil && errs == nil) { t.Fatalf("Index: %d failed Error: %s", i, errs) } } } func TestIsNeFieldValidation(t *testing.T) { var j uint64 var k float64 s := "abcd" i := 1 j = 1 k = 1.543 arr := []string{"test"} now := time.Now().UTC() var j2 uint64 var k2 float64 s2 := "abcdef" i2 := 3 j2 = 2 k2 = 1.5434456 arr2 := []string{"test", "test2"} arr3 := []string{"test"} now2 := now errs := validate.FieldWithValue(s, s2, "nefield") Equal(t, errs, nil) errs = validate.FieldWithValue(i2, i, "nefield") Equal(t, errs, nil) errs = validate.FieldWithValue(j2, j, "nefield") Equal(t, errs, nil) errs = validate.FieldWithValue(k2, k, "nefield") Equal(t, errs, nil) errs = validate.FieldWithValue(arr2, arr, "nefield") Equal(t, errs, nil) errs = validate.FieldWithValue(now2, now, "nefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "nefield") errs = validate.FieldWithValue(arr3, arr, "nefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "nefield") type Test struct { Start *time.Time `validate:"nefield=End"` End *time.Time } sv := &Test{ Start: &now, End: &now, } errs = validate.Struct(sv) NotEqual(t, errs, nil) AssertError(t, errs, "Test.Start", "Start", "nefield") now3 := time.Now().UTC() sv = &Test{ Start: &now, End: &now3, } errs = validate.Struct(sv) Equal(t, errs, nil) errs = validate.FieldWithValue(nil, 1, "nefield") Equal(t, errs, nil) errs = validate.FieldWithValue(sv, now, "nefield") Equal(t, errs, nil) type Test2 struct { Start *time.Time `validate:"nefield=NonExistantField"` End *time.Time } sv2 := &Test2{ Start: &now, End: &now, } errs = validate.Struct(sv2) Equal(t, errs, nil) } func TestIsNeValidation(t *testing.T) { var j uint64 var k float64 s := "abcdef" i := 3 j = 2 k = 1.5434 arr := []string{"test"} now := time.Now().UTC() errs := validate.Field(s, "ne=abcd") Equal(t, errs, nil) errs = validate.Field(i, "ne=1") Equal(t, errs, nil) errs = validate.Field(j, "ne=1") Equal(t, errs, nil) errs = validate.Field(k, "ne=1.543") Equal(t, errs, nil) errs = validate.Field(arr, "ne=2") Equal(t, errs, nil) errs = validate.Field(arr, "ne=1") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ne") PanicMatches(t, func() { validate.Field(now, "ne=now") }, "Bad field type time.Time") } func TestIsEqFieldValidation(t *testing.T) { var j uint64 var k float64 s := "abcd" i := 1 j = 1 k = 1.543 arr := []string{"test"} now := time.Now().UTC() var j2 uint64 var k2 float64 s2 := "abcd" i2 := 1 j2 = 1 k2 = 1.543 arr2 := []string{"test"} arr3 := []string{"test", "test2"} now2 := now errs := validate.FieldWithValue(s, s2, "eqfield") Equal(t, errs, nil) errs = validate.FieldWithValue(i2, i, "eqfield") Equal(t, errs, nil) errs = validate.FieldWithValue(j2, j, "eqfield") Equal(t, errs, nil) errs = validate.FieldWithValue(k2, k, "eqfield") Equal(t, errs, nil) errs = validate.FieldWithValue(arr2, arr, "eqfield") Equal(t, errs, nil) errs = validate.FieldWithValue(now2, now, "eqfield") Equal(t, errs, nil) errs = validate.FieldWithValue(arr3, arr, "eqfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "eqfield") type Test struct { Start *time.Time `validate:"eqfield=End"` End *time.Time } sv := &Test{ Start: &now, End: &now, } errs = validate.Struct(sv) Equal(t, errs, nil) now3 := time.Now().UTC() sv = &Test{ Start: &now, End: &now3, } errs = validate.Struct(sv) NotEqual(t, errs, nil) AssertError(t, errs, "Test.Start", "Start", "eqfield") errs = validate.FieldWithValue(nil, 1, "eqfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "eqfield") channel := make(chan string) errs = validate.FieldWithValue(5, channel, "eqfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "eqfield") errs = validate.FieldWithValue(5, now, "eqfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "eqfield") type Test2 struct { Start *time.Time `validate:"eqfield=NonExistantField"` End *time.Time } sv2 := &Test2{ Start: &now, End: &now, } errs = validate.Struct(sv2) NotEqual(t, errs, nil) AssertError(t, errs, "Test2.Start", "Start", "eqfield") type Inner struct { Name string } type TStruct struct { Inner *Inner CreatedAt *time.Time `validate:"eqfield=Inner"` } inner := &Inner{ Name: "NAME", } test := &TStruct{ Inner: inner, CreatedAt: &now, } errs = validate.Struct(test) NotEqual(t, errs, nil) AssertError(t, errs, "TStruct.CreatedAt", "CreatedAt", "eqfield") } func TestIsEqValidation(t *testing.T) { var j uint64 var k float64 s := "abcd" i := 1 j = 1 k = 1.543 arr := []string{"test"} now := time.Now().UTC() errs := validate.Field(s, "eq=abcd") Equal(t, errs, nil) errs = validate.Field(i, "eq=1") Equal(t, errs, nil) errs = validate.Field(j, "eq=1") Equal(t, errs, nil) errs = validate.Field(k, "eq=1.543") Equal(t, errs, nil) errs = validate.Field(arr, "eq=1") Equal(t, errs, nil) errs = validate.Field(arr, "eq=2") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "eq") PanicMatches(t, func() { validate.Field(now, "eq=now") }, "Bad field type time.Time") } func TestBase64Validation(t *testing.T) { s := "dW5pY29ybg==" errs := validate.Field(s, "base64") Equal(t, errs, nil) s = "dGhpIGlzIGEgdGVzdCBiYXNlNjQ=" errs = validate.Field(s, "base64") Equal(t, errs, nil) s = "" errs = validate.Field(s, "base64") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "base64") s = "dW5pY29ybg== foo bar" errs = validate.Field(s, "base64") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "base64") } func TestNoStructLevelValidation(t *testing.T) { type Inner struct { Test string `validate:"len=5"` } type Outer struct { InnerStruct *Inner `validate:"required,nostructlevel"` } outer := &Outer{ InnerStruct: nil, } errs := validate.Struct(outer) NotEqual(t, errs, nil) AssertError(t, errs, "Outer.InnerStruct", "InnerStruct", "required") inner := &Inner{ Test: "1234", } outer = &Outer{ InnerStruct: inner, } errs = validate.Struct(outer) Equal(t, errs, nil) } func TestStructOnlyValidation(t *testing.T) { type Inner struct { Test string `validate:"len=5"` } type Outer struct { InnerStruct *Inner `validate:"required,structonly"` } outer := &Outer{ InnerStruct: nil, } errs := validate.Struct(outer) NotEqual(t, errs, nil) AssertError(t, errs, "Outer.InnerStruct", "InnerStruct", "required") inner := &Inner{ Test: "1234", } outer = &Outer{ InnerStruct: inner, } errs = validate.Struct(outer) Equal(t, errs, nil) } func TestGtField(t *testing.T) { type TimeTest struct { Start *time.Time `validate:"required,gt"` End *time.Time `validate:"required,gt,gtfield=Start"` } now := time.Now() start := now.Add(time.Hour * 24) end := start.Add(time.Hour * 24) timeTest := &TimeTest{ Start: &start, End: &end, } errs := validate.Struct(timeTest) Equal(t, errs, nil) timeTest = &TimeTest{ Start: &end, End: &start, } errs = validate.Struct(timeTest) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest.End", "End", "gtfield") errs = validate.FieldWithValue(&start, &end, "gtfield") Equal(t, errs, nil) errs = validate.FieldWithValue(&end, &start, "gtfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtfield") errs = validate.FieldWithValue(&timeTest, &end, "gtfield") NotEqual(t, errs, nil) errs = validate.FieldWithValue("test", "test bigger", "gtfield") Equal(t, errs, nil) type IntTest struct { Val1 int `validate:"required"` Val2 int `validate:"required,gtfield=Val1"` } intTest := &IntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(intTest) Equal(t, errs, nil) intTest = &IntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(intTest) NotEqual(t, errs, nil) AssertError(t, errs, "IntTest.Val2", "Val2", "gtfield") errs = validate.FieldWithValue(int(1), int(5), "gtfield") Equal(t, errs, nil) errs = validate.FieldWithValue(int(5), int(1), "gtfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtfield") type UIntTest struct { Val1 uint `validate:"required"` Val2 uint `validate:"required,gtfield=Val1"` } uIntTest := &UIntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(uIntTest) Equal(t, errs, nil) uIntTest = &UIntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(uIntTest) NotEqual(t, errs, nil) AssertError(t, errs, "UIntTest.Val2", "Val2", "gtfield") errs = validate.FieldWithValue(uint(1), uint(5), "gtfield") Equal(t, errs, nil) errs = validate.FieldWithValue(uint(5), uint(1), "gtfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtfield") type FloatTest struct { Val1 float64 `validate:"required"` Val2 float64 `validate:"required,gtfield=Val1"` } floatTest := &FloatTest{ Val1: 1, Val2: 5, } errs = validate.Struct(floatTest) Equal(t, errs, nil) floatTest = &FloatTest{ Val1: 5, Val2: 1, } errs = validate.Struct(floatTest) NotEqual(t, errs, nil) AssertError(t, errs, "FloatTest.Val2", "Val2", "gtfield") errs = validate.FieldWithValue(float32(1), float32(5), "gtfield") Equal(t, errs, nil) errs = validate.FieldWithValue(float32(5), float32(1), "gtfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtfield") errs = validate.FieldWithValue(nil, 1, "gtfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtfield") errs = validate.FieldWithValue(5, "T", "gtfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtfield") errs = validate.FieldWithValue(5, start, "gtfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtfield") type TimeTest2 struct { Start *time.Time `validate:"required"` End *time.Time `validate:"required,gtfield=NonExistantField"` } timeTest2 := &TimeTest2{ Start: &start, End: &end, } errs = validate.Struct(timeTest2) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest2.End", "End", "gtfield") } func TestLtField(t *testing.T) { type TimeTest struct { Start *time.Time `validate:"required,lt,ltfield=End"` End *time.Time `validate:"required,lt"` } now := time.Now() start := now.Add(time.Hour * 24 * -1 * 2) end := start.Add(time.Hour * 24) timeTest := &TimeTest{ Start: &start, End: &end, } errs := validate.Struct(timeTest) Equal(t, errs, nil) timeTest = &TimeTest{ Start: &end, End: &start, } errs = validate.Struct(timeTest) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest.Start", "Start", "ltfield") errs = validate.FieldWithValue(&end, &start, "ltfield") Equal(t, errs, nil) errs = validate.FieldWithValue(&start, &end, "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") errs = validate.FieldWithValue(timeTest, &end, "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") errs = validate.FieldWithValue("test", "tes", "ltfield") Equal(t, errs, nil) type IntTest struct { Val1 int `validate:"required"` Val2 int `validate:"required,ltfield=Val1"` } intTest := &IntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(intTest) Equal(t, errs, nil) intTest = &IntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(intTest) NotEqual(t, errs, nil) AssertError(t, errs, "IntTest.Val2", "Val2", "ltfield") errs = validate.FieldWithValue(int(5), int(1), "ltfield") Equal(t, errs, nil) errs = validate.FieldWithValue(int(1), int(5), "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") type UIntTest struct { Val1 uint `validate:"required"` Val2 uint `validate:"required,ltfield=Val1"` } uIntTest := &UIntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(uIntTest) Equal(t, errs, nil) uIntTest = &UIntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(uIntTest) NotEqual(t, errs, nil) AssertError(t, errs, "UIntTest.Val2", "Val2", "ltfield") errs = validate.FieldWithValue(uint(5), uint(1), "ltfield") Equal(t, errs, nil) errs = validate.FieldWithValue(uint(1), uint(5), "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") type FloatTest struct { Val1 float64 `validate:"required"` Val2 float64 `validate:"required,ltfield=Val1"` } floatTest := &FloatTest{ Val1: 5, Val2: 1, } errs = validate.Struct(floatTest) Equal(t, errs, nil) floatTest = &FloatTest{ Val1: 1, Val2: 5, } errs = validate.Struct(floatTest) NotEqual(t, errs, nil) AssertError(t, errs, "FloatTest.Val2", "Val2", "ltfield") errs = validate.FieldWithValue(float32(5), float32(1), "ltfield") Equal(t, errs, nil) errs = validate.FieldWithValue(float32(1), float32(5), "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") errs = validate.FieldWithValue(nil, 5, "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") errs = validate.FieldWithValue(1, "T", "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") errs = validate.FieldWithValue(1, end, "ltfield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltfield") type TimeTest2 struct { Start *time.Time `validate:"required"` End *time.Time `validate:"required,ltfield=NonExistantField"` } timeTest2 := &TimeTest2{ Start: &end, End: &start, } errs = validate.Struct(timeTest2) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest2.End", "End", "ltfield") } func TestLteField(t *testing.T) { type TimeTest struct { Start *time.Time `validate:"required,lte,ltefield=End"` End *time.Time `validate:"required,lte"` } now := time.Now() start := now.Add(time.Hour * 24 * -1 * 2) end := start.Add(time.Hour * 24) timeTest := &TimeTest{ Start: &start, End: &end, } errs := validate.Struct(timeTest) Equal(t, errs, nil) timeTest = &TimeTest{ Start: &end, End: &start, } errs = validate.Struct(timeTest) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest.Start", "Start", "ltefield") errs = validate.FieldWithValue(&end, &start, "ltefield") Equal(t, errs, nil) errs = validate.FieldWithValue(&start, &end, "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") errs = validate.FieldWithValue(timeTest, &end, "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") errs = validate.FieldWithValue("test", "tes", "ltefield") Equal(t, errs, nil) errs = validate.FieldWithValue("test", "test", "ltefield") Equal(t, errs, nil) type IntTest struct { Val1 int `validate:"required"` Val2 int `validate:"required,ltefield=Val1"` } intTest := &IntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(intTest) Equal(t, errs, nil) intTest = &IntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(intTest) NotEqual(t, errs, nil) AssertError(t, errs, "IntTest.Val2", "Val2", "ltefield") errs = validate.FieldWithValue(int(5), int(1), "ltefield") Equal(t, errs, nil) errs = validate.FieldWithValue(int(1), int(5), "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") type UIntTest struct { Val1 uint `validate:"required"` Val2 uint `validate:"required,ltefield=Val1"` } uIntTest := &UIntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(uIntTest) Equal(t, errs, nil) uIntTest = &UIntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(uIntTest) NotEqual(t, errs, nil) AssertError(t, errs, "UIntTest.Val2", "Val2", "ltefield") errs = validate.FieldWithValue(uint(5), uint(1), "ltefield") Equal(t, errs, nil) errs = validate.FieldWithValue(uint(1), uint(5), "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") type FloatTest struct { Val1 float64 `validate:"required"` Val2 float64 `validate:"required,ltefield=Val1"` } floatTest := &FloatTest{ Val1: 5, Val2: 1, } errs = validate.Struct(floatTest) Equal(t, errs, nil) floatTest = &FloatTest{ Val1: 1, Val2: 5, } errs = validate.Struct(floatTest) NotEqual(t, errs, nil) AssertError(t, errs, "FloatTest.Val2", "Val2", "ltefield") errs = validate.FieldWithValue(float32(5), float32(1), "ltefield") Equal(t, errs, nil) errs = validate.FieldWithValue(float32(1), float32(5), "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") errs = validate.FieldWithValue(nil, 5, "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") errs = validate.FieldWithValue(1, "T", "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") errs = validate.FieldWithValue(1, end, "ltefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "ltefield") type TimeTest2 struct { Start *time.Time `validate:"required"` End *time.Time `validate:"required,ltefield=NonExistantField"` } timeTest2 := &TimeTest2{ Start: &end, End: &start, } errs = validate.Struct(timeTest2) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest2.End", "End", "ltefield") } func TestGteField(t *testing.T) { type TimeTest struct { Start *time.Time `validate:"required,gte"` End *time.Time `validate:"required,gte,gtefield=Start"` } now := time.Now() start := now.Add(time.Hour * 24) end := start.Add(time.Hour * 24) timeTest := &TimeTest{ Start: &start, End: &end, } errs := validate.Struct(timeTest) Equal(t, errs, nil) timeTest = &TimeTest{ Start: &end, End: &start, } errs = validate.Struct(timeTest) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest.End", "End", "gtefield") errs = validate.FieldWithValue(&start, &end, "gtefield") Equal(t, errs, nil) errs = validate.FieldWithValue(&end, &start, "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") errs = validate.FieldWithValue(timeTest, &start, "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") errs = validate.FieldWithValue("test", "test", "gtefield") Equal(t, errs, nil) errs = validate.FieldWithValue("test", "test bigger", "gtefield") Equal(t, errs, nil) type IntTest struct { Val1 int `validate:"required"` Val2 int `validate:"required,gtefield=Val1"` } intTest := &IntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(intTest) Equal(t, errs, nil) intTest = &IntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(intTest) NotEqual(t, errs, nil) AssertError(t, errs, "IntTest.Val2", "Val2", "gtefield") errs = validate.FieldWithValue(int(1), int(5), "gtefield") Equal(t, errs, nil) errs = validate.FieldWithValue(int(5), int(1), "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") type UIntTest struct { Val1 uint `validate:"required"` Val2 uint `validate:"required,gtefield=Val1"` } uIntTest := &UIntTest{ Val1: 1, Val2: 5, } errs = validate.Struct(uIntTest) Equal(t, errs, nil) uIntTest = &UIntTest{ Val1: 5, Val2: 1, } errs = validate.Struct(uIntTest) NotEqual(t, errs, nil) AssertError(t, errs, "UIntTest.Val2", "Val2", "gtefield") errs = validate.FieldWithValue(uint(1), uint(5), "gtefield") Equal(t, errs, nil) errs = validate.FieldWithValue(uint(5), uint(1), "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") type FloatTest struct { Val1 float64 `validate:"required"` Val2 float64 `validate:"required,gtefield=Val1"` } floatTest := &FloatTest{ Val1: 1, Val2: 5, } errs = validate.Struct(floatTest) Equal(t, errs, nil) floatTest = &FloatTest{ Val1: 5, Val2: 1, } errs = validate.Struct(floatTest) NotEqual(t, errs, nil) AssertError(t, errs, "FloatTest.Val2", "Val2", "gtefield") errs = validate.FieldWithValue(float32(1), float32(5), "gtefield") Equal(t, errs, nil) errs = validate.FieldWithValue(float32(5), float32(1), "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") errs = validate.FieldWithValue(nil, 1, "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") errs = validate.FieldWithValue(5, "T", "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") errs = validate.FieldWithValue(5, start, "gtefield") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gtefield") type TimeTest2 struct { Start *time.Time `validate:"required"` End *time.Time `validate:"required,gtefield=NonExistantField"` } timeTest2 := &TimeTest2{ Start: &start, End: &end, } errs = validate.Struct(timeTest2) NotEqual(t, errs, nil) AssertError(t, errs, "TimeTest2.End", "End", "gtefield") } func TestValidateByTagAndValue(t *testing.T) { val := "test" field := "test" errs := validate.FieldWithValue(val, field, "required") Equal(t, errs, nil) fn := func(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return current.String() == field.String() } validate.RegisterValidation("isequaltestfunc", fn) errs = validate.FieldWithValue(val, field, "isequaltestfunc") Equal(t, errs, nil) val = "unequal" errs = validate.FieldWithValue(val, field, "isequaltestfunc") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "isequaltestfunc") } func TestAddFunctions(t *testing.T) { fn := func(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool { return true } config := &Config{ TagName: "validateme", } validate := New(config) errs := validate.RegisterValidation("new", fn) Equal(t, errs, nil) errs = validate.RegisterValidation("", fn) NotEqual(t, errs, nil) validate.RegisterValidation("new", nil) NotEqual(t, errs, nil) errs = validate.RegisterValidation("new", fn) Equal(t, errs, nil) PanicMatches(t, func() { validate.RegisterValidation("dive", fn) }, "Tag 'dive' either contains restricted characters or is the same as a restricted tag needed for normal operation") } func TestChangeTag(t *testing.T) { config := &Config{ TagName: "val", } validate := New(config) type Test struct { Name string `val:"len=4"` } s := &Test{ Name: "TEST", } errs := validate.Struct(s) Equal(t, errs, nil) } func TestUnexposedStruct(t *testing.T) { type Test struct { Name string unexposed struct { A string `validate:"required"` } } s := &Test{ Name: "TEST", } errs := validate.Struct(s) Equal(t, errs, nil) } func TestBadParams(t *testing.T) { i := 1 errs := validate.Field(i, "-") Equal(t, errs, nil) PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") PanicMatches(t, func() { validate.Field(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") var ui uint = 1 PanicMatches(t, func() { validate.Field(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") f := 1.23 PanicMatches(t, func() { validate.Field(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") } func TestLength(t *testing.T) { i := true PanicMatches(t, func() { validate.Field(i, "len") }, "Bad field type bool") } func TestIsGt(t *testing.T) { myMap := map[string]string{} errs := validate.Field(myMap, "gt=0") NotEqual(t, errs, nil) f := 1.23 errs = validate.Field(f, "gt=5") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gt") var ui uint = 5 errs = validate.Field(ui, "gt=10") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gt") i := true PanicMatches(t, func() { validate.Field(i, "gt") }, "Bad field type bool") tm := time.Now().UTC() tm = tm.Add(time.Hour * 24) errs = validate.Field(tm, "gt") Equal(t, errs, nil) t2 := time.Now().UTC() errs = validate.Field(t2, "gt") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gt") type Test struct { Now *time.Time `validate:"gt"` } s := &Test{ Now: &tm, } errs = validate.Struct(s) Equal(t, errs, nil) s = &Test{ Now: &t2, } errs = validate.Struct(s) NotEqual(t, errs, nil) AssertError(t, errs, "Test.Now", "Now", "gt") } func TestIsGte(t *testing.T) { i := true PanicMatches(t, func() { validate.Field(i, "gte") }, "Bad field type bool") t1 := time.Now().UTC() t1 = t1.Add(time.Hour * 24) errs := validate.Field(t1, "gte") Equal(t, errs, nil) t2 := time.Now().UTC() errs = validate.Field(t2, "gte") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "gte") type Test struct { Now *time.Time `validate:"gte"` } s := &Test{ Now: &t1, } errs = validate.Struct(s) Equal(t, errs, nil) s = &Test{ Now: &t2, } errs = validate.Struct(s) NotEqual(t, errs, nil) AssertError(t, errs, "Test.Now", "Now", "gte") } func TestIsLt(t *testing.T) { myMap := map[string]string{} errs := validate.Field(myMap, "lt=0") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "lt") f := 1.23 errs = validate.Field(f, "lt=0") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "lt") var ui uint = 5 errs = validate.Field(ui, "lt=0") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "lt") i := true PanicMatches(t, func() { validate.Field(i, "lt") }, "Bad field type bool") t1 := time.Now().UTC() errs = validate.Field(t1, "lt") Equal(t, errs, nil) t2 := time.Now().UTC() t2 = t2.Add(time.Hour * 24) errs = validate.Field(t2, "lt") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "lt") type Test struct { Now *time.Time `validate:"lt"` } s := &Test{ Now: &t1, } errs = validate.Struct(s) Equal(t, errs, nil) s = &Test{ Now: &t2, } errs = validate.Struct(s) NotEqual(t, errs, nil) AssertError(t, errs, "Test.Now", "Now", "lt") } func TestIsLte(t *testing.T) { i := true PanicMatches(t, func() { validate.Field(i, "lte") }, "Bad field type bool") t1 := time.Now().UTC() errs := validate.Field(t1, "lte") Equal(t, errs, nil) t2 := time.Now().UTC() t2 = t2.Add(time.Hour * 24) errs = validate.Field(t2, "lte") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "lte") type Test struct { Now *time.Time `validate:"lte"` } s := &Test{ Now: &t1, } errs = validate.Struct(s) Equal(t, errs, nil) s = &Test{ Now: &t2, } errs = validate.Struct(s) NotEqual(t, errs, nil) } func TestUrl(t *testing.T) { var tests = []struct { param string expected bool }{ {"http://foo.bar#com", true}, {"http://foobar.com", true}, {"https://foobar.com", true}, {"foobar.com", false}, {"http://foobar.coffee/", true}, {"http://foobar.中文网/", true}, {"http://foobar.org/", true}, {"http://foobar.org:8080/", true}, {"ftp://foobar.ru/", true}, {"http://user:pass@www.foobar.com/", true}, {"http://127.0.0.1/", true}, {"http://duckduckgo.com/?q=%2F", true}, {"http://localhost:3000/", true}, {"http://foobar.com/?foo=bar#baz=qux", true}, {"http://foobar.com?foo=bar", true}, {"http://www.xn--froschgrn-x9a.net/", true}, {"", false}, {"xyz://foobar.com", true}, {"invalid.", false}, {".com", false}, {"rtmp://foobar.com", true}, {"http://www.foo_bar.com/", true}, {"http://localhost:3000/", true}, {"http://foobar.com/#baz", true}, {"http://foobar.com#baz=qux", true}, {"http://foobar.com/t$-_.+!*\\'(),", true}, {"http://www.foobar.com/~foobar", true}, {"http://www.-foobar.com/", true}, {"http://www.foo---bar.com/", true}, {"mailto:someone@example.com", true}, {"irc://irc.server.org/channel", true}, {"irc://#channel@network", true}, {"/abs/test/dir", false}, {"./rel/test/dir", false}, } for i, test := range tests { errs := validate.Field(test.param, "url") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d URL failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d URL failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "url" { t.Fatalf("Index: %d URL failed Error: %s", i, errs) } } } } i := 1 PanicMatches(t, func() { validate.Field(i, "url") }, "Bad field type int") } func TestUri(t *testing.T) { var tests = []struct { param string expected bool }{ {"http://foo.bar#com", true}, {"http://foobar.com", true}, {"https://foobar.com", true}, {"foobar.com", false}, {"http://foobar.coffee/", true}, {"http://foobar.中文网/", true}, {"http://foobar.org/", true}, {"http://foobar.org:8080/", true}, {"ftp://foobar.ru/", true}, {"http://user:pass@www.foobar.com/", true}, {"http://127.0.0.1/", true}, {"http://duckduckgo.com/?q=%2F", true}, {"http://localhost:3000/", true}, {"http://foobar.com/?foo=bar#baz=qux", true}, {"http://foobar.com?foo=bar", true}, {"http://www.xn--froschgrn-x9a.net/", true}, {"", false}, {"xyz://foobar.com", true}, {"invalid.", false}, {".com", false}, {"rtmp://foobar.com", true}, {"http://www.foo_bar.com/", true}, {"http://localhost:3000/", true}, {"http://foobar.com#baz=qux", true}, {"http://foobar.com/t$-_.+!*\\'(),", true}, {"http://www.foobar.com/~foobar", true}, {"http://www.-foobar.com/", true}, {"http://www.foo---bar.com/", true}, {"mailto:someone@example.com", true}, {"irc://irc.server.org/channel", true}, {"irc://#channel@network", true}, {"/abs/test/dir", true}, {"./rel/test/dir", false}, } for i, test := range tests { errs := validate.Field(test.param, "uri") if test.expected { if !IsEqual(errs, nil) { t.Fatalf("Index: %d URI failed Error: %s", i, errs) } } else { if IsEqual(errs, nil) { t.Fatalf("Index: %d URI failed Error: %s", i, errs) } else { val := errs.(ValidationErrors)[""] if val.Tag != "uri" { t.Fatalf("Index: %d URI failed Error: %s", i, errs) } } } } i := 1 PanicMatches(t, func() { validate.Field(i, "uri") }, "Bad field type int") } func TestOrTag(t *testing.T) { s := "rgba(0,31,255,0.5)" errs := validate.Field(s, "rgb|rgba") Equal(t, errs, nil) s = "rgba(0,31,255,0.5)" errs = validate.Field(s, "rgb|rgba|len=18") Equal(t, errs, nil) s = "this ain't right" errs = validate.Field(s, "rgb|rgba") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgb|rgba") s = "this ain't right" errs = validate.Field(s, "rgb|rgba|len=10") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgb|rgba|len") s = "this is right" errs = validate.Field(s, "rgb|rgba|len=13") Equal(t, errs, nil) s = "" errs = validate.Field(s, "omitempty,rgb|rgba") Equal(t, errs, nil) s = "this is right, but a blank or isn't" PanicMatches(t, func() { validate.Field(s, "rgb||len=13") }, "Invalid validation tag on field") PanicMatches(t, func() { validate.Field(s, "rgb|rgbaa|len=13") }, "Undefined validation function on field") } func TestHsla(t *testing.T) { s := "hsla(360,100%,100%,1)" errs := validate.Field(s, "hsla") Equal(t, errs, nil) s = "hsla(360,100%,100%,0.5)" errs = validate.Field(s, "hsla") Equal(t, errs, nil) s = "hsla(0,0%,0%, 0)" errs = validate.Field(s, "hsla") Equal(t, errs, nil) s = "hsl(361,100%,50%,1)" errs = validate.Field(s, "hsla") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsla") s = "hsl(361,100%,50%)" errs = validate.Field(s, "hsla") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsla") s = "hsla(361,100%,50%)" errs = validate.Field(s, "hsla") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsla") s = "hsla(360,101%,50%)" errs = validate.Field(s, "hsla") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsla") s = "hsla(360,100%,101%)" errs = validate.Field(s, "hsla") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsla") i := 1 validate.Field(i, "hsla") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsla") } func TestHsl(t *testing.T) { s := "hsl(360,100%,50%)" errs := validate.Field(s, "hsl") Equal(t, errs, nil) s = "hsl(0,0%,0%)" errs = validate.Field(s, "hsl") Equal(t, errs, nil) s = "hsl(361,100%,50%)" errs = validate.Field(s, "hsl") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsl") s = "hsl(361,101%,50%)" errs = validate.Field(s, "hsl") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsl") s = "hsl(361,100%,101%)" errs = validate.Field(s, "hsl") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsl") s = "hsl(-10,100%,100%)" errs = validate.Field(s, "hsl") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsl") i := 1 errs = validate.Field(i, "hsl") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hsl") } func TestRgba(t *testing.T) { s := "rgba(0,31,255,0.5)" errs := validate.Field(s, "rgba") Equal(t, errs, nil) s = "rgba(0,31,255,0.12)" errs = validate.Field(s, "rgba") Equal(t, errs, nil) s = "rgba(12%,55%,100%,0.12)" errs = validate.Field(s, "rgba") Equal(t, errs, nil) s = "rgba( 0, 31, 255, 0.5)" errs = validate.Field(s, "rgba") Equal(t, errs, nil) s = "rgba(12%,55,100%,0.12)" errs = validate.Field(s, "rgba") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgba") s = "rgb(0, 31, 255)" errs = validate.Field(s, "rgba") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgba") s = "rgb(1,349,275,0.5)" errs = validate.Field(s, "rgba") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgba") s = "rgb(01,31,255,0.5)" errs = validate.Field(s, "rgba") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgba") i := 1 errs = validate.Field(i, "rgba") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgba") } func TestRgb(t *testing.T) { s := "rgb(0,31,255)" errs := validate.Field(s, "rgb") Equal(t, errs, nil) s = "rgb(0, 31, 255)" errs = validate.Field(s, "rgb") Equal(t, errs, nil) s = "rgb(10%, 50%, 100%)" errs = validate.Field(s, "rgb") Equal(t, errs, nil) s = "rgb(10%, 50%, 55)" errs = validate.Field(s, "rgb") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgb") s = "rgb(1,349,275)" errs = validate.Field(s, "rgb") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgb") s = "rgb(01,31,255)" errs = validate.Field(s, "rgb") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgb") s = "rgba(0,31,255)" errs = validate.Field(s, "rgb") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgb") i := 1 errs = validate.Field(i, "rgb") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "rgb") } func TestEmail(t *testing.T) { s := "test@mail.com" errs := validate.Field(s, "email") Equal(t, errs, nil) s = "Dörte@Sörensen.example.com" errs = validate.Field(s, "email") Equal(t, errs, nil) s = "θσερ@εχαμπλε.ψομ" errs = validate.Field(s, "email") Equal(t, errs, nil) s = "юзер@екзампл.ком" errs = validate.Field(s, "email") Equal(t, errs, nil) s = "उपयोगकर्ता@उदाहरण.कॉम" errs = validate.Field(s, "email") Equal(t, errs, nil) s = "用户@例子.广告" errs = validate.Field(s, "email") Equal(t, errs, nil) s = "" errs = validate.Field(s, "email") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "email") s = "test@email" errs = validate.Field(s, "email") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "email") s = "test@email." errs = validate.Field(s, "email") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "email") s = "@email.com" errs = validate.Field(s, "email") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "email") i := true errs = validate.Field(i, "email") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "email") } func TestHexColor(t *testing.T) { s := "#fff" errs := validate.Field(s, "hexcolor") Equal(t, errs, nil) s = "#c2c2c2" errs = validate.Field(s, "hexcolor") Equal(t, errs, nil) s = "fff" errs = validate.Field(s, "hexcolor") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hexcolor") s = "fffFF" errs = validate.Field(s, "hexcolor") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hexcolor") i := true errs = validate.Field(i, "hexcolor") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hexcolor") } func TestHexadecimal(t *testing.T) { s := "ff0044" errs := validate.Field(s, "hexadecimal") Equal(t, errs, nil) s = "abcdefg" errs = validate.Field(s, "hexadecimal") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hexadecimal") i := true errs = validate.Field(i, "hexadecimal") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "hexadecimal") } func TestNumber(t *testing.T) { s := "1" errs := validate.Field(s, "number") Equal(t, errs, nil) s = "+1" errs = validate.Field(s, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") s = "-1" errs = validate.Field(s, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") s = "1.12" errs = validate.Field(s, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") s = "+1.12" errs = validate.Field(s, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") s = "-1.12" errs = validate.Field(s, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") s = "1." errs = validate.Field(s, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") s = "1.o" errs = validate.Field(s, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") i := 1 errs = validate.Field(i, "number") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "number") } func TestNumeric(t *testing.T) { s := "1" errs := validate.Field(s, "numeric") Equal(t, errs, nil) s = "+1" errs = validate.Field(s, "numeric") Equal(t, errs, nil) s = "-1" errs = validate.Field(s, "numeric") Equal(t, errs, nil) s = "1.12" errs = validate.Field(s, "numeric") Equal(t, errs, nil) s = "+1.12" errs = validate.Field(s, "numeric") Equal(t, errs, nil) s = "-1.12" errs = validate.Field(s, "numeric") Equal(t, errs, nil) s = "1." errs = validate.Field(s, "numeric") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "numeric") s = "1.o" errs = validate.Field(s, "numeric") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "numeric") i := 1 errs = validate.Field(i, "numeric") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "numeric") } func TestAlphaNumeric(t *testing.T) { s := "abcd123" errs := validate.Field(s, "alphanum") Equal(t, errs, nil) s = "abc!23" errs = validate.Field(s, "alphanum") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "alphanum") errs = validate.Field(1, "alphanum") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "alphanum") } func TestAlpha(t *testing.T) { s := "abcd" errs := validate.Field(s, "alpha") Equal(t, errs, nil) s = "abc®" errs = validate.Field(s, "alpha") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "alpha") s = "abc÷" errs = validate.Field(s, "alpha") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "alpha") s = "abc1" errs = validate.Field(s, "alpha") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "alpha") errs = validate.Field(1, "alpha") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "alpha") } func TestStructStringValidation(t *testing.T) { tSuccess := &TestString{ Required: "Required", Len: "length==10", Min: "min=1", Max: "1234567890", MinMax: "12345", Lt: "012345678", Lte: "0123456789", Gt: "01234567890", Gte: "0123456789", OmitEmpty: "", Sub: &SubTest{ Test: "1", }, SubIgnore: &SubTest{ Test: "", }, Anonymous: struct { A string `validate:"required"` }{ A: "1", }, Iface: &Impl{ F: "123", }, } errs := validate.Struct(tSuccess) Equal(t, errs, nil) tFail := &TestString{ Required: "", Len: "", Min: "", Max: "12345678901", MinMax: "", Lt: "0123456789", Lte: "01234567890", Gt: "1", Gte: "1", OmitEmpty: "12345678901", Sub: &SubTest{ Test: "", }, Anonymous: struct { A string `validate:"required"` }{ A: "", }, Iface: &Impl{ F: "12", }, } errs = validate.Struct(tFail) // Assert Top Level NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 13) // Assert Fields AssertError(t, errs, "TestString.Required", "Required", "required") AssertError(t, errs, "TestString.Len", "Len", "len") AssertError(t, errs, "TestString.Min", "Min", "min") AssertError(t, errs, "TestString.Max", "Max", "max") AssertError(t, errs, "TestString.MinMax", "MinMax", "min") AssertError(t, errs, "TestString.Lt", "Lt", "lt") AssertError(t, errs, "TestString.Lte", "Lte", "lte") AssertError(t, errs, "TestString.Gt", "Gt", "gt") AssertError(t, errs, "TestString.Gte", "Gte", "gte") AssertError(t, errs, "TestString.OmitEmpty", "OmitEmpty", "max") // Nested Struct Field Errs AssertError(t, errs, "TestString.Anonymous.A", "A", "required") AssertError(t, errs, "TestString.Sub.Test", "Test", "required") AssertError(t, errs, "TestString.Iface.F", "F", "len") } func TestStructInt32Validation(t *testing.T) { tSuccess := &TestInt32{ Required: 1, Len: 10, Min: 1, Max: 10, MinMax: 5, Lt: 9, Lte: 10, Gt: 11, Gte: 10, OmitEmpty: 0, } errs := validate.Struct(tSuccess) Equal(t, errs, nil) tFail := &TestInt32{ Required: 0, Len: 11, Min: -1, Max: 11, MinMax: -1, Lt: 10, Lte: 11, Gt: 10, Gte: 9, OmitEmpty: 11, } errs = validate.Struct(tFail) // Assert Top Level NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 10) // Assert Fields AssertError(t, errs, "TestInt32.Required", "Required", "required") AssertError(t, errs, "TestInt32.Len", "Len", "len") AssertError(t, errs, "TestInt32.Min", "Min", "min") AssertError(t, errs, "TestInt32.Max", "Max", "max") AssertError(t, errs, "TestInt32.MinMax", "MinMax", "min") AssertError(t, errs, "TestInt32.Lt", "Lt", "lt") AssertError(t, errs, "TestInt32.Lte", "Lte", "lte") AssertError(t, errs, "TestInt32.Gt", "Gt", "gt") AssertError(t, errs, "TestInt32.Gte", "Gte", "gte") AssertError(t, errs, "TestInt32.OmitEmpty", "OmitEmpty", "max") } func TestStructUint64Validation(t *testing.T) { tSuccess := &TestUint64{ Required: 1, Len: 10, Min: 1, Max: 10, MinMax: 5, OmitEmpty: 0, } errs := validate.Struct(tSuccess) Equal(t, errs, nil) tFail := &TestUint64{ Required: 0, Len: 11, Min: 0, Max: 11, MinMax: 0, OmitEmpty: 11, } errs = validate.Struct(tFail) // Assert Top Level NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 6) // Assert Fields AssertError(t, errs, "TestUint64.Required", "Required", "required") AssertError(t, errs, "TestUint64.Len", "Len", "len") AssertError(t, errs, "TestUint64.Min", "Min", "min") AssertError(t, errs, "TestUint64.Max", "Max", "max") AssertError(t, errs, "TestUint64.MinMax", "MinMax", "min") AssertError(t, errs, "TestUint64.OmitEmpty", "OmitEmpty", "max") } func TestStructFloat64Validation(t *testing.T) { tSuccess := &TestFloat64{ Required: 1, Len: 10, Min: 1, Max: 10, MinMax: 5, OmitEmpty: 0, } errs := validate.Struct(tSuccess) Equal(t, errs, nil) tFail := &TestFloat64{ Required: 0, Len: 11, Min: 0, Max: 11, MinMax: 0, OmitEmpty: 11, } errs = validate.Struct(tFail) // Assert Top Level NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 6) // Assert Fields AssertError(t, errs, "TestFloat64.Required", "Required", "required") AssertError(t, errs, "TestFloat64.Len", "Len", "len") AssertError(t, errs, "TestFloat64.Min", "Min", "min") AssertError(t, errs, "TestFloat64.Max", "Max", "max") AssertError(t, errs, "TestFloat64.MinMax", "MinMax", "min") AssertError(t, errs, "TestFloat64.OmitEmpty", "OmitEmpty", "max") } func TestStructSliceValidation(t *testing.T) { tSuccess := &TestSlice{ Required: []int{1}, Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, Min: []int{1, 2}, Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, MinMax: []int{1, 2, 3, 4, 5}, OmitEmpty: nil, } errs := validate.Struct(tSuccess) Equal(t, errs, nil) tFail := &TestSlice{ Required: nil, Len: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, Min: []int{}, Max: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, MinMax: []int{}, OmitEmpty: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}, } errs = validate.Struct(tFail) NotEqual(t, errs, nil) Equal(t, len(errs.(ValidationErrors)), 6) // Assert Field Errors AssertError(t, errs, "TestSlice.Required", "Required", "required") AssertError(t, errs, "TestSlice.Len", "Len", "len") AssertError(t, errs, "TestSlice.Min", "Min", "min") AssertError(t, errs, "TestSlice.Max", "Max", "max") AssertError(t, errs, "TestSlice.MinMax", "MinMax", "min") AssertError(t, errs, "TestSlice.OmitEmpty", "OmitEmpty", "max") } func TestInvalidStruct(t *testing.T) { s := &SubTest{ Test: "1", } PanicMatches(t, func() { validate.Struct(s.Test) }, "value passed for validation is not a struct") } func TestInvalidValidatorFunction(t *testing.T) { s := &SubTest{ Test: "1", } PanicMatches(t, func() { validate.Field(s.Test, "zzxxBadFunction") }, "Undefined validation function on field") } func TestCustomFieldName(t *testing.T) { type A struct { B string `schema:"b" validate:"required"` C string `schema:"c" validate:"required"` D []bool `schema:"d" validate:"required"` E string `schema:"-" validate:"required"` } a := &A{} errs := New(&Config{TagName: "validate", FieldNameTag: "schema"}).Struct(a).(ValidationErrors) NotEqual(t, errs, nil) Equal(t, len(errs), 4) Equal(t, errs["A.B"].Name, "b") Equal(t, errs["A.C"].Name, "c") Equal(t, errs["A.D"].Name, "d") Equal(t, errs["A.E"].Name, "E") errs = New(&Config{TagName: "validate"}).Struct(a).(ValidationErrors) NotEqual(t, errs, nil) Equal(t, len(errs), 4) Equal(t, errs["A.B"].Name, "B") Equal(t, errs["A.C"].Name, "C") Equal(t, errs["A.D"].Name, "D") Equal(t, errs["A.E"].Name, "E") } func TestMutipleRecursiveExtractStructCache(t *testing.T) { type Recursive struct { Field *string `validate:"exists,required,len=5,ne=string"` } var test Recursive current := reflect.ValueOf(test) name := "Recursive" proceed := make(chan struct{}) sc := validate.extractStructCache(current, name) ptr := fmt.Sprintf("%p", sc) for i := 0; i < 100; i++ { go func() { <-proceed sc := validate.extractStructCache(current, name) Equal(t, ptr, fmt.Sprintf("%p", sc)) }() } close(proceed) } // Thanks @robbrockbank, see https://github.com/go-playground/validator/issues/249 func TestPointerAndOmitEmpty(t *testing.T) { type Test struct { MyInt *int `validate:"omitempty,gte=2,lte=255"` } val1 := 0 val2 := 256 t1 := Test{MyInt: &val1} // This should fail validation on gte because value is 0 t2 := Test{MyInt: &val2} // This should fail validate on lte because value is 256 t3 := Test{MyInt: nil} // This should succeed validation because pointer is nil errs := validate.Struct(t1) NotEqual(t, errs, nil) AssertError(t, errs, "Test.MyInt", "MyInt", "gte") errs = validate.Struct(t2) NotEqual(t, errs, nil) AssertError(t, errs, "Test.MyInt", "MyInt", "lte") errs = validate.Struct(t3) Equal(t, errs, nil) type TestIface struct { MyInt interface{} `validate:"omitempty,gte=2,lte=255"` } ti1 := TestIface{MyInt: &val1} // This should fail validation on gte because value is 0 ti2 := TestIface{MyInt: &val2} // This should fail validate on lte because value is 256 ti3 := TestIface{MyInt: nil} // This should succeed validation because pointer is nil errs = validate.Struct(ti1) NotEqual(t, errs, nil) AssertError(t, errs, "TestIface.MyInt", "MyInt", "gte") errs = validate.Struct(ti2) NotEqual(t, errs, nil) AssertError(t, errs, "TestIface.MyInt", "MyInt", "lte") errs = validate.Struct(ti3) Equal(t, errs, nil) }