diff --git a/vendor/github.com/asaskevich/govalidator/.travis.yml b/vendor/github.com/asaskevich/govalidator/.travis.yml new file mode 100644 index 000000000..e29f8eef5 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/.travis.yml @@ -0,0 +1,14 @@ +language: go + +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - tip + +notifications: + email: + - bwatas@gmail.com diff --git a/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md b/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md new file mode 100644 index 000000000..57e723846 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md @@ -0,0 +1,26 @@ +#### Support +If you do have a contribution to the package, feel free to create a Pull Request or an Issue. + +#### What to contribute +If you don't know what to do, there are some features and functions that need to be done + +- [ ] Refactor code +- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check +- [ ] Create actual list of contributors and projects that currently using this package +- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) +- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) +- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new +- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc +- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) +- [ ] Implement fuzzing testing +- [ ] Implement some struct/map/array utilities +- [ ] Implement map/array validation +- [ ] Implement benchmarking +- [ ] Implement batch of examples +- [ ] Look at forks for new features and fixes + +#### Advice +Feel free to create what you want, but keep in mind when you implement new features: +- Code must be clear and readable, names of variables/constants clearly describes what they are doing +- Public functions must be documented and described in source file and added to README.md to the list of available functions +- There are must be unit-tests for any new functions and improvements \ No newline at end of file diff --git a/vendor/github.com/asaskevich/govalidator/LICENSE b/vendor/github.com/asaskevich/govalidator/LICENSE new file mode 100644 index 000000000..2f9a31fad --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Alex Saskevich + +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. \ No newline at end of file diff --git a/vendor/github.com/asaskevich/govalidator/README.md b/vendor/github.com/asaskevich/govalidator/README.md new file mode 100644 index 000000000..223880940 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/README.md @@ -0,0 +1,449 @@ +govalidator +=========== +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043) +[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) [![Go Report Card](https://goreportcard.com/badge/github.com/asaskevich/govalidator)](https://goreportcard.com/report/github.com/asaskevich/govalidator) [![GoSearch](http://go-search.org/badge?id=github.com%2Fasaskevich%2Fgovalidator)](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) + +A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js). + +#### Installation +Make sure that Go is installed on your computer. +Type the following command in your terminal: + + go get github.com/asaskevich/govalidator + +or you can get specified release of the package with `gopkg.in`: + + go get gopkg.in/asaskevich/govalidator.v4 + +After it the package is ready to use. + + +#### Import package in your project +Add following line in your `*.go` file: +```go +import "github.com/asaskevich/govalidator" +``` +If you are unhappy to use long `govalidator`, you can do something like this: +```go +import ( + valid "github.com/asaskevich/govalidator" +) +``` + +#### Activate behavior to require all fields have a validation tag by default +`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function. + +```go +import "github.com/asaskevich/govalidator" + +func init() { + govalidator.SetFieldsRequiredByDefault(true) +} +``` + +Here's some code to explain it: +```go +// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): +type exampleStruct struct { + Name string `` + Email string `valid:"email"` +} + +// this, however, will only fail when Email is empty or an invalid email address: +type exampleStruct2 struct { + Name string `valid:"-"` + Email string `valid:"email"` +} + +// lastly, this will only fail when Email is an invalid email address but not when it's empty: +type exampleStruct2 struct { + Name string `valid:"-"` + Email string `valid:"email,optional"` +} +``` + +#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123)) +##### Custom validator function signature +A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible. +```go +import "github.com/asaskevich/govalidator" + +// old signature +func(i interface{}) bool + +// new signature +func(i interface{}, o interface{}) bool +``` + +##### Adding a custom validator +This was changed to prevent data races when accessing custom validators. +```go +import "github.com/asaskevich/govalidator" + +// before +govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool { + // ... +}) + +// after +govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + // ... +})) +``` + +#### List of functions: +```go +func Abs(value float64) float64 +func BlackList(str, chars string) string +func ByteLength(str string, params ...string) bool +func CamelCaseToUnderscore(str string) string +func Contains(str, substring string) bool +func Count(array []interface{}, iterator ConditionIterator) int +func Each(array []interface{}, iterator Iterator) +func ErrorByField(e error, field string) string +func ErrorsByField(e error) map[string]string +func Filter(array []interface{}, iterator ConditionIterator) []interface{} +func Find(array []interface{}, iterator ConditionIterator) interface{} +func GetLine(s string, index int) (string, error) +func GetLines(s string) []string +func InRange(value, left, right float64) bool +func IsASCII(str string) bool +func IsAlpha(str string) bool +func IsAlphanumeric(str string) bool +func IsBase64(str string) bool +func IsByteLength(str string, min, max int) bool +func IsCIDR(str string) bool +func IsCreditCard(str string) bool +func IsDNSName(str string) bool +func IsDataURI(str string) bool +func IsDialString(str string) bool +func IsDivisibleBy(str, num string) bool +func IsEmail(str string) bool +func IsFilePath(str string) (bool, int) +func IsFloat(str string) bool +func IsFullWidth(str string) bool +func IsHalfWidth(str string) bool +func IsHexadecimal(str string) bool +func IsHexcolor(str string) bool +func IsHost(str string) bool +func IsIP(str string) bool +func IsIPv4(str string) bool +func IsIPv6(str string) bool +func IsISBN(str string, version int) bool +func IsISBN10(str string) bool +func IsISBN13(str string) bool +func IsISO3166Alpha2(str string) bool +func IsISO3166Alpha3(str string) bool +func IsISO693Alpha2(str string) bool +func IsISO693Alpha3b(str string) bool +func IsISO4217(str string) bool +func IsIn(str string, params ...string) bool +func IsInt(str string) bool +func IsJSON(str string) bool +func IsLatitude(str string) bool +func IsLongitude(str string) bool +func IsLowerCase(str string) bool +func IsMAC(str string) bool +func IsMongoID(str string) bool +func IsMultibyte(str string) bool +func IsNatural(value float64) bool +func IsNegative(value float64) bool +func IsNonNegative(value float64) bool +func IsNonPositive(value float64) bool +func IsNull(str string) bool +func IsNumeric(str string) bool +func IsPort(str string) bool +func IsPositive(value float64) bool +func IsPrintableASCII(str string) bool +func IsRFC3339(str string) bool +func IsRFC3339WithoutZone(str string) bool +func IsRGBcolor(str string) bool +func IsRequestURI(rawurl string) bool +func IsRequestURL(rawurl string) bool +func IsSSN(str string) bool +func IsSemver(str string) bool +func IsTime(str string, format string) bool +func IsURL(str string) bool +func IsUTFDigit(str string) bool +func IsUTFLetter(str string) bool +func IsUTFLetterNumeric(str string) bool +func IsUTFNumeric(str string) bool +func IsUUID(str string) bool +func IsUUIDv3(str string) bool +func IsUUIDv4(str string) bool +func IsUUIDv5(str string) bool +func IsUpperCase(str string) bool +func IsVariableWidth(str string) bool +func IsWhole(value float64) bool +func LeftTrim(str, chars string) string +func Map(array []interface{}, iterator ResultIterator) []interface{} +func Matches(str, pattern string) bool +func NormalizeEmail(str string) (string, error) +func PadBoth(str string, padStr string, padLen int) string +func PadLeft(str string, padStr string, padLen int) string +func PadRight(str string, padStr string, padLen int) string +func Range(str string, params ...string) bool +func RemoveTags(s string) string +func ReplacePattern(str, pattern, replace string) string +func Reverse(s string) string +func RightTrim(str, chars string) string +func RuneLength(str string, params ...string) bool +func SafeFileName(str string) string +func SetFieldsRequiredByDefault(value bool) +func Sign(value float64) float64 +func StringLength(str string, params ...string) bool +func StringMatches(s string, params ...string) bool +func StripLow(str string, keepNewLines bool) string +func ToBoolean(str string) (bool, error) +func ToFloat(str string) (float64, error) +func ToInt(str string) (int64, error) +func ToJSON(obj interface{}) (string, error) +func ToString(obj interface{}) string +func Trim(str, chars string) string +func Truncate(str string, length int, ending string) string +func UnderscoreToCamelCase(s string) string +func ValidateStruct(s interface{}) (bool, error) +func WhiteList(str, chars string) string +type ConditionIterator +type CustomTypeValidator +type Error +func (e Error) Error() string +type Errors +func (es Errors) Error() string +func (es Errors) Errors() []error +type ISO3166Entry +type Iterator +type ParamValidator +type ResultIterator +type UnsupportedTypeError +func (e *UnsupportedTypeError) Error() string +type Validator +``` + +#### Examples +###### IsURL +```go +println(govalidator.IsURL(`http://user@pass:domain.com/path/page`)) +``` +###### ToString +```go +type User struct { + FirstName string + LastName string +} + +str := govalidator.ToString(&User{"John", "Juan"}) +println(str) +``` +###### Each, Map, Filter, Count for slices +Each iterates over the slice/array and calls Iterator for every item +```go +data := []interface{}{1, 2, 3, 4, 5} +var fn govalidator.Iterator = func(value interface{}, index int) { + println(value.(int)) +} +govalidator.Each(data, fn) +``` +```go +data := []interface{}{1, 2, 3, 4, 5} +var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} { + return value.(int) * 3 +} +_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15} +``` +```go +data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} +var fn govalidator.ConditionIterator = func(value interface{}, index int) bool { + return value.(int)%2 == 0 +} +_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10} +_ = govalidator.Count(data, fn) // result = 5 +``` +###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2) +If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this: +```go +govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { + return str == "duck" +}) +``` +For completely custom validators (interface-based), see below. + +Here is a list of available validators for struct fields (validator - used function): +```go +"email": IsEmail, +"url": IsURL, +"dialstring": IsDialString, +"requrl": IsRequestURL, +"requri": IsRequestURI, +"alpha": IsAlpha, +"utfletter": IsUTFLetter, +"alphanum": IsAlphanumeric, +"utfletternum": IsUTFLetterNumeric, +"numeric": IsNumeric, +"utfnumeric": IsUTFNumeric, +"utfdigit": IsUTFDigit, +"hexadecimal": IsHexadecimal, +"hexcolor": IsHexcolor, +"rgbcolor": IsRGBcolor, +"lowercase": IsLowerCase, +"uppercase": IsUpperCase, +"int": IsInt, +"float": IsFloat, +"null": IsNull, +"uuid": IsUUID, +"uuidv3": IsUUIDv3, +"uuidv4": IsUUIDv4, +"uuidv5": IsUUIDv5, +"creditcard": IsCreditCard, +"isbn10": IsISBN10, +"isbn13": IsISBN13, +"json": IsJSON, +"multibyte": IsMultibyte, +"ascii": IsASCII, +"printableascii": IsPrintableASCII, +"fullwidth": IsFullWidth, +"halfwidth": IsHalfWidth, +"variablewidth": IsVariableWidth, +"base64": IsBase64, +"datauri": IsDataURI, +"ip": IsIP, +"port": IsPort, +"ipv4": IsIPv4, +"ipv6": IsIPv6, +"dns": IsDNSName, +"host": IsHost, +"mac": IsMAC, +"latitude": IsLatitude, +"longitude": IsLongitude, +"ssn": IsSSN, +"semver": IsSemver, +"rfc3339": IsRFC3339, +"rfc3339WithoutZone": IsRFC3339WithoutZone, +"ISO3166Alpha2": IsISO3166Alpha2, +"ISO3166Alpha3": IsISO3166Alpha3, +``` +Validators with parameters + +```go +"range(min|max)": Range, +"length(min|max)": ByteLength, +"runelength(min|max)": RuneLength, +"matches(pattern)": StringMatches, +"in(string1|string2|...|stringN)": IsIn, +``` + +And here is small example of usage: +```go +type Post struct { + Title string `valid:"alphanum,required"` + Message string `valid:"duck,ascii"` + AuthorIP string `valid:"ipv4"` + Date string `valid:"-"` +} +post := &Post{ + Title: "My Example Post", + Message: "duck", + AuthorIP: "123.234.54.3", +} + +// Add your own struct validation tags +govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { + return str == "duck" +}) + +result, err := govalidator.ValidateStruct(post) +if err != nil { + println("error: " + err.Error()) +} +println(result) +``` +###### WhiteList +```go +// Remove all characters from string ignoring characters between "a" and "z" +println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa") +``` + +###### Custom validation functions +Custom validation using your own domain specific validators is also available - here's an example of how to use it: +```go +import "github.com/asaskevich/govalidator" + +type CustomByteArray [6]byte // custom types are supported and can be validated + +type StructWithCustomByteArray struct { + ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence + Email string `valid:"email"` + CustomMinLength int `valid:"-"` +} + +govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool { + switch v := context.(type) { // you can type switch on the context interface being validated + case StructWithCustomByteArray: + // you can check and validate against some other field in the context, + // return early or not validate against the context at all – your choice + case SomeOtherType: + // ... + default: + // expecting some other type? Throw/panic here or continue + } + + switch v := i.(type) { // type switch on the struct field being validated + case CustomByteArray: + for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes + if e != 0 { + return true + } + } + } + return false +})) +govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool { + switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation + case StructWithCustomByteArray: + return len(v.ID) >= v.CustomMinLength + } + return false +})) +``` + +#### Notes +Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator). +Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator). + +#### Support +If you do have a contribution to the package, feel free to create a Pull Request or an Issue. + +#### What to contribute +If you don't know what to do, there are some features and functions that need to be done + +- [ ] Refactor code +- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check +- [ ] Create actual list of contributors and projects that currently using this package +- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) +- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) +- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new +- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc +- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) +- [ ] Implement fuzzing testing +- [ ] Implement some struct/map/array utilities +- [ ] Implement map/array validation +- [ ] Implement benchmarking +- [ ] Implement batch of examples +- [ ] Look at forks for new features and fixes + +#### Advice +Feel free to create what you want, but keep in mind when you implement new features: +- Code must be clear and readable, names of variables/constants clearly describes what they are doing +- Public functions must be documented and described in source file and added to README.md to the list of available functions +- There are must be unit-tests for any new functions and improvements + +#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors) +* [Daniel Lohse](https://github.com/annismckenzie) +* [Attila Oláh](https://github.com/attilaolah) +* [Daniel Korner](https://github.com/Dadie) +* [Steven Wilkin](https://github.com/stevenwilkin) +* [Deiwin Sarjas](https://github.com/deiwin) +* [Noah Shibley](https://github.com/slugmobile) +* [Nathan Davies](https://github.com/nathj07) +* [Matt Sanford](https://github.com/mzsanford) +* [Simon ccl1115](https://github.com/ccl1115) diff --git a/vendor/github.com/asaskevich/govalidator/arrays.go b/vendor/github.com/asaskevich/govalidator/arrays.go new file mode 100644 index 000000000..5bace2654 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/arrays.go @@ -0,0 +1,58 @@ +package govalidator + +// Iterator is the function that accepts element of slice/array and its index +type Iterator func(interface{}, int) + +// ResultIterator is the function that accepts element of slice/array and its index and returns any result +type ResultIterator func(interface{}, int) interface{} + +// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean +type ConditionIterator func(interface{}, int) bool + +// Each iterates over the slice and apply Iterator to every item +func Each(array []interface{}, iterator Iterator) { + for index, data := range array { + iterator(data, index) + } +} + +// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result. +func Map(array []interface{}, iterator ResultIterator) []interface{} { + var result = make([]interface{}, len(array)) + for index, data := range array { + result[index] = iterator(data, index) + } + return result +} + +// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise. +func Find(array []interface{}, iterator ConditionIterator) interface{} { + for index, data := range array { + if iterator(data, index) { + return data + } + } + return nil +} + +// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice. +func Filter(array []interface{}, iterator ConditionIterator) []interface{} { + var result = make([]interface{}, 0) + for index, data := range array { + if iterator(data, index) { + result = append(result, data) + } + } + return result +} + +// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator. +func Count(array []interface{}, iterator ConditionIterator) int { + count := 0 + for index, data := range array { + if iterator(data, index) { + count = count + 1 + } + } + return count +} diff --git a/vendor/github.com/asaskevich/govalidator/arrays_test.go b/vendor/github.com/asaskevich/govalidator/arrays_test.go new file mode 100644 index 000000000..1a9ac6696 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/arrays_test.go @@ -0,0 +1,116 @@ +package govalidator + +import "testing" + +func TestEach(t *testing.T) { + // TODO Maybe refactor? + t.Parallel() + acc := 0 + data := []interface{}{1, 2, 3, 4, 5} + var fn Iterator = func(value interface{}, index int) { + acc = acc + value.(int) + } + Each(data, fn) + if acc != 15 { + t.Errorf("Expected Each(..) to be %v, got %v", 15, acc) + } +} + +func ExampleEach() { + data := []interface{}{1, 2, 3, 4, 5} + var fn Iterator = func(value interface{}, index int) { + println(value.(int)) + } + Each(data, fn) +} + +func TestMap(t *testing.T) { + // TODO Maybe refactor? + t.Parallel() + data := []interface{}{1, 2, 3, 4, 5} + var fn ResultIterator = func(value interface{}, index int) interface{} { + return value.(int) * 3 + } + result := Map(data, fn) + for i, d := range result { + if d != fn(data[i], i) { + t.Errorf("Expected Map(..) to be %v, got %v", fn(data[i], i), d) + } + } +} + +func ExampleMap() { + data := []interface{}{1, 2, 3, 4, 5} + var fn ResultIterator = func(value interface{}, index int) interface{} { + return value.(int) * 3 + } + _ = Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15} +} + +func TestFind(t *testing.T) { + // TODO Maybe refactor? + t.Parallel() + findElement := 96 + data := []interface{}{1, 2, 3, 4, findElement, 5} + var fn1 ConditionIterator = func(value interface{}, index int) bool { + return value.(int) == findElement + } + var fn2 ConditionIterator = func(value interface{}, index int) bool { + value, _ = value.(string) + return value == "govalidator" + } + val1 := Find(data, fn1) + val2 := Find(data, fn2) + if val1 != findElement { + t.Errorf("Expected Find(..) to be %v, got %v", findElement, val1) + } + if val2 != nil { + t.Errorf("Expected Find(..) to be %v, got %v", nil, val2) + } +} + +func TestFilter(t *testing.T) { + // TODO Maybe refactor? + t.Parallel() + data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + answer := []interface{}{2, 4, 6, 8, 10} + var fn ConditionIterator = func(value interface{}, index int) bool { + return value.(int)%2 == 0 + } + result := Filter(data, fn) + for i := range result { + if result[i] != answer[i] { + t.Errorf("Expected Filter(..) to be %v, got %v", answer[i], result[i]) + } + } +} + +func ExampleFilter() { + data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + var fn ConditionIterator = func(value interface{}, index int) bool { + return value.(int)%2 == 0 + } + _ = Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10} +} + +func TestCount(t *testing.T) { + // TODO Maybe refactor? + t.Parallel() + data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + count := 5 + var fn ConditionIterator = func(value interface{}, index int) bool { + return value.(int)%2 == 0 + } + result := Count(data, fn) + if result != count { + t.Errorf("Expected Count(..) to be %v, got %v", count, result) + } +} + +func ExampleCount() { + data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + var fn ConditionIterator = func(value interface{}, index int) bool { + return value.(int)%2 == 0 + } + _ = Count(data, fn) // result = 5 +} diff --git a/vendor/github.com/asaskevich/govalidator/converter.go b/vendor/github.com/asaskevich/govalidator/converter.go new file mode 100644 index 000000000..d69114c4b --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/converter.go @@ -0,0 +1,45 @@ +package govalidator + +import ( + "encoding/json" + "fmt" + "strconv" +) + +// ToString convert the input to a string. +func ToString(obj interface{}) string { + res := fmt.Sprintf("%v", obj) + return string(res) +} + +// ToJSON convert the input to a valid JSON string +func ToJSON(obj interface{}) (string, error) { + res, err := json.Marshal(obj) + if err != nil { + res = []byte("") + } + return string(res), err +} + +// ToFloat convert the input string to a float, or 0.0 if the input is not a float. +func ToFloat(str string) (float64, error) { + res, err := strconv.ParseFloat(str, 64) + if err != nil { + res = 0.0 + } + return res, err +} + +// ToInt convert the input string to an integer, or 0 if the input is not an integer. +func ToInt(str string) (int64, error) { + res, err := strconv.ParseInt(str, 0, 64) + if err != nil { + res = 0 + } + return res, err +} + +// ToBoolean convert the input string to a boolean. +func ToBoolean(str string) (bool, error) { + return strconv.ParseBool(str) +} diff --git a/vendor/github.com/asaskevich/govalidator/converter_test.go b/vendor/github.com/asaskevich/govalidator/converter_test.go new file mode 100644 index 000000000..ecc457be8 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/converter_test.go @@ -0,0 +1,78 @@ +package govalidator + +import ( + "fmt" + "testing" +) + +func TestToInt(t *testing.T) { + tests := []string{"1000", "-123", "abcdef", "100000000000000000000000000000000000000000000"} + expected := []int64{1000, -123, 0, 0} + for i := 0; i < len(tests); i++ { + result, _ := ToInt(tests[i]) + if result != expected[i] { + t.Log("Case ", i, ": expected ", expected[i], " when result is ", result) + t.FailNow() + } + } +} + +func TestToBoolean(t *testing.T) { + tests := []string{"true", "1", "True", "false", "0", "abcdef"} + expected := []bool{true, true, true, false, false, false} + for i := 0; i < len(tests); i++ { + res, _ := ToBoolean(tests[i]) + if res != expected[i] { + t.Log("Case ", i, ": expected ", expected[i], " when result is ", res) + t.FailNow() + } + } +} + +func toString(t *testing.T, test interface{}, expected string) { + res := ToString(test) + if res != expected { + t.Log("Case ToString: expected ", expected, " when result is ", res) + t.FailNow() + } +} + +func TestToString(t *testing.T) { + toString(t, "str123", "str123") + toString(t, 123, "123") + toString(t, 12.3, "12.3") + toString(t, true, "true") + toString(t, 1.5+10i, "(1.5+10i)") + // Sprintf function not guarantee that maps with equal keys always will be equal in string representation + //toString(t, struct{ Keys map[int]int }{Keys: map[int]int{1: 2, 3: 4}}, "{map[1:2 3:4]}") +} + +func TestToFloat(t *testing.T) { + tests := []string{"", "123", "-.01", "10.", "string", "1.23e3", ".23e10"} + expected := []float64{0, 123, -0.01, 10.0, 0, 1230, 0.23e10} + for i := 0; i < len(tests); i++ { + res, _ := ToFloat(tests[i]) + if res != expected[i] { + t.Log("Case ", i, ": expected ", expected[i], " when result is ", res) + t.FailNow() + } + } +} + +func TestToJSON(t *testing.T) { + tests := []interface{}{"test", map[string]string{"a": "b", "b": "c"}, func() error { return fmt.Errorf("Error") }} + expected := [][]string{ + {"\"test\"", ""}, + {"{\"a\":\"b\",\"b\":\"c\"}", ""}, + {"", "json: unsupported type: func() error"}, + } + for i, test := range tests { + actual, err := ToJSON(test) + if actual != expected[i][0] { + t.Errorf("Expected toJSON(%v) to return '%v', got '%v'", test, expected[i][0], actual) + } + if fmt.Sprintf("%v", err) != expected[i][1] { + t.Errorf("Expected error returned from toJSON(%v) to return '%v', got '%v'", test, expected[i][1], fmt.Sprintf("%v", err)) + } + } +} diff --git a/vendor/github.com/asaskevich/govalidator/error.go b/vendor/github.com/asaskevich/govalidator/error.go new file mode 100644 index 000000000..b9c32079b --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/error.go @@ -0,0 +1,36 @@ +package govalidator + +import "strings" + +// Errors is an array of multiple errors and conforms to the error interface. +type Errors []error + +// Errors returns itself. +func (es Errors) Errors() []error { + return es +} + +func (es Errors) Error() string { + var errs []string + for _, e := range es { + errs = append(errs, e.Error()) + } + return strings.Join(errs, ";") +} + +// Error encapsulates a name, an error and whether there's a custom error message or not. +type Error struct { + Name string + Err error + CustomErrorMessageExists bool + + // Validator indicates the name of the validator that failed + Validator string +} + +func (e Error) Error() string { + if e.CustomErrorMessageExists { + return e.Err.Error() + } + return e.Name + ": " + e.Err.Error() +} diff --git a/vendor/github.com/asaskevich/govalidator/error_test.go b/vendor/github.com/asaskevich/govalidator/error_test.go new file mode 100644 index 000000000..e673f2824 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/error_test.go @@ -0,0 +1,29 @@ +package govalidator + +import ( + "fmt" + "testing" +) + +func TestErrorsToString(t *testing.T) { + t.Parallel() + customErr := &Error{Name: "Custom Error Name", Err: fmt.Errorf("stdlib error")} + customErrWithCustomErrorMessage := &Error{Name: "Custom Error Name 2", Err: fmt.Errorf("Bad stuff happened"), CustomErrorMessageExists: true} + + var tests = []struct { + param1 Errors + expected string + }{ + {Errors{}, ""}, + {Errors{fmt.Errorf("Error 1")}, "Error 1"}, + {Errors{fmt.Errorf("Error 1"), fmt.Errorf("Error 2")}, "Error 1;Error 2"}, + {Errors{customErr, fmt.Errorf("Error 2")}, "Custom Error Name: stdlib error;Error 2"}, + {Errors{fmt.Errorf("Error 123"), customErrWithCustomErrorMessage}, "Error 123;Bad stuff happened"}, + } + for _, test := range tests { + actual := test.param1.Error() + if actual != test.expected { + t.Errorf("Expected Error() to return '%v', got '%v'", test.expected, actual) + } + } +} diff --git a/vendor/github.com/asaskevich/govalidator/numerics.go b/vendor/github.com/asaskevich/govalidator/numerics.go new file mode 100644 index 000000000..d0140d421 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/numerics.go @@ -0,0 +1,94 @@ +package govalidator + +import ( + "math" + "reflect" +) + +// Abs returns absolute value of number +func Abs(value float64) float64 { + return math.Abs(value) +} + +// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise +func Sign(value float64) float64 { + if value > 0 { + return 1 + } else if value < 0 { + return -1 + } else { + return 0 + } +} + +// IsNegative returns true if value < 0 +func IsNegative(value float64) bool { + return value < 0 +} + +// IsPositive returns true if value > 0 +func IsPositive(value float64) bool { + return value > 0 +} + +// IsNonNegative returns true if value >= 0 +func IsNonNegative(value float64) bool { + return value >= 0 +} + +// IsNonPositive returns true if value <= 0 +func IsNonPositive(value float64) bool { + return value <= 0 +} + +// InRange returns true if value lies between left and right border +func InRangeInt(value, left, right int) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// InRange returns true if value lies between left and right border +func InRangeFloat32(value, left, right float32) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// InRange returns true if value lies between left and right border +func InRangeFloat64(value, left, right float64) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// InRange returns true if value lies between left and right border, generic type to handle int, float32 or float64, all types must the same type +func InRange(value interface{}, left interface{}, right interface{}) bool { + + reflectValue := reflect.TypeOf(value).Kind() + reflectLeft := reflect.TypeOf(left).Kind() + reflectRight := reflect.TypeOf(right).Kind() + + if reflectValue == reflect.Int && reflectLeft == reflect.Int && reflectRight == reflect.Int { + return InRangeInt(value.(int), left.(int), right.(int)) + } else if reflectValue == reflect.Float32 && reflectLeft == reflect.Float32 && reflectRight == reflect.Float32 { + return InRangeFloat32(value.(float32), left.(float32), right.(float32)) + } else if reflectValue == reflect.Float64 && reflectLeft == reflect.Float64 && reflectRight == reflect.Float64 { + return InRangeFloat64(value.(float64), left.(float64), right.(float64)) + } else { + return false + } +} + +// IsWhole returns true if value is whole number +func IsWhole(value float64) bool { + return math.Remainder(value, 1) == 0 +} + +// IsNatural returns true if value is natural number (positive and whole) +func IsNatural(value float64) bool { + return IsWhole(value) && IsPositive(value) +} diff --git a/vendor/github.com/asaskevich/govalidator/numerics_test.go b/vendor/github.com/asaskevich/govalidator/numerics_test.go new file mode 100644 index 000000000..ca743dfed --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/numerics_test.go @@ -0,0 +1,349 @@ +package govalidator + +import "testing" + +func TestAbs(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected float64 + }{ + {0, 0}, + {-1, 1}, + {10, 10}, + {3.14, 3.14}, + {-96, 96}, + {-10e-12, 10e-12}, + } + for _, test := range tests { + actual := Abs(test.param) + if actual != test.expected { + t.Errorf("Expected Abs(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestSign(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected float64 + }{ + {0, 0}, + {-1, -1}, + {10, 1}, + {3.14, 1}, + {-96, -1}, + {-10e-12, -1}, + } + for _, test := range tests { + actual := Sign(test.param) + if actual != test.expected { + t.Errorf("Expected Sign(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsNegative(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected bool + }{ + {0, false}, + {-1, true}, + {10, false}, + {3.14, false}, + {-96, true}, + {-10e-12, true}, + } + for _, test := range tests { + actual := IsNegative(test.param) + if actual != test.expected { + t.Errorf("Expected IsNegative(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsNonNegative(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected bool + }{ + {0, true}, + {-1, false}, + {10, true}, + {3.14, true}, + {-96, false}, + {-10e-12, false}, + } + for _, test := range tests { + actual := IsNonNegative(test.param) + if actual != test.expected { + t.Errorf("Expected IsNonNegative(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsPositive(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected bool + }{ + {0, false}, + {-1, false}, + {10, true}, + {3.14, true}, + {-96, false}, + {-10e-12, false}, + } + for _, test := range tests { + actual := IsPositive(test.param) + if actual != test.expected { + t.Errorf("Expected IsPositive(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsNonPositive(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected bool + }{ + {0, true}, + {-1, true}, + {10, false}, + {3.14, false}, + {-96, true}, + {-10e-12, true}, + } + for _, test := range tests { + actual := IsNonPositive(test.param) + if actual != test.expected { + t.Errorf("Expected IsNonPositive(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsWhole(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected bool + }{ + {0, true}, + {-1, true}, + {10, true}, + {3.14, false}, + {-96, true}, + {-10e-12, false}, + } + for _, test := range tests { + actual := IsWhole(test.param) + if actual != test.expected { + t.Errorf("Expected IsWhole(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsNatural(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + expected bool + }{ + {0, false}, + {-1, false}, + {10, true}, + {3.14, false}, + {96, true}, + {-10e-12, false}, + } + for _, test := range tests { + actual := IsNatural(test.param) + if actual != test.expected { + t.Errorf("Expected IsNatural(%v) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestInRangeInt(t *testing.T) { + t.Parallel() + + var tests = []struct { + param int + left int + right int + expected bool + }{ + {0, 0, 0, true}, + {1, 0, 0, false}, + {-1, 0, 0, false}, + {0, -1, 1, true}, + {0, 0, 1, true}, + {0, -1, 0, true}, + {0, 0, -1, true}, + {0, 10, 5, false}, + } + for _, test := range tests { + actual := InRangeInt(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRangeInt(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } +} + +func TestInRangeFloat32(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float32 + left float32 + right float32 + expected bool + }{ + {0, 0, 0, true}, + {1, 0, 0, false}, + {-1, 0, 0, false}, + {0, -1, 1, true}, + {0, 0, 1, true}, + {0, -1, 0, true}, + {0, 0, -1, true}, + {0, 10, 5, false}, + } + for _, test := range tests { + actual := InRangeFloat32(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRangeFloat32(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } +} + +func TestInRangeFloat64(t *testing.T) { + t.Parallel() + + var tests = []struct { + param float64 + left float64 + right float64 + expected bool + }{ + {0, 0, 0, true}, + {1, 0, 0, false}, + {-1, 0, 0, false}, + {0, -1, 1, true}, + {0, 0, 1, true}, + {0, -1, 0, true}, + {0, 0, -1, true}, + {0, 10, 5, false}, + } + for _, test := range tests { + actual := InRangeFloat64(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRangeFloat64(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } +} + +func TestInRange(t *testing.T) { + t.Parallel() + + var testsInt = []struct { + param int + left int + right int + expected bool + }{ + {0, 0, 0, true}, + {1, 0, 0, false}, + {-1, 0, 0, false}, + {0, -1, 1, true}, + {0, 0, 1, true}, + {0, -1, 0, true}, + {0, 0, -1, true}, + {0, 10, 5, false}, + } + for _, test := range testsInt { + actual := InRange(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } + + var testsFloat32 = []struct { + param float32 + left float32 + right float32 + expected bool + }{ + {0, 0, 0, true}, + {1, 0, 0, false}, + {-1, 0, 0, false}, + {0, -1, 1, true}, + {0, 0, 1, true}, + {0, -1, 0, true}, + {0, 0, -1, true}, + {0, 10, 5, false}, + } + for _, test := range testsFloat32 { + actual := InRange(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } + + var testsFloat64 = []struct { + param float64 + left float64 + right float64 + expected bool + }{ + {0, 0, 0, true}, + {1, 0, 0, false}, + {-1, 0, 0, false}, + {0, -1, 1, true}, + {0, 0, 1, true}, + {0, -1, 0, true}, + {0, 0, -1, true}, + {0, 10, 5, false}, + } + for _, test := range testsFloat64 { + actual := InRange(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } + + var testsTypeMix = []struct { + param int + left float64 + right float64 + expected bool + }{ + {0, 0, 0, false}, + {1, 0, 0, false}, + {-1, 0, 0, false}, + {0, -1, 1, false}, + {0, 0, 1, false}, + {0, -1, 0, false}, + {0, 0, -1, false}, + {0, 10, 5, false}, + } + for _, test := range testsTypeMix { + actual := InRange(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRange(%v, %v, %v) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } +} diff --git a/vendor/github.com/asaskevich/govalidator/patterns.go b/vendor/github.com/asaskevich/govalidator/patterns.go new file mode 100644 index 000000000..4a34e2240 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/patterns.go @@ -0,0 +1,90 @@ +package govalidator + +import "regexp" + +// Basic regular expressions for validating strings +const ( + Email string = "^(((([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}])))\\.?$" + CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$" + ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$" + ISBN13 string = "^(?:[0-9]{13})$" + UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" + UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + Alpha string = "^[a-zA-Z]+$" + Alphanumeric string = "^[a-zA-Z0-9]+$" + Numeric string = "^[0-9]+$" + Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$" + Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$" + Hexadecimal string = "^[0-9a-fA-F]+$" + Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + RGBcolor string = "^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])\\s*\\)$" + ASCII string = "^[\x00-\x7F]+$" + Multibyte string = "[^\x00-\x7F]" + FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" + HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" + Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" + PrintableASCII string = "^[\x20-\x7E]+$" + DataURI string = "^data:.+\\/(.+);base64$" + Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" + Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" + DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$` + IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` + URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)` + URLUsername string = `(\S+(:\S*)?@)` + URLPath string = `((\/|\?|#)[^\s]*)` + URLPort string = `(:(\d{1,5}))` + URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))` + URLSubdomain string = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))` + URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$` + SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` + WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` + UnixPath string = `^(/[^/\x00]*)+/?$` + Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$" + tagName string = "valid" +) + +// Used by IsFilePath func +const ( + // Unknown is unresolved OS type + Unknown = iota + // Win is Windows type + Win + // Unix is *nix OS types + Unix +) + +var ( + rxEmail = regexp.MustCompile(Email) + rxCreditCard = regexp.MustCompile(CreditCard) + rxISBN10 = regexp.MustCompile(ISBN10) + rxISBN13 = regexp.MustCompile(ISBN13) + rxUUID3 = regexp.MustCompile(UUID3) + rxUUID4 = regexp.MustCompile(UUID4) + rxUUID5 = regexp.MustCompile(UUID5) + rxUUID = regexp.MustCompile(UUID) + rxAlpha = regexp.MustCompile(Alpha) + rxAlphanumeric = regexp.MustCompile(Alphanumeric) + rxNumeric = regexp.MustCompile(Numeric) + rxInt = regexp.MustCompile(Int) + rxFloat = regexp.MustCompile(Float) + rxHexadecimal = regexp.MustCompile(Hexadecimal) + rxHexcolor = regexp.MustCompile(Hexcolor) + rxRGBcolor = regexp.MustCompile(RGBcolor) + rxASCII = regexp.MustCompile(ASCII) + rxPrintableASCII = regexp.MustCompile(PrintableASCII) + rxMultibyte = regexp.MustCompile(Multibyte) + rxFullWidth = regexp.MustCompile(FullWidth) + rxHalfWidth = regexp.MustCompile(HalfWidth) + rxBase64 = regexp.MustCompile(Base64) + rxDataURI = regexp.MustCompile(DataURI) + rxLatitude = regexp.MustCompile(Latitude) + rxLongitude = regexp.MustCompile(Longitude) + rxDNSName = regexp.MustCompile(DNSName) + rxURL = regexp.MustCompile(URL) + rxSSN = regexp.MustCompile(SSN) + rxWinPath = regexp.MustCompile(WinPath) + rxUnixPath = regexp.MustCompile(UnixPath) + rxSemver = regexp.MustCompile(Semver) +) diff --git a/vendor/github.com/asaskevich/govalidator/types.go b/vendor/github.com/asaskevich/govalidator/types.go new file mode 100644 index 000000000..ddd30b122 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/types.go @@ -0,0 +1,616 @@ +package govalidator + +import ( + "reflect" + "regexp" + "sync" +) + +// Validator is a wrapper for a validator function that returns bool and accepts string. +type Validator func(str string) bool + +// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type. +// The second parameter should be the context (in the case of validating a struct: the whole object being validated). +type CustomTypeValidator func(i interface{}, o interface{}) bool + +// ParamValidator is a wrapper for validator functions that accepts additional parameters. +type ParamValidator func(str string, params ...string) bool +type tagOptionsMap map[string]string + +// UnsupportedTypeError is a wrapper for reflect.Type +type UnsupportedTypeError struct { + Type reflect.Type +} + +// stringValues is a slice of reflect.Value holding *reflect.StringValue. +// It implements the methods to sort by string. +type stringValues []reflect.Value + +// ParamTagMap is a map of functions accept variants parameters +var ParamTagMap = map[string]ParamValidator{ + "length": ByteLength, + "range": Range, + "runelength": RuneLength, + "stringlength": StringLength, + "matches": StringMatches, + "in": isInRaw, + "rsapub": IsRsaPub, +} + +// ParamTagRegexMap maps param tags to their respective regexes. +var ParamTagRegexMap = map[string]*regexp.Regexp{ + "range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"), + "length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"), + "runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"), + "stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"), + "in": regexp.MustCompile(`^in\((.*)\)`), + "matches": regexp.MustCompile(`^matches\((.+)\)$`), + "rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"), +} + +type customTypeTagMap struct { + validators map[string]CustomTypeValidator + + sync.RWMutex +} + +func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) { + tm.RLock() + defer tm.RUnlock() + v, ok := tm.validators[name] + return v, ok +} + +func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) { + tm.Lock() + defer tm.Unlock() + tm.validators[name] = ctv +} + +// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function. +// Use this to validate compound or custom types that need to be handled as a whole, e.g. +// `type UUID [16]byte` (this would be handled as an array of bytes). +var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)} + +// TagMap is a map of functions, that can be used as tags for ValidateStruct function. +var TagMap = map[string]Validator{ + "email": IsEmail, + "url": IsURL, + "dialstring": IsDialString, + "requrl": IsRequestURL, + "requri": IsRequestURI, + "alpha": IsAlpha, + "utfletter": IsUTFLetter, + "alphanum": IsAlphanumeric, + "utfletternum": IsUTFLetterNumeric, + "numeric": IsNumeric, + "utfnumeric": IsUTFNumeric, + "utfdigit": IsUTFDigit, + "hexadecimal": IsHexadecimal, + "hexcolor": IsHexcolor, + "rgbcolor": IsRGBcolor, + "lowercase": IsLowerCase, + "uppercase": IsUpperCase, + "int": IsInt, + "float": IsFloat, + "null": IsNull, + "uuid": IsUUID, + "uuidv3": IsUUIDv3, + "uuidv4": IsUUIDv4, + "uuidv5": IsUUIDv5, + "creditcard": IsCreditCard, + "isbn10": IsISBN10, + "isbn13": IsISBN13, + "json": IsJSON, + "multibyte": IsMultibyte, + "ascii": IsASCII, + "printableascii": IsPrintableASCII, + "fullwidth": IsFullWidth, + "halfwidth": IsHalfWidth, + "variablewidth": IsVariableWidth, + "base64": IsBase64, + "datauri": IsDataURI, + "ip": IsIP, + "port": IsPort, + "ipv4": IsIPv4, + "ipv6": IsIPv6, + "dns": IsDNSName, + "host": IsHost, + "mac": IsMAC, + "latitude": IsLatitude, + "longitude": IsLongitude, + "ssn": IsSSN, + "semver": IsSemver, + "rfc3339": IsRFC3339, + "rfc3339WithoutZone": IsRFC3339WithoutZone, + "ISO3166Alpha2": IsISO3166Alpha2, + "ISO3166Alpha3": IsISO3166Alpha3, + "ISO4217": IsISO4217, +} + +// ISO3166Entry stores country codes +type ISO3166Entry struct { + EnglishShortName string + FrenchShortName string + Alpha2Code string + Alpha3Code string + Numeric string +} + +//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes" +var ISO3166List = []ISO3166Entry{ + {"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"}, + {"Albania", "Albanie (l')", "AL", "ALB", "008"}, + {"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"}, + {"Algeria", "Algérie (l')", "DZ", "DZA", "012"}, + {"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"}, + {"Andorra", "Andorre (l')", "AD", "AND", "020"}, + {"Angola", "Angola (l')", "AO", "AGO", "024"}, + {"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"}, + {"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"}, + {"Argentina", "Argentine (l')", "AR", "ARG", "032"}, + {"Australia", "Australie (l')", "AU", "AUS", "036"}, + {"Austria", "Autriche (l')", "AT", "AUT", "040"}, + {"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"}, + {"Bahrain", "Bahreïn", "BH", "BHR", "048"}, + {"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"}, + {"Armenia", "Arménie (l')", "AM", "ARM", "051"}, + {"Barbados", "Barbade (la)", "BB", "BRB", "052"}, + {"Belgium", "Belgique (la)", "BE", "BEL", "056"}, + {"Bermuda", "Bermudes (les)", "BM", "BMU", "060"}, + {"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"}, + {"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"}, + {"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"}, + {"Botswana", "Botswana (le)", "BW", "BWA", "072"}, + {"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"}, + {"Brazil", "Brésil (le)", "BR", "BRA", "076"}, + {"Belize", "Belize (le)", "BZ", "BLZ", "084"}, + {"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"}, + {"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"}, + {"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"}, + {"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"}, + {"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"}, + {"Myanmar", "Myanmar (le)", "MM", "MMR", "104"}, + {"Burundi", "Burundi (le)", "BI", "BDI", "108"}, + {"Belarus", "Bélarus (le)", "BY", "BLR", "112"}, + {"Cambodia", "Cambodge (le)", "KH", "KHM", "116"}, + {"Cameroon", "Cameroun (le)", "CM", "CMR", "120"}, + {"Canada", "Canada (le)", "CA", "CAN", "124"}, + {"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"}, + {"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"}, + {"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"}, + {"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"}, + {"Chad", "Tchad (le)", "TD", "TCD", "148"}, + {"Chile", "Chili (le)", "CL", "CHL", "152"}, + {"China", "Chine (la)", "CN", "CHN", "156"}, + {"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"}, + {"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"}, + {"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"}, + {"Colombia", "Colombie (la)", "CO", "COL", "170"}, + {"Comoros (the)", "Comores (les)", "KM", "COM", "174"}, + {"Mayotte", "Mayotte", "YT", "MYT", "175"}, + {"Congo (the)", "Congo (le)", "CG", "COG", "178"}, + {"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"}, + {"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"}, + {"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"}, + {"Croatia", "Croatie (la)", "HR", "HRV", "191"}, + {"Cuba", "Cuba", "CU", "CUB", "192"}, + {"Cyprus", "Chypre", "CY", "CYP", "196"}, + {"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"}, + {"Benin", "Bénin (le)", "BJ", "BEN", "204"}, + {"Denmark", "Danemark (le)", "DK", "DNK", "208"}, + {"Dominica", "Dominique (la)", "DM", "DMA", "212"}, + {"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"}, + {"Ecuador", "Équateur (l')", "EC", "ECU", "218"}, + {"El Salvador", "El Salvador", "SV", "SLV", "222"}, + {"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"}, + {"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"}, + {"Eritrea", "Érythrée (l')", "ER", "ERI", "232"}, + {"Estonia", "Estonie (l')", "EE", "EST", "233"}, + {"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"}, + {"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"}, + {"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"}, + {"Fiji", "Fidji (les)", "FJ", "FJI", "242"}, + {"Finland", "Finlande (la)", "FI", "FIN", "246"}, + {"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"}, + {"France", "France (la)", "FR", "FRA", "250"}, + {"French Guiana", "Guyane française (la )", "GF", "GUF", "254"}, + {"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"}, + {"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"}, + {"Djibouti", "Djibouti", "DJ", "DJI", "262"}, + {"Gabon", "Gabon (le)", "GA", "GAB", "266"}, + {"Georgia", "Géorgie (la)", "GE", "GEO", "268"}, + {"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"}, + {"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"}, + {"Germany", "Allemagne (l')", "DE", "DEU", "276"}, + {"Ghana", "Ghana (le)", "GH", "GHA", "288"}, + {"Gibraltar", "Gibraltar", "GI", "GIB", "292"}, + {"Kiribati", "Kiribati", "KI", "KIR", "296"}, + {"Greece", "Grèce (la)", "GR", "GRC", "300"}, + {"Greenland", "Groenland (le)", "GL", "GRL", "304"}, + {"Grenada", "Grenade (la)", "GD", "GRD", "308"}, + {"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"}, + {"Guam", "Guam", "GU", "GUM", "316"}, + {"Guatemala", "Guatemala (le)", "GT", "GTM", "320"}, + {"Guinea", "Guinée (la)", "GN", "GIN", "324"}, + {"Guyana", "Guyana (le)", "GY", "GUY", "328"}, + {"Haiti", "Haïti", "HT", "HTI", "332"}, + {"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"}, + {"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"}, + {"Honduras", "Honduras (le)", "HN", "HND", "340"}, + {"Hong Kong", "Hong Kong", "HK", "HKG", "344"}, + {"Hungary", "Hongrie (la)", "HU", "HUN", "348"}, + {"Iceland", "Islande (l')", "IS", "ISL", "352"}, + {"India", "Inde (l')", "IN", "IND", "356"}, + {"Indonesia", "Indonésie (l')", "ID", "IDN", "360"}, + {"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"}, + {"Iraq", "Iraq (l')", "IQ", "IRQ", "368"}, + {"Ireland", "Irlande (l')", "IE", "IRL", "372"}, + {"Israel", "Israël", "IL", "ISR", "376"}, + {"Italy", "Italie (l')", "IT", "ITA", "380"}, + {"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"}, + {"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"}, + {"Japan", "Japon (le)", "JP", "JPN", "392"}, + {"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"}, + {"Jordan", "Jordanie (la)", "JO", "JOR", "400"}, + {"Kenya", "Kenya (le)", "KE", "KEN", "404"}, + {"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"}, + {"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"}, + {"Kuwait", "Koweït (le)", "KW", "KWT", "414"}, + {"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"}, + {"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"}, + {"Lebanon", "Liban (le)", "LB", "LBN", "422"}, + {"Lesotho", "Lesotho (le)", "LS", "LSO", "426"}, + {"Latvia", "Lettonie (la)", "LV", "LVA", "428"}, + {"Liberia", "Libéria (le)", "LR", "LBR", "430"}, + {"Libya", "Libye (la)", "LY", "LBY", "434"}, + {"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"}, + {"Lithuania", "Lituanie (la)", "LT", "LTU", "440"}, + {"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"}, + {"Macao", "Macao", "MO", "MAC", "446"}, + {"Madagascar", "Madagascar", "MG", "MDG", "450"}, + {"Malawi", "Malawi (le)", "MW", "MWI", "454"}, + {"Malaysia", "Malaisie (la)", "MY", "MYS", "458"}, + {"Maldives", "Maldives (les)", "MV", "MDV", "462"}, + {"Mali", "Mali (le)", "ML", "MLI", "466"}, + {"Malta", "Malte", "MT", "MLT", "470"}, + {"Martinique", "Martinique (la)", "MQ", "MTQ", "474"}, + {"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"}, + {"Mauritius", "Maurice", "MU", "MUS", "480"}, + {"Mexico", "Mexique (le)", "MX", "MEX", "484"}, + {"Monaco", "Monaco", "MC", "MCO", "492"}, + {"Mongolia", "Mongolie (la)", "MN", "MNG", "496"}, + {"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"}, + {"Montenegro", "Monténégro (le)", "ME", "MNE", "499"}, + {"Montserrat", "Montserrat", "MS", "MSR", "500"}, + {"Morocco", "Maroc (le)", "MA", "MAR", "504"}, + {"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"}, + {"Oman", "Oman", "OM", "OMN", "512"}, + {"Namibia", "Namibie (la)", "NA", "NAM", "516"}, + {"Nauru", "Nauru", "NR", "NRU", "520"}, + {"Nepal", "Népal (le)", "NP", "NPL", "524"}, + {"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"}, + {"Curaçao", "Curaçao", "CW", "CUW", "531"}, + {"Aruba", "Aruba", "AW", "ABW", "533"}, + {"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"}, + {"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"}, + {"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"}, + {"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"}, + {"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"}, + {"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"}, + {"Niger (the)", "Niger (le)", "NE", "NER", "562"}, + {"Nigeria", "Nigéria (le)", "NG", "NGA", "566"}, + {"Niue", "Niue", "NU", "NIU", "570"}, + {"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"}, + {"Norway", "Norvège (la)", "NO", "NOR", "578"}, + {"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"}, + {"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"}, + {"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"}, + {"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"}, + {"Palau", "Palaos (les)", "PW", "PLW", "585"}, + {"Pakistan", "Pakistan (le)", "PK", "PAK", "586"}, + {"Panama", "Panama (le)", "PA", "PAN", "591"}, + {"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"}, + {"Paraguay", "Paraguay (le)", "PY", "PRY", "600"}, + {"Peru", "Pérou (le)", "PE", "PER", "604"}, + {"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"}, + {"Pitcairn", "Pitcairn", "PN", "PCN", "612"}, + {"Poland", "Pologne (la)", "PL", "POL", "616"}, + {"Portugal", "Portugal (le)", "PT", "PRT", "620"}, + {"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"}, + {"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"}, + {"Puerto Rico", "Porto Rico", "PR", "PRI", "630"}, + {"Qatar", "Qatar (le)", "QA", "QAT", "634"}, + {"Réunion", "Réunion (La)", "RE", "REU", "638"}, + {"Romania", "Roumanie (la)", "RO", "ROU", "642"}, + {"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"}, + {"Rwanda", "Rwanda (le)", "RW", "RWA", "646"}, + {"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"}, + {"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"}, + {"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"}, + {"Anguilla", "Anguilla", "AI", "AIA", "660"}, + {"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"}, + {"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"}, + {"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"}, + {"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"}, + {"San Marino", "Saint-Marin", "SM", "SMR", "674"}, + {"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"}, + {"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"}, + {"Senegal", "Sénégal (le)", "SN", "SEN", "686"}, + {"Serbia", "Serbie (la)", "RS", "SRB", "688"}, + {"Seychelles", "Seychelles (les)", "SC", "SYC", "690"}, + {"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"}, + {"Singapore", "Singapour", "SG", "SGP", "702"}, + {"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"}, + {"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"}, + {"Slovenia", "Slovénie (la)", "SI", "SVN", "705"}, + {"Somalia", "Somalie (la)", "SO", "SOM", "706"}, + {"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"}, + {"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"}, + {"Spain", "Espagne (l')", "ES", "ESP", "724"}, + {"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"}, + {"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"}, + {"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"}, + {"Suriname", "Suriname (le)", "SR", "SUR", "740"}, + {"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"}, + {"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"}, + {"Sweden", "Suède (la)", "SE", "SWE", "752"}, + {"Switzerland", "Suisse (la)", "CH", "CHE", "756"}, + {"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"}, + {"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"}, + {"Thailand", "Thaïlande (la)", "TH", "THA", "764"}, + {"Togo", "Togo (le)", "TG", "TGO", "768"}, + {"Tokelau", "Tokelau (les)", "TK", "TKL", "772"}, + {"Tonga", "Tonga (les)", "TO", "TON", "776"}, + {"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"}, + {"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"}, + {"Tunisia", "Tunisie (la)", "TN", "TUN", "788"}, + {"Turkey", "Turquie (la)", "TR", "TUR", "792"}, + {"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"}, + {"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"}, + {"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"}, + {"Uganda", "Ouganda (l')", "UG", "UGA", "800"}, + {"Ukraine", "Ukraine (l')", "UA", "UKR", "804"}, + {"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"}, + {"Egypt", "Égypte (l')", "EG", "EGY", "818"}, + {"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"}, + {"Guernsey", "Guernesey", "GG", "GGY", "831"}, + {"Jersey", "Jersey", "JE", "JEY", "832"}, + {"Isle of Man", "Île de Man", "IM", "IMN", "833"}, + {"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"}, + {"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"}, + {"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"}, + {"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"}, + {"Uruguay", "Uruguay (l')", "UY", "URY", "858"}, + {"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"}, + {"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"}, + {"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"}, + {"Samoa", "Samoa (le)", "WS", "WSM", "882"}, + {"Yemen", "Yémen (le)", "YE", "YEM", "887"}, + {"Zambia", "Zambie (la)", "ZM", "ZMB", "894"}, +} + +// ISO4217List is the list of ISO currency codes +var ISO4217List = []string{ + "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", + "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", + "CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK", + "DJF", "DKK", "DOP", "DZD", + "EGP", "ERN", "ETB", "EUR", + "FJD", "FKP", + "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", + "HKD", "HNL", "HRK", "HTG", "HUF", + "IDR", "ILS", "INR", "IQD", "IRR", "ISK", + "JMD", "JOD", "JPY", + "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", + "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", + "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", + "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", + "OMR", + "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", + "QAR", + "RON", "RSD", "RUB", "RWF", + "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL", + "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", + "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS", + "VEF", "VND", "VUV", + "WST", + "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", + "YER", + "ZAR", "ZMW", "ZWL", +} + +// ISO693Entry stores ISO language codes +type ISO693Entry struct { + Alpha3bCode string + Alpha2Code string + English string +} + +//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json +var ISO693List = []ISO693Entry{ + {Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"}, + {Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"}, + {Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"}, + {Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"}, + {Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"}, + {Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"}, + {Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"}, + {Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"}, + {Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"}, + {Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"}, + {Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"}, + {Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"}, + {Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"}, + {Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"}, + {Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"}, + {Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"}, + {Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"}, + {Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"}, + {Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"}, + {Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"}, + {Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"}, + {Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"}, + {Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"}, + {Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"}, + {Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"}, + {Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"}, + {Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"}, + {Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"}, + {Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"}, + {Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"}, + {Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"}, + {Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"}, + {Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"}, + {Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"}, + {Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"}, + {Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"}, + {Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"}, + {Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"}, + {Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"}, + {Alpha3bCode: "eng", Alpha2Code: "en", English: "English"}, + {Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"}, + {Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"}, + {Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"}, + {Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"}, + {Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"}, + {Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"}, + {Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"}, + {Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"}, + {Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"}, + {Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"}, + {Alpha3bCode: "ger", Alpha2Code: "de", English: "German"}, + {Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"}, + {Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"}, + {Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"}, + {Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"}, + {Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"}, + {Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"}, + {Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"}, + {Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"}, + {Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"}, + {Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"}, + {Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"}, + {Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"}, + {Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"}, + {Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"}, + {Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"}, + {Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"}, + {Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"}, + {Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"}, + {Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"}, + {Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"}, + {Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"}, + {Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"}, + {Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"}, + {Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"}, + {Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"}, + {Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"}, + {Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"}, + {Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"}, + {Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"}, + {Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"}, + {Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"}, + {Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"}, + {Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"}, + {Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"}, + {Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"}, + {Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"}, + {Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"}, + {Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"}, + {Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"}, + {Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"}, + {Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"}, + {Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"}, + {Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"}, + {Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"}, + {Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"}, + {Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"}, + {Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"}, + {Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"}, + {Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"}, + {Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"}, + {Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"}, + {Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"}, + {Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"}, + {Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"}, + {Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"}, + {Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"}, + {Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"}, + {Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"}, + {Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"}, + {Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"}, + {Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"}, + {Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"}, + {Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"}, + {Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"}, + {Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"}, + {Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"}, + {Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"}, + {Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"}, + {Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"}, + {Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"}, + {Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"}, + {Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"}, + {Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"}, + {Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"}, + {Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"}, + {Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"}, + {Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"}, + {Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"}, + {Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"}, + {Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"}, + {Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"}, + {Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"}, + {Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"}, + {Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"}, + {Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"}, + {Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"}, + {Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"}, + {Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"}, + {Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"}, + {Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"}, + {Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"}, + {Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"}, + {Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"}, + {Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"}, + {Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"}, + {Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"}, + {Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"}, + {Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"}, + {Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"}, + {Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"}, + {Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"}, + {Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"}, + {Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"}, + {Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"}, + {Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"}, + {Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"}, + {Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"}, + {Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"}, + {Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"}, + {Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"}, + {Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"}, + {Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"}, + {Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"}, + {Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"}, + {Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"}, + {Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"}, + {Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"}, + {Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"}, + {Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"}, + {Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"}, + {Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"}, + {Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"}, + {Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"}, + {Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"}, + {Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"}, + {Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"}, + {Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"}, + {Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"}, + {Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"}, + {Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"}, + {Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"}, + {Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"}, + {Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"}, +} diff --git a/vendor/github.com/asaskevich/govalidator/utils.go b/vendor/github.com/asaskevich/govalidator/utils.go new file mode 100644 index 000000000..78ed3fbab --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/utils.go @@ -0,0 +1,262 @@ +package govalidator + +import ( + "errors" + "fmt" + "html" + "math" + "path" + "regexp" + "strings" + "unicode" + "unicode/utf8" +) + +// Contains check if the string contains the substring. +func Contains(str, substring string) bool { + return strings.Contains(str, substring) +} + +// Matches check if string matches the pattern (pattern is regular expression) +// In case of error return false +func Matches(str, pattern string) bool { + match, _ := regexp.MatchString(pattern, str) + return match +} + +// LeftTrim trim characters from the left-side of the input. +// If second argument is empty, it's will be remove leading spaces. +func LeftTrim(str, chars string) string { + if chars == "" { + return strings.TrimLeftFunc(str, unicode.IsSpace) + } + r, _ := regexp.Compile("^[" + chars + "]+") + return r.ReplaceAllString(str, "") +} + +// RightTrim trim characters from the right-side of the input. +// If second argument is empty, it's will be remove spaces. +func RightTrim(str, chars string) string { + if chars == "" { + return strings.TrimRightFunc(str, unicode.IsSpace) + } + r, _ := regexp.Compile("[" + chars + "]+$") + return r.ReplaceAllString(str, "") +} + +// Trim trim characters from both sides of the input. +// If second argument is empty, it's will be remove spaces. +func Trim(str, chars string) string { + return LeftTrim(RightTrim(str, chars), chars) +} + +// WhiteList remove characters that do not appear in the whitelist. +func WhiteList(str, chars string) string { + pattern := "[^" + chars + "]+" + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, "") +} + +// BlackList remove characters that appear in the blacklist. +func BlackList(str, chars string) string { + pattern := "[" + chars + "]+" + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, "") +} + +// StripLow remove characters with a numerical value < 32 and 127, mostly control characters. +// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD). +func StripLow(str string, keepNewLines bool) string { + chars := "" + if keepNewLines { + chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F" + } else { + chars = "\x00-\x1F\x7F" + } + return BlackList(str, chars) +} + +// ReplacePattern replace regular expression pattern in string +func ReplacePattern(str, pattern, replace string) string { + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, replace) +} + +// Escape replace <, >, & and " with HTML entities. +var Escape = html.EscapeString + +func addSegment(inrune, segment []rune) []rune { + if len(segment) == 0 { + return inrune + } + if len(inrune) != 0 { + inrune = append(inrune, '_') + } + inrune = append(inrune, segment...) + return inrune +} + +// UnderscoreToCamelCase converts from underscore separated form to camel case form. +// Ex.: my_func => MyFunc +func UnderscoreToCamelCase(s string) string { + return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1) +} + +// CamelCaseToUnderscore converts from camel case form to underscore separated form. +// Ex.: MyFunc => my_func +func CamelCaseToUnderscore(str string) string { + var output []rune + var segment []rune + for _, r := range str { + if !unicode.IsLower(r) && string(r) != "_" { + output = addSegment(output, segment) + segment = nil + } + segment = append(segment, unicode.ToLower(r)) + } + output = addSegment(output, segment) + return string(output) +} + +// Reverse return reversed string +func Reverse(s string) string { + r := []rune(s) + for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { + r[i], r[j] = r[j], r[i] + } + return string(r) +} + +// GetLines split string by "\n" and return array of lines +func GetLines(s string) []string { + return strings.Split(s, "\n") +} + +// GetLine return specified line of multiline string +func GetLine(s string, index int) (string, error) { + lines := GetLines(s) + if index < 0 || index >= len(lines) { + return "", errors.New("line index out of bounds") + } + return lines[index], nil +} + +// RemoveTags remove all tags from HTML string +func RemoveTags(s string) string { + return ReplacePattern(s, "<[^>]*>", "") +} + +// SafeFileName return safe string that can be used in file names +func SafeFileName(str string) string { + name := strings.ToLower(str) + name = path.Clean(path.Base(name)) + name = strings.Trim(name, " ") + separators, err := regexp.Compile(`[ &_=+:]`) + if err == nil { + name = separators.ReplaceAllString(name, "-") + } + legal, err := regexp.Compile(`[^[:alnum:]-.]`) + if err == nil { + name = legal.ReplaceAllString(name, "") + } + for strings.Contains(name, "--") { + name = strings.Replace(name, "--", "-", -1) + } + return name +} + +// NormalizeEmail canonicalize an email address. +// The local part of the email address is lowercased for all domains; the hostname is always lowercased and +// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail). +// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and +// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are +// normalized to @gmail.com. +func NormalizeEmail(str string) (string, error) { + if !IsEmail(str) { + return "", fmt.Errorf("%s is not an email", str) + } + parts := strings.Split(str, "@") + parts[0] = strings.ToLower(parts[0]) + parts[1] = strings.ToLower(parts[1]) + if parts[1] == "gmail.com" || parts[1] == "googlemail.com" { + parts[1] = "gmail.com" + parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0] + } + return strings.Join(parts, "@"), nil +} + +// Truncate a string to the closest length without breaking words. +func Truncate(str string, length int, ending string) string { + var aftstr, befstr string + if len(str) > length { + words := strings.Fields(str) + before, present := 0, 0 + for i := range words { + befstr = aftstr + before = present + aftstr = aftstr + words[i] + " " + present = len(aftstr) + if present > length && i != 0 { + if (length - before) < (present - length) { + return Trim(befstr, " /\\.,\"'#!?&@+-") + ending + } + return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending + } + } + } + + return str +} + +// PadLeft pad left side of string if size of string is less then indicated pad length +func PadLeft(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, true, false) +} + +// PadRight pad right side of string if size of string is less then indicated pad length +func PadRight(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, false, true) +} + +// PadBoth pad sides of string if size of string is less then indicated pad length +func PadBoth(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, true, true) +} + +// PadString either left, right or both sides, not the padding string can be unicode and more then one +// character +func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string { + + // When padded length is less then the current string size + if padLen < utf8.RuneCountInString(str) { + return str + } + + padLen -= utf8.RuneCountInString(str) + + targetLen := padLen + + targetLenLeft := targetLen + targetLenRight := targetLen + if padLeft && padRight { + targetLenLeft = padLen / 2 + targetLenRight = padLen - targetLenLeft + } + + strToRepeatLen := utf8.RuneCountInString(padStr) + + repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen))) + repeatedString := strings.Repeat(padStr, repeatTimes) + + leftSide := "" + if padLeft { + leftSide = repeatedString[0:targetLenLeft] + } + + rightSide := "" + if padRight { + rightSide = repeatedString[0:targetLenRight] + } + + return leftSide + str + rightSide +} diff --git a/vendor/github.com/asaskevich/govalidator/utils_test.go b/vendor/github.com/asaskevich/govalidator/utils_test.go new file mode 100644 index 000000000..5ad8faeb4 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/utils_test.go @@ -0,0 +1,501 @@ +package govalidator + +import ( + "reflect" + "testing" +) + +func TestContains(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected bool + }{ + {"abacada", "", true}, + {"abacada", "ritir", false}, + {"abacada", "a", true}, + {"abacada", "aca", true}, + } + for _, test := range tests { + actual := Contains(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected Contains(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +func TestMatches(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected bool + }{ + {"123456789", "[0-9]+", true}, + {"abacada", "cab$", false}, + {"111222333", "((111|222|333)+)+", true}, + {"abacaba", "((123+]", false}, + } + for _, test := range tests { + actual := Matches(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected Matches(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +func TestLeftTrim(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected string + }{ + {" \r\n\tfoo \r\n\t ", "", "foo \r\n\t "}, + {"010100201000", "01", "201000"}, + } + for _, test := range tests { + actual := LeftTrim(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected LeftTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +func TestRightTrim(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected string + }{ + {" \r\n\tfoo \r\n\t ", "", " \r\n\tfoo"}, + {"010100201000", "01", "0101002"}, + } + for _, test := range tests { + actual := RightTrim(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected RightTrim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +func TestTrim(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected string + }{ + {" \r\n\tfoo \r\n\t ", "", "foo"}, + {"010100201000", "01", "2"}, + {"1234567890987654321", "1-8", "909"}, + } + for _, test := range tests { + actual := Trim(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected Trim(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +// This small example illustrate how to work with Trim function. +func ExampleTrim() { + // Remove from left and right spaces and "\r", "\n", "\t" characters + println(Trim(" \r\r\ntext\r \t\n", "") == "text") + // Remove from left and right characters that are between "1" and "8". + // "1-8" is like full list "12345678". + println(Trim("1234567890987654321", "1-8") == "909") +} + +func TestWhiteList(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected string + }{ + {"abcdef", "abc", "abc"}, + {"aaaaaaaaaabbbbbbbbbb", "abc", "aaaaaaaaaabbbbbbbbbb"}, + {"a1b2c3", "abc", "abc"}, + {" ", "abc", ""}, + {"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "aaaaaaaaaaaa"}, + } + for _, test := range tests { + actual := WhiteList(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected WhiteList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +// This small example illustrate how to work with WhiteList function. +func ExampleWhiteList() { + // Remove all characters from string ignoring characters between "a" and "z" + println(WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa") +} + +func TestBlackList(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected string + }{ + {"abcdef", "abc", "def"}, + {"aaaaaaaaaabbbbbbbbbb", "abc", ""}, + {"a1b2c3", "abc", "123"}, + {" ", "abc", " "}, + {"a3a43a5a4a3a2a23a4a5a4a3a4", "a-z", "34354322345434"}, + } + for _, test := range tests { + actual := BlackList(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected BlackList(%q,%q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +func TestStripLow(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 bool + expected string + }{ + {"foo\x00", false, "foo"}, + {"\x7Ffoo\x02", false, "foo"}, + {"\x01\x09", false, ""}, + {"foo\x0A\x0D", false, "foo"}, + {"perch\u00e9", false, "perch\u00e9"}, + {"\u20ac", false, "\u20ac"}, + {"\u2206\x0A", false, "\u2206"}, + {"foo\x0A\x0D", true, "foo\x0A\x0D"}, + {"\x03foo\x0A\x0D", true, "foo\x0A\x0D"}, + } + for _, test := range tests { + actual := StripLow(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected StripLow(%q,%t) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +func TestReplacePattern(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + param3 string + expected string + }{ + {"ab123ba", "[0-9]+", "aca", "abacaba"}, + {"abacaba", "[0-9]+", "aca", "abacaba"}, + {"httpftp://github.comio", "(ftp|io)", "", "http://github.com"}, + {"aaaaaaaaaa", "a", "", ""}, + {"http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "", "http://github.com"}, + } + for _, test := range tests { + actual := ReplacePattern(test.param1, test.param2, test.param3) + if actual != test.expected { + t.Errorf("Expected ReplacePattern(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual) + } + } +} + +// This small example illustrate how to work with ReplacePattern function. +func ExampleReplacePattern() { + // Replace in "http123123ftp://git534543hub.comio" following (pattern "(ftp|io|[0-9]+)"): + // - Sequence "ftp". + // - Sequence "io". + // - Sequence of digits. + // with empty string. + println(ReplacePattern("http123123ftp://git534543hub.comio", "(ftp|io|[0-9]+)", "") == "http://github.com") +} + +func TestEscape(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {`foo&bar`, "<img alt="foo&bar">"}, + } + for _, test := range tests { + actual := Escape(test.param) + if actual != test.expected { + t.Errorf("Expected Escape(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestUnderscoreToCamelCase(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {"a_b_c", "ABC"}, + {"my_func", "MyFunc"}, + {"1ab_cd", "1abCd"}, + } + for _, test := range tests { + actual := UnderscoreToCamelCase(test.param) + if actual != test.expected { + t.Errorf("Expected UnderscoreToCamelCase(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestCamelCaseToUnderscore(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {"MyFunc", "my_func"}, + {"ABC", "a_b_c"}, + {"1B", "1_b"}, + {"foo_bar", "foo_bar"}, + } + for _, test := range tests { + actual := CamelCaseToUnderscore(test.param) + if actual != test.expected { + t.Errorf("Expected CamelCaseToUnderscore(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestReverse(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {"abc", "cba"}, + {"カタカナ", "ナカタカ"}, + } + for _, test := range tests { + actual := Reverse(test.param) + if actual != test.expected { + t.Errorf("Expected Reverse(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestGetLines(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected []string + }{ + {"abc", []string{"abc"}}, + {"a\nb\nc", []string{"a", "b", "c"}}, + } + for _, test := range tests { + actual := GetLines(test.param) + if !reflect.DeepEqual(actual, test.expected) { + t.Errorf("Expected GetLines(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestGetLine(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 int + expected string + }{ + {"abc", 0, "abc"}, + {"a\nb\nc", 0, "a"}, + {"abc", -1, ""}, + {"abacaba\n", 1, ""}, + {"abc", 3, ""}, + } + for _, test := range tests { + actual, _ := GetLine(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected GetLine(%q, %d) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +func TestRemoveTags(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {"abc", "abc"}, + {"", ""}, + {"

Text

", "Text"}, + {`Link`, "Link"}, + } + for _, test := range tests { + actual := RemoveTags(test.param) + if actual != test.expected { + t.Errorf("Expected RemoveTags(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestSafeFileName(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {"abc", "abc"}, + {"123456789 '_-?ASDF@£$%£%^é.html", "123456789-asdf.html"}, + {"ReadMe.md", "readme.md"}, + {"file:///c:/test.go", "test.go"}, + {"../../../Hello World!.txt", "hello-world.txt"}, + } + for _, test := range tests { + actual := SafeFileName(test.param) + if actual != test.expected { + t.Errorf("Expected SafeFileName(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestNormalizeEmail(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {`test@me.com`, `test@me.com`}, + {`some.name@gmail.com`, `somename@gmail.com`}, + {`some.name@googlemail.com`, `somename@gmail.com`}, + {`some.name+extension@gmail.com`, `somename@gmail.com`}, + {`some.name+extension@googlemail.com`, `somename@gmail.com`}, + {`some.name.middlename+extension@gmail.com`, `somenamemiddlename@gmail.com`}, + {`some.name.middlename+extension@googlemail.com`, `somenamemiddlename@gmail.com`}, + {`some.name.midd.lena.me.+extension@gmail.com`, `somenamemiddlename@gmail.com`}, + {`some.name.midd.lena.me.+extension@googlemail.com`, `somenamemiddlename@gmail.com`}, + {`some.name+extension@unknown.com`, `some.name+extension@unknown.com`}, + {`hans@m端ller.com`, `hans@m端ller.com`}, + {`hans`, ``}, + } + for _, test := range tests { + actual, err := NormalizeEmail(test.param) + if actual != test.expected { + t.Errorf("Expected NormalizeEmail(%q) to be %v, got %v, err %v", test.param, test.expected, actual, err) + } + } +} + +func TestTruncate(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 int + param3 string + expected string + }{ + {`Lorem ipsum dolor sit amet, consectetur adipiscing elit.`, 25, `...`, `Lorem ipsum dolor sit amet...`}, + {`Measuring programming progress by lines of code is like measuring aircraft building progress by weight.`, 35, ` new born babies!`, `Measuring programming progress by new born babies!`}, + {`Testestestestestestestestestest testestestestestestestestest`, 7, `...`, `Testestestestestestestestestest...`}, + {`Testing`, 7, `...`, `Testing`}, + } + for _, test := range tests { + actual := Truncate(test.param1, test.param2, test.param3) + if actual != test.expected { + t.Errorf("Expected Truncate(%q, %d, %q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual) + } + } +} + +func TestPadLeft(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + param3 int + expected string + }{ + {"こんにちは", "xyz", 12, "xyzxyzxこんにちは"}, + {"こんにちは", "xyz", 11, "xyzxyzこんにちは"}, + {"abc", "x", 5, "xxabc"}, + {"abc", "xyz", 5, "xyabc"}, + {"abcde", "xyz", 5, "abcde"}, + {"abcde", "xyz", 4, "abcde"}, + } + for _, test := range tests { + actual := PadLeft(test.param1, test.param2, test.param3) + if actual != test.expected { + t.Errorf("Expected PadLeft(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual) + } + } +} + +func TestPadRight(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + param3 int + expected string + }{ + {"こんにちは", "xyz", 12, "こんにちはxyzxyzx"}, + {"こんにちは", "xyz", 11, "こんにちはxyzxyz"}, + {"abc", "x", 5, "abcxx"}, + {"abc", "xyz", 5, "abcxy"}, + {"abcde", "xyz", 5, "abcde"}, + {"abcde", "xyz", 4, "abcde"}, + } + for _, test := range tests { + actual := PadRight(test.param1, test.param2, test.param3) + if actual != test.expected { + t.Errorf("Expected PadRight(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual) + } + } +} + +func TestPadBoth(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + param3 int + expected string + }{ + {"こんにちは", "xyz", 12, "xyzこんにちはxyzx"}, + {"こんにちは", "xyz", 11, "xyzこんにちはxyz"}, + {"abc", "x", 5, "xabcx"}, + {"abc", "xyz", 5, "xabcx"}, + {"abcde", "xyz", 5, "abcde"}, + {"abcde", "xyz", 4, "abcde"}, + } + for _, test := range tests { + actual := PadBoth(test.param1, test.param2, test.param3) + if actual != test.expected { + t.Errorf("Expected PadBoth(%q,%q,%q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual) + } + } +} diff --git a/vendor/github.com/asaskevich/govalidator/validator.go b/vendor/github.com/asaskevich/govalidator/validator.go new file mode 100644 index 000000000..7c158c5f8 --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/validator.go @@ -0,0 +1,1167 @@ +// Package govalidator is package of validators and sanitizers for strings, structs and collections. +package govalidator + +import ( + "bytes" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "io/ioutil" + "net" + "net/url" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +var ( + fieldsRequiredByDefault bool + notNumberRegexp = regexp.MustCompile("[^0-9]+") + whiteSpacesAndMinus = regexp.MustCompile("[\\s-]+") + paramsRegexp = regexp.MustCompile("\\(.*\\)$") +) + +const maxURLRuneCount = 2083 +const minURLRuneCount = 3 +const RF3339WithoutZone = "2006-01-02T15:04:05" + +// SetFieldsRequiredByDefault causes validation to fail when struct fields +// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). +// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): +// type exampleStruct struct { +// Name string `` +// Email string `valid:"email"` +// This, however, will only fail when Email is empty or an invalid email address: +// type exampleStruct2 struct { +// Name string `valid:"-"` +// Email string `valid:"email"` +// Lastly, this will only fail when Email is an invalid email address but not when it's empty: +// type exampleStruct2 struct { +// Name string `valid:"-"` +// Email string `valid:"email,optional"` +func SetFieldsRequiredByDefault(value bool) { + fieldsRequiredByDefault = value +} + +// IsEmail check if the string is an email. +func IsEmail(str string) bool { + // TODO uppercase letters are not supported + return rxEmail.MatchString(str) +} + +// IsURL check if the string is an URL. +func IsURL(str string) bool { + if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") { + return false + } + strTemp := str + if strings.Index(str, ":") >= 0 && strings.Index(str, "://") == -1 { + // support no indicated urlscheme but with colon for port number + // http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString + strTemp = "http://" + str + } + u, err := url.Parse(strTemp) + if err != nil { + return false + } + if strings.HasPrefix(u.Host, ".") { + return false + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return false + } + return rxURL.MatchString(str) +} + +// IsRequestURL check if the string rawurl, assuming +// it was received in an HTTP request, is a valid +// URL confirm to RFC 3986 +func IsRequestURL(rawurl string) bool { + url, err := url.ParseRequestURI(rawurl) + if err != nil { + return false //Couldn't even parse the rawurl + } + if len(url.Scheme) == 0 { + return false //No Scheme found + } + return true +} + +// IsRequestURI check if the string rawurl, assuming +// it was received in an HTTP request, is an +// absolute URI or an absolute path. +func IsRequestURI(rawurl string) bool { + _, err := url.ParseRequestURI(rawurl) + return err == nil +} + +// IsAlpha check if the string contains only letters (a-zA-Z). Empty string is valid. +func IsAlpha(str string) bool { + if IsNull(str) { + return true + } + return rxAlpha.MatchString(str) +} + +//IsUTFLetter check if the string contains only unicode letter characters. +//Similar to IsAlpha but for all languages. Empty string is valid. +func IsUTFLetter(str string) bool { + if IsNull(str) { + return true + } + + for _, c := range str { + if !unicode.IsLetter(c) { + return false + } + } + return true + +} + +// IsAlphanumeric check if the string contains only letters and numbers. Empty string is valid. +func IsAlphanumeric(str string) bool { + if IsNull(str) { + return true + } + return rxAlphanumeric.MatchString(str) +} + +// IsUTFLetterNumeric check if the string contains only unicode letters and numbers. Empty string is valid. +func IsUTFLetterNumeric(str string) bool { + if IsNull(str) { + return true + } + for _, c := range str { + if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok + return false + } + } + return true + +} + +// IsNumeric check if the string contains only numbers. Empty string is valid. +func IsNumeric(str string) bool { + if IsNull(str) { + return true + } + return rxNumeric.MatchString(str) +} + +// IsUTFNumeric check if the string contains only unicode numbers of any kind. +// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid. +func IsUTFNumeric(str string) bool { + if IsNull(str) { + return true + } + if strings.IndexAny(str, "+-") > 0 { + return false + } + if len(str) > 1 { + str = strings.TrimPrefix(str, "-") + str = strings.TrimPrefix(str, "+") + } + for _, c := range str { + if unicode.IsNumber(c) == false { //numbers && minus sign are ok + return false + } + } + return true + +} + +// IsUTFDigit check if the string contains only unicode radix-10 decimal digits. Empty string is valid. +func IsUTFDigit(str string) bool { + if IsNull(str) { + return true + } + if strings.IndexAny(str, "+-") > 0 { + return false + } + if len(str) > 1 { + str = strings.TrimPrefix(str, "-") + str = strings.TrimPrefix(str, "+") + } + for _, c := range str { + if !unicode.IsDigit(c) { //digits && minus sign are ok + return false + } + } + return true + +} + +// IsHexadecimal check if the string is a hexadecimal number. +func IsHexadecimal(str string) bool { + return rxHexadecimal.MatchString(str) +} + +// IsHexcolor check if the string is a hexadecimal color. +func IsHexcolor(str string) bool { + return rxHexcolor.MatchString(str) +} + +// IsRGBcolor check if the string is a valid RGB color in form rgb(RRR, GGG, BBB). +func IsRGBcolor(str string) bool { + return rxRGBcolor.MatchString(str) +} + +// IsLowerCase check if the string is lowercase. Empty string is valid. +func IsLowerCase(str string) bool { + if IsNull(str) { + return true + } + return str == strings.ToLower(str) +} + +// IsUpperCase check if the string is uppercase. Empty string is valid. +func IsUpperCase(str string) bool { + if IsNull(str) { + return true + } + return str == strings.ToUpper(str) +} + +// IsInt check if the string is an integer. Empty string is valid. +func IsInt(str string) bool { + if IsNull(str) { + return true + } + return rxInt.MatchString(str) +} + +// IsFloat check if the string is a float. +func IsFloat(str string) bool { + return str != "" && rxFloat.MatchString(str) +} + +// IsDivisibleBy check if the string is a number that's divisible by another. +// If second argument is not valid integer or zero, it's return false. +// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero). +func IsDivisibleBy(str, num string) bool { + f, _ := ToFloat(str) + p := int64(f) + q, _ := ToInt(num) + if q == 0 { + return false + } + return (p == 0) || (p%q == 0) +} + +// IsNull check if the string is null. +func IsNull(str string) bool { + return len(str) == 0 +} + +// IsByteLength check if the string's length (in bytes) falls in a range. +func IsByteLength(str string, min, max int) bool { + return len(str) >= min && len(str) <= max +} + +// IsUUIDv3 check if the string is a UUID version 3. +func IsUUIDv3(str string) bool { + return rxUUID3.MatchString(str) +} + +// IsUUIDv4 check if the string is a UUID version 4. +func IsUUIDv4(str string) bool { + return rxUUID4.MatchString(str) +} + +// IsUUIDv5 check if the string is a UUID version 5. +func IsUUIDv5(str string) bool { + return rxUUID5.MatchString(str) +} + +// IsUUID check if the string is a UUID (version 3, 4 or 5). +func IsUUID(str string) bool { + return rxUUID.MatchString(str) +} + +// IsCreditCard check if the string is a credit card. +func IsCreditCard(str string) bool { + sanitized := notNumberRegexp.ReplaceAllString(str, "") + if !rxCreditCard.MatchString(sanitized) { + return false + } + var sum int64 + var digit string + var tmpNum int64 + var shouldDouble bool + for i := len(sanitized) - 1; i >= 0; i-- { + digit = sanitized[i:(i + 1)] + tmpNum, _ = ToInt(digit) + if shouldDouble { + tmpNum *= 2 + if tmpNum >= 10 { + sum += ((tmpNum % 10) + 1) + } else { + sum += tmpNum + } + } else { + sum += tmpNum + } + shouldDouble = !shouldDouble + } + + if sum%10 == 0 { + return true + } + return false +} + +// IsISBN10 check if the string is an ISBN version 10. +func IsISBN10(str string) bool { + return IsISBN(str, 10) +} + +// IsISBN13 check if the string is an ISBN version 13. +func IsISBN13(str string) bool { + return IsISBN(str, 13) +} + +// IsISBN check if the string is an ISBN (version 10 or 13). +// If version value is not equal to 10 or 13, it will be check both variants. +func IsISBN(str string, version int) bool { + sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") + var checksum int32 + var i int32 + if version == 10 { + if !rxISBN10.MatchString(sanitized) { + return false + } + for i = 0; i < 9; i++ { + checksum += (i + 1) * int32(sanitized[i]-'0') + } + if sanitized[9] == 'X' { + checksum += 10 * 10 + } else { + checksum += 10 * int32(sanitized[9]-'0') + } + if checksum%11 == 0 { + return true + } + return false + } else if version == 13 { + if !rxISBN13.MatchString(sanitized) { + return false + } + factor := []int32{1, 3} + for i = 0; i < 12; i++ { + checksum += factor[i%2] * int32(sanitized[i]-'0') + } + if (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0 { + return true + } + return false + } + return IsISBN(str, 10) || IsISBN(str, 13) +} + +// IsJSON check if the string is valid JSON (note: uses json.Unmarshal). +func IsJSON(str string) bool { + var js json.RawMessage + return json.Unmarshal([]byte(str), &js) == nil +} + +// IsMultibyte check if the string contains one or more multibyte chars. Empty string is valid. +func IsMultibyte(str string) bool { + if IsNull(str) { + return true + } + return rxMultibyte.MatchString(str) +} + +// IsASCII check if the string contains ASCII chars only. Empty string is valid. +func IsASCII(str string) bool { + if IsNull(str) { + return true + } + return rxASCII.MatchString(str) +} + +// IsPrintableASCII check if the string contains printable ASCII chars only. Empty string is valid. +func IsPrintableASCII(str string) bool { + if IsNull(str) { + return true + } + return rxPrintableASCII.MatchString(str) +} + +// IsFullWidth check if the string contains any full-width chars. Empty string is valid. +func IsFullWidth(str string) bool { + if IsNull(str) { + return true + } + return rxFullWidth.MatchString(str) +} + +// IsHalfWidth check if the string contains any half-width chars. Empty string is valid. +func IsHalfWidth(str string) bool { + if IsNull(str) { + return true + } + return rxHalfWidth.MatchString(str) +} + +// IsVariableWidth check if the string contains a mixture of full and half-width chars. Empty string is valid. +func IsVariableWidth(str string) bool { + if IsNull(str) { + return true + } + return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str) +} + +// IsBase64 check if a string is base64 encoded. +func IsBase64(str string) bool { + return rxBase64.MatchString(str) +} + +// IsFilePath check is a string is Win or Unix file path and returns it's type. +func IsFilePath(str string) (bool, int) { + if rxWinPath.MatchString(str) { + //check windows path limit see: + // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath + if len(str[3:]) > 32767 { + return false, Win + } + return true, Win + } else if rxUnixPath.MatchString(str) { + return true, Unix + } + return false, Unknown +} + +// IsDataURI checks if a string is base64 encoded data URI such as an image +func IsDataURI(str string) bool { + dataURI := strings.Split(str, ",") + if !rxDataURI.MatchString(dataURI[0]) { + return false + } + return IsBase64(dataURI[1]) +} + +// IsISO3166Alpha2 checks if a string is valid two-letter country code +func IsISO3166Alpha2(str string) bool { + for _, entry := range ISO3166List { + if str == entry.Alpha2Code { + return true + } + } + return false +} + +// IsISO3166Alpha3 checks if a string is valid three-letter country code +func IsISO3166Alpha3(str string) bool { + for _, entry := range ISO3166List { + if str == entry.Alpha3Code { + return true + } + } + return false +} + +// IsISO693Alpha2 checks if a string is valid two-letter language code +func IsISO693Alpha2(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha2Code { + return true + } + } + return false +} + +// IsISO693Alpha3b checks if a string is valid three-letter language code +func IsISO693Alpha3b(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha3bCode { + return true + } + } + return false +} + +// IsDNSName will validate the given string as a DNS name +func IsDNSName(str string) bool { + if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 { + // constraints already violated + return false + } + return !IsIP(str) && rxDNSName.MatchString(str) +} + +// IsHash checks if a string is a hash of type algorithm. +// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b'] +func IsHash(str string, algorithm string) bool { + len := "0" + algo := strings.ToLower(algorithm) + + if algo == "crc32" || algo == "crc32b" { + len = "8" + } else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" { + len = "32" + } else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" { + len = "40" + } else if algo == "tiger192" { + len = "48" + } else if algo == "sha256" { + len = "64" + } else if algo == "sha384" { + len = "96" + } else if algo == "sha512" { + len = "128" + } else { + return false + } + + return Matches(str, "^[a-f0-9]{" + len + "}$") +} + +// IsDialString validates the given string for usage with the various Dial() functions +func IsDialString(str string) bool { + + if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) { + return true + } + + return false +} + +// IsIP checks if a string is either IP version 4 or 6. +func IsIP(str string) bool { + return net.ParseIP(str) != nil +} + +// IsPort checks if a string represents a valid port +func IsPort(str string) bool { + if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 { + return true + } + return false +} + +// IsIPv4 check if the string is an IP version 4. +func IsIPv4(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ".") +} + +// IsIPv6 check if the string is an IP version 6. +func IsIPv6(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ":") +} + +// IsCIDR check if the string is an valid CIDR notiation (IPV4 & IPV6) +func IsCIDR(str string) bool { + _, _, err := net.ParseCIDR(str) + return err == nil +} + +// IsMAC check if a string is valid MAC address. +// Possible MAC formats: +// 01:23:45:67:89:ab +// 01:23:45:67:89:ab:cd:ef +// 01-23-45-67-89-ab +// 01-23-45-67-89-ab-cd-ef +// 0123.4567.89ab +// 0123.4567.89ab.cdef +func IsMAC(str string) bool { + _, err := net.ParseMAC(str) + return err == nil +} + +// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name +func IsHost(str string) bool { + return IsIP(str) || IsDNSName(str) +} + +// IsMongoID check if the string is a valid hex-encoded representation of a MongoDB ObjectId. +func IsMongoID(str string) bool { + return rxHexadecimal.MatchString(str) && (len(str) == 24) +} + +// IsLatitude check if a string is valid latitude. +func IsLatitude(str string) bool { + return rxLatitude.MatchString(str) +} + +// IsLongitude check if a string is valid longitude. +func IsLongitude(str string) bool { + return rxLongitude.MatchString(str) +} + +// IsRsaPublicKey check if a string is valid public key with provided length +func IsRsaPublicKey(str string, keylen int) bool { + bb := bytes.NewBufferString(str) + pemBytes, err := ioutil.ReadAll(bb) + if err != nil { + return false + } + block, _ := pem.Decode(pemBytes) + if block != nil && block.Type != "PUBLIC KEY" { + return false + } + var der []byte + + if block != nil { + der = block.Bytes + } else { + der, err = base64.StdEncoding.DecodeString(str) + if err != nil { + return false + } + } + + key, err := x509.ParsePKIXPublicKey(der) + if err != nil { + return false + } + pubkey, ok := key.(*rsa.PublicKey) + if !ok { + return false + } + bitlen := len(pubkey.N.Bytes()) * 8 + return bitlen == int(keylen) +} + +func toJSONName(tag string) string { + if tag == "" { + return "" + } + + // JSON name always comes first. If there's no options then split[0] is + // JSON name, if JSON name is not set, then split[0] is an empty string. + split := strings.SplitN(tag, ",", 2) + + name := split[0] + + // However it is possible that the field is skipped when + // (de-)serializing from/to JSON, in which case assume that there is no + // tag name to use + if name == "-" { + return "" + } + return name +} + +// ValidateStruct use tags for fields. +// result will be equal to `false` if there are any errors. +func ValidateStruct(s interface{}) (bool, error) { + if s == nil { + return true, nil + } + result := true + var err error + val := reflect.ValueOf(s) + if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { + val = val.Elem() + } + // we only accept structs + if val.Kind() != reflect.Struct { + return false, fmt.Errorf("function only accepts structs; got %s", val.Kind()) + } + var errs Errors + for i := 0; i < val.NumField(); i++ { + valueField := val.Field(i) + typeField := val.Type().Field(i) + if typeField.PkgPath != "" { + continue // Private field + } + structResult := true + if valueField.Kind() == reflect.Struct && typeField.Tag.Get(tagName) != "-" { + var err error + structResult, err = ValidateStruct(valueField.Interface()) + if err != nil { + errs = append(errs, err) + } + } + resultField, err2 := typeCheck(valueField, typeField, val, nil) + if err2 != nil { + + // Replace structure name with JSON name if there is a tag on the variable + jsonTag := toJSONName(typeField.Tag.Get("json")) + if jsonTag != "" { + switch jsonError := err2.(type) { + case Error: + jsonError.Name = jsonTag + err2 = jsonError + case Errors: + for i2, err3 := range jsonError { + switch customErr := err3.(type) { + case Error: + customErr.Name = jsonTag + jsonError[i2] = customErr + } + } + + err2 = jsonError + } + } + + errs = append(errs, err2) + } + result = result && resultField && structResult + } + if len(errs) > 0 { + err = errs + } + return result, err +} + +// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""} +func parseTagIntoMap(tag string) tagOptionsMap { + optionsMap := make(tagOptionsMap) + options := strings.Split(tag, ",") + + for _, option := range options { + option = strings.TrimSpace(option) + + validationOptions := strings.Split(option, "~") + if !isValidTag(validationOptions[0]) { + continue + } + if len(validationOptions) == 2 { + optionsMap[validationOptions[0]] = validationOptions[1] + } else { + optionsMap[validationOptions[0]] = "" + } + } + return optionsMap +} + +func isValidTag(s string) bool { + if s == "" { + return false + } + for _, c := range s { + switch { + case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + // Backslash and quote chars are reserved, but + // otherwise any punctuation chars are allowed + // in a tag name. + default: + if !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return false + } + } + } + return true +} + +// IsSSN will validate the given string as a U.S. Social Security Number +func IsSSN(str string) bool { + if str == "" || len(str) != 11 { + return false + } + return rxSSN.MatchString(str) +} + +// IsSemver check if string is valid semantic version +func IsSemver(str string) bool { + return rxSemver.MatchString(str) +} + +// IsTime check if string is valid according to given format +func IsTime(str string, format string) bool { + _, err := time.Parse(format, str) + return err == nil +} + +// IsRFC3339 check if string is valid timestamp value according to RFC3339 +func IsRFC3339(str string) bool { + return IsTime(str, time.RFC3339) +} + +// IsRFC3339WithoutZone check if string is valid timestamp value according to RFC3339 which excludes the timezone. +func IsRFC3339WithoutZone(str string) bool { + return IsTime(str, RF3339WithoutZone) +} + +// IsISO4217 check if string is valid ISO currency code +func IsISO4217(str string) bool { + for _, currency := range ISO4217List { + if str == currency { + return true + } + } + + return false +} + +// ByteLength check string's length +func ByteLength(str string, params ...string) bool { + if len(params) == 2 { + min, _ := ToInt(params[0]) + max, _ := ToInt(params[1]) + return len(str) >= int(min) && len(str) <= int(max) + } + + return false +} + +// RuneLength check string's length +// Alias for StringLength +func RuneLength(str string, params ...string) bool { + return StringLength(str, params...) +} + +// IsRsaPub check whether string is valid RSA key +// Alias for IsRsaPublicKey +func IsRsaPub(str string, params ...string) bool { + if len(params) == 1 { + len, _ := ToInt(params[0]) + return IsRsaPublicKey(str, int(len)) + } + + return false +} + +// StringMatches checks if a string matches a given pattern. +func StringMatches(s string, params ...string) bool { + if len(params) == 1 { + pattern := params[0] + return Matches(s, pattern) + } + return false +} + +// StringLength check string's length (including multi byte strings) +func StringLength(str string, params ...string) bool { + + if len(params) == 2 { + strLength := utf8.RuneCountInString(str) + min, _ := ToInt(params[0]) + max, _ := ToInt(params[1]) + return strLength >= int(min) && strLength <= int(max) + } + + return false +} + +// Range check string's length +func Range(str string, params ...string) bool { + if len(params) == 2 { + value, _ := ToFloat(str) + min, _ := ToFloat(params[0]) + max, _ := ToFloat(params[1]) + return InRange(value, min, max) + } + + return false +} + +func isInRaw(str string, params ...string) bool { + if len(params) == 1 { + rawParams := params[0] + + parsedParams := strings.Split(rawParams, "|") + + return IsIn(str, parsedParams...) + } + + return false +} + +// IsIn check if string str is a member of the set of strings params +func IsIn(str string, params ...string) bool { + for _, param := range params { + if str == param { + return true + } + } + + return false +} + +func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) { + if requiredOption, isRequired := options["required"]; isRequired { + if len(requiredOption) > 0 { + return false, Error{t.Name, fmt.Errorf(requiredOption), true, "required"} + } + return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required"} + } else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional { + return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required"} + } + // not required and empty is valid + return true, nil +} + +func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) { + if !v.IsValid() { + return false, nil + } + + tag := t.Tag.Get(tagName) + + // Check if the field should be ignored + switch tag { + case "": + if !fieldsRequiredByDefault { + return true, nil + } + return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required"} + case "-": + return true, nil + } + + isRootType := false + if options == nil { + isRootType = true + options = parseTagIntoMap(tag) + } + + if isEmptyValue(v) { + // an empty value is not validated, check only required + return checkRequired(v, t, options) + } + + var customTypeErrors Errors + for validatorName, customErrorMessage := range options { + if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok { + delete(options, validatorName) + + if result := validatefunc(v.Interface(), o.Interface()); !result { + if len(customErrorMessage) > 0 { + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf(customErrorMessage), CustomErrorMessageExists: true, Validator: stripParams(validatorName)}) + continue + } + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)}) + } + } + } + + if len(customTypeErrors.Errors()) > 0 { + return false, customTypeErrors + } + + if isRootType { + // Ensure that we've checked the value by all specified validators before report that the value is valid + defer func() { + delete(options, "optional") + delete(options, "required") + + if isValid && resultErr == nil && len(options) != 0 { + for validator := range options { + isValid = false + resultErr = Error{t.Name, fmt.Errorf( + "The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator)} + return + } + } + }() + } + + switch v.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, + reflect.String: + // for each tag option check the map of validator functions + for validatorSpec, customErrorMessage := range options { + var negate bool + validator := validatorSpec + customMsgExists := len(customErrorMessage) > 0 + + // Check whether the tag looks like '!something' or 'something' + if validator[0] == '!' { + validator = validator[1:] + negate = true + } + + // Check for param validators + for key, value := range ParamTagRegexMap { + ps := value.FindStringSubmatch(validator) + if len(ps) == 0 { + continue + } + + validatefunc, ok := ParamTagMap[key] + if !ok { + continue + } + + delete(options, validatorSpec) + + switch v.Kind() { + case reflect.String: + field := fmt.Sprint(v) // make value into string, then validate with regex + if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) { + if customMsgExists { + return false, Error{t.Name, fmt.Errorf(customErrorMessage), customMsgExists, stripParams(validatorSpec)} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec)} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec)} + } + default: + // type not yet supported, fail + return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec)} + } + } + + if validatefunc, ok := TagMap[validator]; ok { + delete(options, validatorSpec) + + switch v.Kind() { + case reflect.String: + field := fmt.Sprint(v) // make value into string, then validate with regex + if result := validatefunc(field); !result && !negate || result && negate { + if customMsgExists { + return false, Error{t.Name, fmt.Errorf(customErrorMessage), customMsgExists, stripParams(validatorSpec)} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec)} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec)} + } + default: + //Not Yet Supported Types (Fail here!) + err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v) + return false, Error{t.Name, err, false, stripParams(validatorSpec)} + } + } + } + return true, nil + case reflect.Map: + if v.Type().Key().Kind() != reflect.String { + return false, &UnsupportedTypeError{v.Type()} + } + var sv stringValues + sv = v.MapKeys() + sort.Sort(sv) + result := true + for _, k := range sv { + var resultItem bool + var err error + if v.MapIndex(k).Kind() != reflect.Struct { + resultItem, err = typeCheck(v.MapIndex(k), t, o, options) + if err != nil { + return false, err + } + } else { + resultItem, err = ValidateStruct(v.MapIndex(k).Interface()) + if err != nil { + return false, err + } + } + result = result && resultItem + } + return result, nil + case reflect.Slice, reflect.Array: + result := true + for i := 0; i < v.Len(); i++ { + var resultItem bool + var err error + if v.Index(i).Kind() != reflect.Struct { + resultItem, err = typeCheck(v.Index(i), t, o, options) + if err != nil { + return false, err + } + } else { + resultItem, err = ValidateStruct(v.Index(i).Interface()) + if err != nil { + return false, err + } + } + result = result && resultItem + } + return result, nil + case reflect.Interface: + // If the value is an interface then encode its element + if v.IsNil() { + return true, nil + } + return ValidateStruct(v.Interface()) + case reflect.Ptr: + // If the value is a pointer then check its element + if v.IsNil() { + return true, nil + } + return typeCheck(v.Elem(), t, o, options) + case reflect.Struct: + return ValidateStruct(v.Interface()) + default: + return false, &UnsupportedTypeError{v.Type()} + } +} + +func stripParams(validatorString string) string { + return paramsRegexp.ReplaceAllString(validatorString, "") +} + +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.String, reflect.Array: + return v.Len() == 0 + case reflect.Map, reflect.Slice: + return v.Len() == 0 || v.IsNil() + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) +} + +// ErrorByField returns error for specified field of the struct +// validated by ValidateStruct or empty string if there are no errors +// or this field doesn't exists or doesn't have any errors. +func ErrorByField(e error, field string) string { + if e == nil { + return "" + } + return ErrorsByField(e)[field] +} + +// ErrorsByField returns map of errors of the struct validated +// by ValidateStruct or empty map if there are no errors. +func ErrorsByField(e error) map[string]string { + m := make(map[string]string) + if e == nil { + return m + } + // prototype for ValidateStruct + + switch e.(type) { + case Error: + m[e.(Error).Name] = e.(Error).Err.Error() + case Errors: + for _, item := range e.(Errors).Errors() { + n := ErrorsByField(item) + for k, v := range n { + m[k] = v + } + } + } + + return m +} + +// Error returns string equivalent for reflect.Type +func (e *UnsupportedTypeError) Error() string { + return "validator: unsupported type: " + e.Type.String() +} + +func (sv stringValues) Len() int { return len(sv) } +func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } +func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } +func (sv stringValues) get(i int) string { return sv[i].String() } diff --git a/vendor/github.com/asaskevich/govalidator/validator_test.go b/vendor/github.com/asaskevich/govalidator/validator_test.go new file mode 100644 index 000000000..cf56f7a5a --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/validator_test.go @@ -0,0 +1,3040 @@ +package govalidator + +import ( + "fmt" + "strings" + "testing" + "time" +) + +func init() { + CustomTypeTagMap.Set("customFalseValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return false + })) + CustomTypeTagMap.Set("customTrueValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return true + })) +} + +func TestIsAlpha(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"\n", false}, + {"\r", false}, + {"Ⅸ", false}, + {"", true}, + {" fooo ", false}, + {"abc!!!", false}, + {"abc1", false}, + {"abc〩", false}, + {"abc", true}, + {"소주", false}, + {"ABC", true}, + {"FoObAr", true}, + {"소aBC", false}, + {"소", false}, + {"달기&Co.", false}, + {"〩Hours", false}, + {"\ufff0", false}, + {"\u0070", true}, //UTF-8(ASCII): p + {"\u0026", false}, //UTF-8(ASCII): & + {"\u0030", false}, //UTF-8(ASCII): 0 + {"123", false}, + {"0123", false}, + {"-00123", false}, + {"0", false}, + {"-0", false}, + {"123.123", false}, + {" ", false}, + {".", false}, + {"-1¾", false}, + {"1¾", false}, + {"〥〩", false}, + {"모자", false}, + {"ix", true}, + {"۳۵۶۰", false}, + {"1--", false}, + {"1-1", false}, + {"-", false}, + {"--", false}, + {"1++", false}, + {"1+1", false}, + {"+", false}, + {"++", false}, + {"+1", false}, + } + for _, test := range tests { + actual := IsAlpha(test.param) + if actual != test.expected { + t.Errorf("Expected IsAlpha(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsUTFLetter(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"\n", false}, + {"\r", false}, + {"Ⅸ", false}, + {"", true}, + {" fooo ", false}, + {"abc!!!", false}, + {"abc1", false}, + {"abc〩", false}, + {"", true}, + {"abc", true}, + {"소주", true}, + {"ABC", true}, + {"FoObAr", true}, + {"소aBC", true}, + {"소", true}, + {"달기&Co.", false}, + {"〩Hours", false}, + {"\ufff0", false}, + {"\u0070", true}, //UTF-8(ASCII): p + {"\u0026", false}, //UTF-8(ASCII): & + {"\u0030", false}, //UTF-8(ASCII): 0 + {"123", false}, + {"0123", false}, + {"-00123", false}, + {"0", false}, + {"-0", false}, + {"123.123", false}, + {" ", false}, + {".", false}, + {"-1¾", false}, + {"1¾", false}, + {"〥〩", false}, + {"모자", true}, + {"ix", true}, + {"۳۵۶۰", false}, + {"1--", false}, + {"1-1", false}, + {"-", false}, + {"--", false}, + {"1++", false}, + {"1+1", false}, + {"+", false}, + {"++", false}, + {"+1", false}, + } + for _, test := range tests { + actual := IsUTFLetter(test.param) + if actual != test.expected { + t.Errorf("Expected IsUTFLetter(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsAlphanumeric(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"\n", false}, + {"\r", false}, + {"Ⅸ", false}, + {"", true}, + {" fooo ", false}, + {"abc!!!", false}, + {"abc123", true}, + {"ABC111", true}, + {"abc1", true}, + {"abc〩", false}, + {"abc", true}, + {"소주", false}, + {"ABC", true}, + {"FoObAr", true}, + {"소aBC", false}, + {"소", false}, + {"달기&Co.", false}, + {"〩Hours", false}, + {"\ufff0", false}, + {"\u0070", true}, //UTF-8(ASCII): p + {"\u0026", false}, //UTF-8(ASCII): & + {"\u0030", true}, //UTF-8(ASCII): 0 + {"123", true}, + {"0123", true}, + {"-00123", false}, + {"0", true}, + {"-0", false}, + {"123.123", false}, + {" ", false}, + {".", false}, + {"-1¾", false}, + {"1¾", false}, + {"〥〩", false}, + {"모자", false}, + {"ix", true}, + {"۳۵۶۰", false}, + {"1--", false}, + {"1-1", false}, + {"-", false}, + {"--", false}, + {"1++", false}, + {"1+1", false}, + {"+", false}, + {"++", false}, + {"+1", false}, + } + for _, test := range tests { + actual := IsAlphanumeric(test.param) + if actual != test.expected { + t.Errorf("Expected IsAlphanumeric(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsUTFLetterNumeric(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"\n", false}, + {"\r", false}, + {"Ⅸ", true}, + {"", true}, + {" fooo ", false}, + {"abc!!!", false}, + {"abc1", true}, + {"abc〩", true}, + {"abc", true}, + {"소주", true}, + {"ABC", true}, + {"FoObAr", true}, + {"소aBC", true}, + {"소", true}, + {"달기&Co.", false}, + {"〩Hours", true}, + {"\ufff0", false}, + {"\u0070", true}, //UTF-8(ASCII): p + {"\u0026", false}, //UTF-8(ASCII): & + {"\u0030", true}, //UTF-8(ASCII): 0 + {"123", true}, + {"0123", true}, + {"-00123", false}, + {"0", true}, + {"-0", false}, + {"123.123", false}, + {" ", false}, + {".", false}, + {"-1¾", false}, + {"1¾", true}, + {"〥〩", true}, + {"모자", true}, + {"ix", true}, + {"۳۵۶۰", true}, + {"1--", false}, + {"1-1", false}, + {"-", false}, + {"--", false}, + {"1++", false}, + {"1+1", false}, + {"+", false}, + {"++", false}, + {"+1", false}, + } + for _, test := range tests { + actual := IsUTFLetterNumeric(test.param) + if actual != test.expected { + t.Errorf("Expected IsUTFLetterNumeric(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsNumeric(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"\n", false}, + {"\r", false}, + {"Ⅸ", false}, + {"", true}, + {" fooo ", false}, + {"abc!!!", false}, + {"abc1", false}, + {"abc〩", false}, + {"abc", false}, + {"소주", false}, + {"ABC", false}, + {"FoObAr", false}, + {"소aBC", false}, + {"소", false}, + {"달기&Co.", false}, + {"〩Hours", false}, + {"\ufff0", false}, + {"\u0070", false}, //UTF-8(ASCII): p + {"\u0026", false}, //UTF-8(ASCII): & + {"\u0030", true}, //UTF-8(ASCII): 0 + {"123", true}, + {"0123", true}, + {"-00123", false}, + {"+00123", false}, + {"0", true}, + {"-0", false}, + {"123.123", false}, + {" ", false}, + {".", false}, + {"12𐅪3", false}, + {"-1¾", false}, + {"1¾", false}, + {"〥〩", false}, + {"모자", false}, + {"ix", false}, + {"۳۵۶۰", false}, + {"1--", false}, + {"1-1", false}, + {"-", false}, + {"--", false}, + {"1++", false}, + {"1+1", false}, + {"+", false}, + {"++", false}, + {"+1", false}, + } + for _, test := range tests { + actual := IsNumeric(test.param) + if actual != test.expected { + t.Errorf("Expected IsNumeric(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsUTFNumeric(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"\n", false}, + {"\r", false}, + {"Ⅸ", true}, + {"", true}, + {" fooo ", false}, + {"abc!!!", false}, + {"abc1", false}, + {"abc〩", false}, + {"abc", false}, + {"소주", false}, + {"ABC", false}, + {"FoObAr", false}, + {"소aBC", false}, + {"소", false}, + {"달기&Co.", false}, + {"〩Hours", false}, + {"\ufff0", false}, + {"\u0070", false}, //UTF-8(ASCII): p + {"\u0026", false}, //UTF-8(ASCII): & + {"\u0030", true}, //UTF-8(ASCII): 0 + {"123", true}, + {"0123", true}, + {"-00123", true}, + {"0", true}, + {"-0", true}, + {"--0", false}, + {"-0-", false}, + {"123.123", false}, + {" ", false}, + {".", false}, + {"12𐅪3", true}, + {"-1¾", true}, + {"1¾", true}, + {"〥〩", true}, + {"모자", false}, + {"ix", false}, + {"۳۵۶۰", true}, + {"1++", false}, + {"1+1", false}, + {"+", false}, + {"++", false}, + {"+1", true}, + } + for _, test := range tests { + actual := IsUTFNumeric(test.param) + if actual != test.expected { + t.Errorf("Expected IsUTFNumeric(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsUTFDigit(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + + {"\n", false}, + {"\r", false}, + {"Ⅸ", false}, + {"", true}, + {" fooo ", false}, + {"abc!!!", false}, + {"abc1", false}, + {"abc〩", false}, + {"abc", false}, + {"소주", false}, + {"ABC", false}, + {"FoObAr", false}, + {"소aBC", false}, + {"소", false}, + {"달기&Co.", false}, + {"〩Hours", false}, + {"\ufff0", false}, + {"\u0070", false}, //UTF-8(ASCII): p + {"\u0026", false}, //UTF-8(ASCII): & + {"\u0030", true}, //UTF-8(ASCII): 0 + {"123", true}, + {"0123", true}, + {"-00123", true}, + {"0", true}, + {"-0", true}, + {"--0", false}, + {"-0-", false}, + {"123.123", false}, + {" ", false}, + {".", false}, + {"12𐅪3", false}, + {"1483920", true}, + {"", true}, + {"۳۵۶۰", true}, + {"-29", true}, + {"-1¾", false}, + {"1¾", false}, + {"〥〩", false}, + {"모자", false}, + {"ix", false}, + {"۳۵۶۰", true}, + {"1++", false}, + {"1+1", false}, + {"+", false}, + {"++", false}, + {"+1", true}, + } + for _, test := range tests { + actual := IsUTFDigit(test.param) + if actual != test.expected { + t.Errorf("Expected IsUTFDigit(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsLowerCase(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", true}, + {"abc123", true}, + {"abc", true}, + {"a b c", true}, + {"abcß", true}, + {"abcẞ", false}, + {"ABCẞ", false}, + {"tr竪s 端ber", true}, + {"fooBar", false}, + {"123ABC", false}, + {"ABC123", false}, + {"ABC", false}, + {"S T R", false}, + {"fooBar", false}, + {"abacaba123", true}, + } + for _, test := range tests { + actual := IsLowerCase(test.param) + if actual != test.expected { + t.Errorf("Expected IsLowerCase(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsUpperCase(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", true}, + {"abc123", false}, + {"abc", false}, + {"a b c", false}, + {"abcß", false}, + {"abcẞ", false}, + {"ABCẞ", true}, + {"tr竪s 端ber", false}, + {"fooBar", false}, + {"123ABC", true}, + {"ABC123", true}, + {"ABC", true}, + {"S T R", true}, + {"fooBar", false}, + {"abacaba123", false}, + } + for _, test := range tests { + actual := IsUpperCase(test.param) + if actual != test.expected { + t.Errorf("Expected IsUpperCase(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsInt(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"-2147483648", true}, //Signed 32 Bit Min Int + {"2147483647", true}, //Signed 32 Bit Max Int + {"-2147483649", true}, //Signed 32 Bit Min Int - 1 + {"2147483648", true}, //Signed 32 Bit Max Int + 1 + {"4294967295", true}, //Unsigned 32 Bit Max Int + {"4294967296", true}, //Unsigned 32 Bit Max Int + 1 + {"-9223372036854775808", true}, //Signed 64 Bit Min Int + {"9223372036854775807", true}, //Signed 64 Bit Max Int + {"-9223372036854775809", true}, //Signed 64 Bit Min Int - 1 + {"9223372036854775808", true}, //Signed 64 Bit Max Int + 1 + {"18446744073709551615", true}, //Unsigned 64 Bit Max Int + {"18446744073709551616", true}, //Unsigned 64 Bit Max Int + 1 + {"", true}, + {"123", true}, + {"0", true}, + {"-0", true}, + {"+0", true}, + {"01", false}, + {"123.123", false}, + {" ", false}, + {"000", false}, + } + for _, test := range tests { + actual := IsInt(test.param) + if actual != test.expected { + t.Errorf("Expected IsInt(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + + +func TestIsHash(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + algo string + expected bool + }{ + {"3ca25ae354e192b26879f651a51d92aa8a34d8d3", "sha1", true}, + {"3ca25ae354e192b26879f651a51d34d8d3", "sha1", false}, + {"3ca25ae354e192b26879f651a51d92aa8a34d8d3", "Tiger160", true}, + {"3ca25ae354e192b26879f651a51d34d8d3", "ripemd160", false}, + {"579282cfb65ca1f109b78536effaf621b853c9f7079664a3fbe2b519f435898c", "sha256", true}, + {"579282cfb65ca1f109b78536effaf621b853c9f7079664a3fbe2b519f435898casfdsafsadfsdf", "sha256", false}, + {"bf547c3fc5841a377eb1519c2890344dbab15c40ae4150b4b34443d2212e5b04aa9d58865bf03d8ae27840fef430b891", "sha384", true}, + {"579282cfb65ca1f109b78536effaf621b853c9f7079664a3fbe2b519f435898casfdsafsadfsdf", "sha384", false}, + {"45bc5fa8cb45ee408c04b6269e9f1e1c17090c5ce26ffeeda2af097735b29953ce547e40ff3ad0d120e5361cc5f9cee35ea91ecd4077f3f589b4d439168f91b9", "sha512", true}, + {"579282cfb65ca1f109b78536effaf621b853c9f7079664a3fbe2b519f435898casfdsafsadfsdf", "sha512", false}, + {"46fc0125a148788a3ac1d649566fc04eb84a746f1a6e4fa7", "tiger192", true}, + {"46fc0125a148788a3ac1d649566fc04eb84a746f1a6$$%@^", "TIGER192", false}, + {"46fc0125a148788a3ac1d649566fc04eb84a746f1a6$$%@^", "SOMEHASH", false}, + } + for _, test := range tests { + actual := IsHash(test.param, test.algo) + if actual != test.expected { + t.Errorf("Expected IsHash(%q, %q) to be %v, got %v", test.param, test.algo, test.expected, actual) + } + } +} + +func TestIsEmail(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"foo@bar.com", true}, + {"x@x.x", true}, + {"foo@bar.com.au", true}, + {"foo+bar@bar.com", true}, + {"foo@bar.coffee", true}, + {"foo@bar.coffee..coffee", false}, + {"foo@bar.bar.coffee", true}, + {"foo@bar.中文网", true}, + {"invalidemail@", false}, + {"invalid.com", false}, + {"@invalid.com", false}, + {"test|123@m端ller.com", true}, + {"hans@m端ller.com", true}, + {"hans.m端ller@test.com", true}, + {"NathAn.daVIeS@DomaIn.cOM", true}, + {"NATHAN.DAVIES@DOMAIN.CO.UK", true}, + } + for _, test := range tests { + actual := IsEmail(test.param) + if actual != test.expected { + t.Errorf("Expected IsEmail(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsURL(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"http://foo.bar#com", true}, + {"http://foobar.com", true}, + {"https://foobar.com", true}, + {"foobar.com", true}, + {"http://foobar.coffee/", true}, + {"http://foobar.中文网/", true}, + {"http://foobar.org/", true}, + {"http://foobar.ORG", true}, + {"http://foobar.org:8080/", true}, + {"ftp://foobar.ru/", true}, + {"ftp.foo.bar", true}, + {"http://user:pass@www.foobar.com/", true}, + {"http://user:pass@www.foobar.com/path/file", 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}, + {"http://foobar.com/a-", true}, + {"http://foobar.پاکستان/", true}, + {"http://foobar.c_o_m", false}, + {"", false}, + {"xyz://foobar.com", false}, + // {"invalid.", false}, is it false like "localhost."? + {".com", false}, + {"rtmp://foobar.com", false}, + {"http://www.foo_bar.com/", false}, + {"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/", false}, + {"http://www.foo---bar.com/", false}, + {"http://r6---snnvoxuioq6.googlevideo.com", true}, + {"mailto:someone@example.com", true}, + {"irc://irc.server.org/channel", false}, + {"irc://#channel@network", true}, + {"/abs/test/dir", false}, + {"./rel/test/dir", false}, + {"http://foo^bar.org", false}, + {"http://foo&*bar.org", false}, + {"http://foo&bar.org", false}, + {"http://foo bar.org", false}, + {"http://foo.bar.org", true}, + {"http://www.foo.bar.org", true}, + {"http://www.foo.co.uk", true}, + {"foo", false}, + {"http://.foo.com", false}, + {"http://,foo.com", false}, + {",foo.com", false}, + {"http://myservice.:9093/", true}, + // according to issues #62 #66 + {"https://pbs.twimg.com/profile_images/560826135676588032/j8fWrmYY_normal.jpeg", true}, + // according to #125 + {"http://prometheus-alertmanager.service.q:9093", true}, + {"aio1_alertmanager_container-63376c45:9093", true}, + {"https://www.logn-123-123.url.with.sigle.letter.d:12345/url/path/foo?bar=zzz#user", true}, + {"http://me.example.com", true}, + {"http://www.me.example.com", true}, + {"https://farm6.static.flickr.com", true}, + {"https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5", true}, + {"google", false}, + // According to #87 + {"http://hyphenated-host-name.example.co.in", true}, + {"http://cant-end-with-hyphen-.example.com", false}, + {"http://-cant-start-with-hyphen.example.com", false}, + {"http://www.domain-can-have-dashes.com", true}, + {"http://m.abcd.com/test.html", true}, + {"http://m.abcd.com/a/b/c/d/test.html?args=a&b=c", true}, + {"http://[::1]:9093", true}, + {"http://[::1]:909388", false}, + {"1200::AB00:1234::2552:7777:1313", false}, + {"http://[2001:db8:a0b:12f0::1]/index.html", true}, + {"http://[1200:0000:AB00:1234:0000:2552:7777:1313]", true}, + {"http://user:pass@[::1]:9093/a/b/c/?a=v#abc", true}, + {"https://127.0.0.1/a/b/c?a=v&c=11d", true}, + {"https://foo_bar.example.com", true}, + {"http://foo_bar.example.com", true}, + {"http://foo_bar_fizz_buzz.example.com", true}, + {"http://_cant_start_with_underescore", false}, + {"http://cant_end_with_underescore_", false}, + {"foo_bar.example.com", true}, + {"foo_bar_fizz_buzz.example.com", true}, + {"http://hello_world.example.com", true}, + // According to #212 + {"foo_bar-fizz-buzz:1313", true}, + {"foo_bar-fizz-buzz:13:13", false}, + {"foo_bar-fizz-buzz://1313", false}, + } + for _, test := range tests { + actual := IsURL(test.param) + if actual != test.expected { + t.Errorf("Expected IsURL(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsRequestURL(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"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", false}, + {"./rel/test/dir", false}, + } + for _, test := range tests { + actual := IsRequestURL(test.param) + if actual != test.expected { + t.Errorf("Expected IsRequestURL(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsRequestURI(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"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}, + {"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 _, test := range tests { + actual := IsRequestURI(test.param) + if actual != test.expected { + t.Errorf("Expected IsRequestURI(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsFloat(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {" ", false}, + {"-.123", false}, + {"abacaba", false}, + {"1f", false}, + {"-1f", false}, + {"+1f", false}, + {"123", true}, + {"123.", true}, + {"123.123", true}, + {"-123.123", true}, + {"+123.123", true}, + {"0.123", true}, + {"-0.123", true}, + {"+0.123", true}, + {".0", true}, + {"01.123", true}, + {"-0.22250738585072011e-307", true}, + {"+0.22250738585072011e-307", true}, + } + for _, test := range tests { + actual := IsFloat(test.param) + if actual != test.expected { + t.Errorf("Expected IsFloat(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsHexadecimal(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"abcdefg", false}, + {"", false}, + {"..", false}, + {"deadBEEF", true}, + {"ff0044", true}, + } + for _, test := range tests { + actual := IsHexadecimal(test.param) + if actual != test.expected { + t.Errorf("Expected IsHexadecimal(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsHexcolor(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"#ff", false}, + {"fff0", false}, + {"#ff12FG", false}, + {"CCccCC", true}, + {"fff", true}, + {"#f00", true}, + } + for _, test := range tests { + actual := IsHexcolor(test.param) + if actual != test.expected { + t.Errorf("Expected IsHexcolor(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsRGBcolor(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"rgb(0,31,255)", true}, + {"rgb(1,349,275)", false}, + {"rgb(01,31,255)", false}, + {"rgb(0.6,31,255)", false}, + {"rgba(0,31,255)", false}, + {"rgb(0, 31, 255)", true}, + } + for _, test := range tests { + actual := IsRGBcolor(test.param) + if actual != test.expected { + t.Errorf("Expected IsRGBcolor(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsNull(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"abacaba", false}, + {"", true}, + } + for _, test := range tests { + actual := IsNull(test.param) + if actual != test.expected { + t.Errorf("Expected IsNull(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsDivisibleBy(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 string + expected bool + }{ + {"4", "2", true}, + {"100", "10", true}, + {"", "1", true}, + {"123", "foo", false}, + {"123", "0", false}, + } + for _, test := range tests { + actual := IsDivisibleBy(test.param1, test.param2) + if actual != test.expected { + t.Errorf("Expected IsDivisibleBy(%q, %q) to be %v, got %v", test.param1, test.param2, test.expected, actual) + } + } +} + +// This small example illustrate how to work with IsDivisibleBy function. +func ExampleIsDivisibleBy() { + println("1024 is divisible by 64: ", IsDivisibleBy("1024", "64")) +} + +func TestIsByteLength(t *testing.T) { + t.Parallel() + + var tests = []struct { + param1 string + param2 int + param3 int + expected bool + }{ + {"abacaba", 100, -1, false}, + {"abacaba", 1, 3, false}, + {"abacaba", 1, 7, true}, + {"abacaba", 0, 8, true}, + {"\ufff0", 1, 1, false}, + } + for _, test := range tests { + actual := IsByteLength(test.param1, test.param2, test.param3) + if actual != test.expected { + t.Errorf("Expected IsByteLength(%q, %q, %q) to be %v, got %v", test.param1, test.param2, test.param3, test.expected, actual) + } + } +} + +func TestIsJSON(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"145", true}, + {"asdf", false}, + {"123:f00", false}, + {"{\"Name\":\"Alice\",\"Body\":\"Hello\",\"Time\":1294706395881547000}", true}, + {"{}", true}, + {"{\"Key\":{\"Key\":{\"Key\":123}}}", true}, + {"[]", true}, + {"null", true}, + } + for _, test := range tests { + actual := IsJSON(test.param) + if actual != test.expected { + t.Errorf("Expected IsJSON(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsMultibyte(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"abc", false}, + {"123", false}, + {"<>@;.-=", false}, + {"ひらがな・カタカナ、.漢字", true}, + {"あいうえお foobar", true}, + {"test@example.com", true}, + {"test@example.com", true}, + {"1234abcDExyz", true}, + {"カタカナ", true}, + {"", true}, + } + for _, test := range tests { + actual := IsMultibyte(test.param) + if actual != test.expected { + t.Errorf("Expected IsMultibyte(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsASCII(t *testing.T) { + t.Parallel() + + var 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 _, test := range tests { + actual := IsASCII(test.param) + if actual != test.expected { + t.Errorf("Expected IsASCII(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsPrintableASCII(t *testing.T) { + t.Parallel() + + var 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 _, test := range tests { + actual := IsPrintableASCII(test.param) + if actual != test.expected { + t.Errorf("Expected IsPrintableASCII(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsFullWidth(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", true}, + {"abc", false}, + {"abc123", false}, + {"!\"#$%&()<>/+=-_? ~^|.,@`{}[]", false}, + {"ひらがな・カタカナ、.漢字", true}, + {"3ー0 a@com", true}, + {"Fカタカナ゙ᆲ", true}, + {"Good=Parts", true}, + {"", true}, + } + for _, test := range tests { + actual := IsFullWidth(test.param) + if actual != test.expected { + t.Errorf("Expected IsFullWidth(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsHalfWidth(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", true}, + {"あいうえお", false}, + {"0011", false}, + {"!\"#$%&()<>/+=-_? ~^|.,@`{}[]", true}, + {"l-btn_02--active", true}, + {"abc123い", true}, + {"カタカナ゙ᆲ←", true}, + {"", true}, + } + for _, test := range tests { + actual := IsHalfWidth(test.param) + if actual != test.expected { + t.Errorf("Expected IsHalfWidth(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsVariableWidth(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", true}, + {"ひらがなカタカナ漢字ABCDE", true}, + {"3ー0123", true}, + {"Fカタカナ゙ᆲ", true}, + {"", true}, + {"Good=Parts", true}, + {"abc", false}, + {"abc123", false}, + {"!\"#$%&()<>/+=-_? ~^|.,@`{}[]", false}, + {"ひらがな・カタカナ、.漢字", false}, + {"123456", false}, + {"カタカナ゙ᆲ", false}, + } + for _, test := range tests { + actual := IsVariableWidth(test.param) + if actual != test.expected { + t.Errorf("Expected IsVariableWidth(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsUUID(t *testing.T) { + t.Parallel() + + // Tests without version + var 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 _, test := range tests { + actual := IsUUID(test.param) + if actual != test.expected { + t.Errorf("Expected IsUUID(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + // UUID ver. 3 + 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 _, test := range tests { + actual := IsUUIDv3(test.param) + if actual != test.expected { + t.Errorf("Expected IsUUIDv3(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + // UUID ver. 4 + 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 _, test := range tests { + actual := IsUUIDv4(test.param) + if actual != test.expected { + t.Errorf("Expected IsUUIDv4(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + // UUID ver. 5 + 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 _, test := range tests { + actual := IsUUIDv5(test.param) + if actual != test.expected { + t.Errorf("Expected IsUUIDv5(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsCreditCard(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"foo", false}, + {"5398228707871528", false}, + {"375556917985515", true}, + {"36050234196908", true}, + {"4716461583322103", true}, + {"4716-2210-5188-5662", true}, + {"4929 7226 5379 7141", true}, + {"5398228707871527", true}, + } + for _, test := range tests { + actual := IsCreditCard(test.param) + if actual != test.expected { + t.Errorf("Expected IsCreditCard(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISBN(t *testing.T) { + t.Parallel() + + // Without version + var 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 _, test := range tests { + actual := IsISBN(test.param, -1) + if actual != test.expected { + t.Errorf("Expected IsISBN(%q, -1) to be %v, got %v", test.param, test.expected, actual) + } + } + + // ISBN 10 + 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 _, test := range tests { + actual := IsISBN10(test.param) + if actual != test.expected { + t.Errorf("Expected IsISBN10(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + // ISBN 13 + 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 _, test := range tests { + actual := IsISBN13(test.param) + if actual != test.expected { + t.Errorf("Expected IsISBN13(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsDataURI(t *testing.T) { + t.Parallel() + + var 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 _, test := range tests { + actual := IsDataURI(test.param) + if actual != test.expected { + t.Errorf("Expected IsDataURI(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsBase64(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=", true}, + {"Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==", true}, + {"U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==", true}, + {"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw" + + "UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye" + + "rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619" + + "FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx" + + "QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ" + + "Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ" + "HQIDAQAB", true}, + {"12345", false}, + {"", false}, + {"Vml2YW11cyBmZXJtZtesting123", false}, + } + for _, test := range tests { + actual := IsBase64(test.param) + if actual != test.expected { + t.Errorf("Expected IsBase64(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISO3166Alpha2(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"ABCD", false}, + {"A", false}, + {"AC", false}, + {"AP", false}, + {"GER", false}, + {"NU", true}, + {"DE", true}, + {"JP", true}, + {"JPN", false}, + {"ZWE", false}, + {"GER", false}, + {"DEU", false}, + } + for _, test := range tests { + actual := IsISO3166Alpha2(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO3166Alpha2(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISO3166Alpha3(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"ABCD", false}, + {"A", false}, + {"AC", false}, + {"AP", false}, + {"NU", false}, + {"DE", false}, + {"JP", false}, + {"ZWE", true}, + {"JPN", true}, + {"GER", false}, + {"DEU", true}, + } + for _, test := range tests { + actual := IsISO3166Alpha3(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO3166Alpha3(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISO693Alpha2(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"abcd", false}, + {"a", false}, + {"ac", false}, + {"ap", false}, + {"de", true}, + {"DE", false}, + {"mk", true}, + {"mac", false}, + {"sw", true}, + {"SW", false}, + {"ger", false}, + {"deu", false}, + } + for _, test := range tests { + actual := IsISO693Alpha2(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO693Alpha2(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISO693Alpha3b(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"abcd", false}, + {"a", false}, + {"ac", false}, + {"ap", false}, + {"de", false}, + {"DE", false}, + {"mkd", false}, + {"mac", true}, + {"sw", false}, + {"SW", false}, + {"ger", true}, + {"deu", false}, + } + for _, test := range tests { + actual := IsISO693Alpha3b(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO693Alpha3b(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsIP(t *testing.T) { + t.Parallel() + + // Without version + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"127.0.0.1", true}, + {"0.0.0.0", true}, + {"255.255.255.255", true}, + {"1.2.3.4", true}, + {"::1", true}, + {"2001:db8:0000:1:1:1:1:1", true}, + {"300.0.0.0", false}, + } + for _, test := range tests { + actual := IsIP(test.param) + if actual != test.expected { + t.Errorf("Expected IsIP(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + // IPv4 + tests = []struct { + param string + expected bool + }{ + {"", false}, + {"127.0.0.1", true}, + {"0.0.0.0", true}, + {"255.255.255.255", true}, + {"1.2.3.4", true}, + {"::1", false}, + {"2001:db8:0000:1:1:1:1:1", false}, + {"300.0.0.0", false}, + } + for _, test := range tests { + actual := IsIPv4(test.param) + if actual != test.expected { + t.Errorf("Expected IsIPv4(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + // IPv6 + tests = []struct { + param string + expected bool + }{ + {"", false}, + {"127.0.0.1", false}, + {"0.0.0.0", false}, + {"255.255.255.255", false}, + {"1.2.3.4", false}, + {"::1", true}, + {"2001:db8:0000:1:1:1:1:1", true}, + {"300.0.0.0", false}, + } + for _, test := range tests { + actual := IsIPv6(test.param) + if actual != test.expected { + t.Errorf("Expected IsIPv6(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsPort(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"1", true}, + {"65535", true}, + {"0", false}, + {"65536", false}, + {"65538", false}, + } + + for _, test := range tests { + actual := IsPort(test.param) + if actual != test.expected { + t.Errorf("Expected IsPort(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsDNSName(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"localhost", true}, + {"a.bc", true}, + {"a.b.", true}, + {"a.b..", false}, + {"localhost.local", true}, + {"localhost.localdomain.intern", true}, + {"l.local.intern", true}, + {"ru.link.n.svpncloud.com", true}, + {"-localhost", false}, + {"localhost.-localdomain", false}, + {"localhost.localdomain.-int", false}, + {"_localhost", true}, + {"localhost._localdomain", true}, + {"localhost.localdomain._int", true}, + {"lÖcalhost", false}, + {"localhost.lÖcaldomain", false}, + {"localhost.localdomain.üntern", false}, + {"__", true}, + {"localhost/", false}, + {"127.0.0.1", false}, + {"[::1]", false}, + {"50.50.50.50", false}, + {"localhost.localdomain.intern:65535", false}, + {"漢字汉字", false}, + {"www.jubfvq1v3p38i51622y0dvmdk1mymowjyeu26gbtw9andgynj1gg8z3msb1kl5z6906k846pj3sulm4kiyk82ln5teqj9nsht59opr0cs5ssltx78lfyvml19lfq1wp4usbl0o36cmiykch1vywbttcus1p9yu0669h8fj4ll7a6bmop505908s1m83q2ec2qr9nbvql2589adma3xsq2o38os2z3dmfh2tth4is4ixyfasasasefqwe4t2ub2fz1rme.de", false}, + } + + for _, test := range tests { + actual := IsDNSName(test.param) + if actual != test.expected { + t.Errorf("Expected IsDNS(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsHost(t *testing.T) { + t.Parallel() + var tests = []struct { + param string + expected bool + }{ + {"localhost", true}, + {"localhost.localdomain", true}, + {"2001:db8:0000:1:1:1:1:1", true}, + {"::1", true}, + {"play.golang.org", true}, + {"localhost.localdomain.intern:65535", false}, + {"-[::1]", false}, + {"-localhost", false}, + {".localhost", false}, + } + for _, test := range tests { + actual := IsHost(test.param) + if actual != test.expected { + t.Errorf("Expected IsHost(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + +} + +func TestIsDialString(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"localhost.local:1", true}, + {"localhost.localdomain:9090", true}, + {"localhost.localdomain.intern:65535", true}, + {"127.0.0.1:30000", true}, + {"[::1]:80", true}, + {"[1200::AB00:1234::2552:7777:1313]:22", false}, + {"-localhost:1", false}, + {"localhost.-localdomain:9090", false}, + {"localhost.localdomain.-int:65535", false}, + {"localhost.loc:100000", false}, + {"漢字汉字:2", false}, + {"www.jubfvq1v3p38i51622y0dvmdk1mymowjyeu26gbtw9andgynj1gg8z3msb1kl5z6906k846pj3sulm4kiyk82ln5teqj9nsht59opr0cs5ssltx78lfyvml19lfq1wp4usbl0o36cmiykch1vywbttcus1p9yu0669h8fj4ll7a6bmop505908s1m83q2ec2qr9nbvql2589adma3xsq2o38os2z3dmfh2tth4is4ixyfasasasefqwe4t2ub2fz1rme.de:20000", false}, + } + + for _, test := range tests { + actual := IsDialString(test.param) + if actual != test.expected { + t.Errorf("Expected IsDialString(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsMAC(t *testing.T) { + t.Parallel() + + var 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}, + } + for _, test := range tests { + actual := IsMAC(test.param) + if actual != test.expected { + t.Errorf("Expected IsMAC(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestFilePath(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + osType int + }{ + {"c:\\" + strings.Repeat("a", 32767), true, Win}, //See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath + {"c:\\" + strings.Repeat("a", 32768), false, Win}, + {"c:\\path\\file (x86)\bar", true, Win}, + {"c:\\path\\file", true, Win}, + {"c:\\path\\file:exe", false, Unknown}, + {"C:\\", true, Win}, + {"c:\\path\\file\\", true, Win}, + {"c:/path/file/", false, Unknown}, + {"/path/file/", true, Unix}, + {"/path/file:SAMPLE/", true, Unix}, + {"/path/file:/.txt", true, Unix}, + {"/path", true, Unix}, + {"/path/__bc/file.txt", true, Unix}, + {"/path/a--ac/file.txt", true, Unix}, + {"/_path/file.txt", true, Unix}, + {"/path/__bc/file.txt", true, Unix}, + {"/path/a--ac/file.txt", true, Unix}, + {"/__path/--file.txt", true, Unix}, + {"/path/a bc", true, Unix}, + } + for _, test := range tests { + actual, osType := IsFilePath(test.param) + if actual != test.expected || osType != test.osType { + t.Errorf("Expected IsFilePath(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsLatitude(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"-90.000", true}, + {"+90", true}, + {"47.1231231", true}, + {"+99.9", false}, + {"108", false}, + } + for _, test := range tests { + actual := IsLatitude(test.param) + if actual != test.expected { + t.Errorf("Expected IsLatitude(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsLongitude(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"-180.000", true}, + {"180.1", false}, + {"+73.234", true}, + {"+382.3811", false}, + {"23.11111111", true}, + } + for _, test := range tests { + actual := IsLongitude(test.param) + if actual != test.expected { + t.Errorf("Expected IsLongitude(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsSSN(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"00-90-8787", false}, + {"66690-76", false}, + {"191 60 2869", true}, + {"191-60-2869", true}, + } + for _, test := range tests { + actual := IsSSN(test.param) + if actual != test.expected { + t.Errorf("Expected IsSSN(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsMongoID(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"507f1f77bcf86cd799439011", true}, + {"507f1f77bcf86cd7994390", false}, + {"507f1f77bcf86cd79943901z", false}, + {"507f1f77bcf86cd799439011 ", false}, + {"", false}, + } + for _, test := range tests { + actual := IsMongoID(test.param) + if actual != test.expected { + t.Errorf("Expected IsMongoID(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsSemver(t *testing.T) { + t.Parallel() + var tests = []struct { + param string + expected bool + }{ + {"v1.0.0", true}, + {"1.0.0", true}, + {"1.1.01", false}, + {"1.01.0", false}, + {"01.1.0", false}, + {"v1.1.01", false}, + {"v1.01.0", false}, + {"v01.1.0", false}, + {"1.0.0-alpha", true}, + {"1.0.0-alpha.1", true}, + {"1.0.0-0.3.7", true}, + {"1.0.0-0.03.7", false}, + {"1.0.0-00.3.7", false}, + {"1.0.0-x.7.z.92", true}, + {"1.0.0-alpha+001", true}, + {"1.0.0+20130313144700", true}, + {"1.0.0-beta+exp.sha.5114f85", true}, + {"1.0.0-beta+exp.sha.05114f85", true}, + {"1.0.0-+beta", false}, + {"1.0.0-b+-9+eta", false}, + {"v+1.8.0-b+-9+eta", false}, + } + for _, test := range tests { + actual := IsSemver(test.param) + if actual != test.expected { + t.Errorf("Expected IsSemver(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsTime(t *testing.T) { + t.Parallel() + var tests = []struct { + param string + format string + expected bool + }{ + {"2016-12-31 11:00", time.RFC3339, false}, + {"2016-12-31 11:00:00", time.RFC3339, false}, + {"2016-12-31T11:00", time.RFC3339, false}, + {"2016-12-31T11:00:00", time.RFC3339, false}, + {"2016-12-31T11:00:00Z", time.RFC3339, true}, + {"2016-12-31T11:00:00+01:00", time.RFC3339, true}, + {"2016-12-31T11:00:00-01:00", time.RFC3339, true}, + {"2016-12-31T11:00:00.05Z", time.RFC3339, true}, + {"2016-12-31T11:00:00.05-01:00", time.RFC3339, true}, + {"2016-12-31T11:00:00.05+01:00", time.RFC3339, true}, + {"2016-12-31T11:00:00", RF3339WithoutZone, true}, + {"2016-12-31T11:00:00Z", RF3339WithoutZone, false}, + {"2016-12-31T11:00:00+01:00", RF3339WithoutZone, false}, + {"2016-12-31T11:00:00-01:00", RF3339WithoutZone, false}, + {"2016-12-31T11:00:00.05Z", RF3339WithoutZone, false}, + {"2016-12-31T11:00:00.05-01:00", RF3339WithoutZone, false}, + {"2016-12-31T11:00:00.05+01:00", RF3339WithoutZone, false}, + } + for _, test := range tests { + actual := IsTime(test.param, test.format) + if actual != test.expected { + t.Errorf("Expected IsTime(%q, time.RFC3339) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsRFC3339(t *testing.T) { + t.Parallel() + var tests = []struct { + param string + expected bool + }{ + {"2016-12-31 11:00", false}, + {"2016-12-31 11:00:00", false}, + {"2016-12-31T11:00", false}, + {"2016-12-31T11:00:00", false}, + {"2016-12-31T11:00:00Z", true}, + {"2016-12-31T11:00:00+01:00", true}, + {"2016-12-31T11:00:00-01:00", true}, + {"2016-12-31T11:00:00.05Z", true}, + {"2016-12-31T11:00:00.05-01:00", true}, + {"2016-12-31T11:00:00.05+01:00", true}, + } + for _, test := range tests { + actual := IsRFC3339(test.param) + if actual != test.expected { + t.Errorf("Expected IsRFC3339(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestIsISO4217(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"", false}, + {"ABCD", false}, + {"A", false}, + {"ZZZ", false}, + {"usd", false}, + {"USD", true}, + } + for _, test := range tests { + actual := IsISO4217(test.param) + if actual != test.expected { + t.Errorf("Expected IsISO4217(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestByteLength(t *testing.T) { + t.Parallel() + + var tests = []struct { + value string + min string + max string + expected bool + }{ + {"123456", "0", "100", true}, + {"1239999", "0", "0", false}, + {"1239asdfasf99", "100", "200", false}, + {"1239999asdff29", "10", "30", true}, + {"你", "0", "1", false}, + } + for _, test := range tests { + actual := ByteLength(test.value, test.min, test.max) + if actual != test.expected { + t.Errorf("Expected ByteLength(%s, %s, %s) to be %v, got %v", test.value, test.min, test.max, test.expected, actual) + } + } +} + +func TestRuneLength(t *testing.T) { + t.Parallel() + + var tests = []struct { + value string + min string + max string + expected bool + }{ + {"123456", "0", "100", true}, + {"1239999", "0", "0", false}, + {"1239asdfasf99", "100", "200", false}, + {"1239999asdff29", "10", "30", true}, + {"你", "0", "1", true}, + } + for _, test := range tests { + actual := RuneLength(test.value, test.min, test.max) + if actual != test.expected { + t.Errorf("Expected RuneLength(%s, %s, %s) to be %v, got %v", test.value, test.min, test.max, test.expected, actual) + } + } +} + +func TestStringLength(t *testing.T) { + t.Parallel() + + var tests = []struct { + value string + min string + max string + expected bool + }{ + {"123456", "0", "100", true}, + {"1239999", "0", "0", false}, + {"1239asdfasf99", "100", "200", false}, + {"1239999asdff29", "10", "30", true}, + {"あいうえお", "0", "5", true}, + {"あいうえおか", "0", "5", false}, + {"あいうえお", "0", "0", false}, + {"あいうえ", "5", "10", false}, + } + for _, test := range tests { + actual := StringLength(test.value, test.min, test.max) + if actual != test.expected { + t.Errorf("Expected StringLength(%s, %s, %s) to be %v, got %v", test.value, test.min, test.max, test.expected, actual) + } + } +} + +func TestIsIn(t *testing.T) { + t.Parallel() + + var tests = []struct { + value string + params []string + expected bool + }{ + {"PRESENT", []string{"PRESENT"}, true}, + {"PRESENT", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, true}, + {"PRÉSENTE", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, true}, + {"PRESENT", []string{}, false}, + {"PRESENT", nil, false}, + {"ABSENT", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, false}, + {"", []string{"PRESENT", "PRÉSENTE", "NOTABSENT"}, false}, + } + for _, test := range tests { + actual := IsIn(test.value, test.params...) + if actual != test.expected { + t.Errorf("Expected IsIn(%s, %v) to be %v, got %v", test.value, test.params, test.expected, actual) + } + } +} + +type Address struct { + Street string `valid:"-"` + Zip string `json:"zip" valid:"numeric,required"` +} + +type User struct { + Name string `valid:"required"` + Email string `valid:"required,email"` + Password string `valid:"required"` + Age int `valid:"required,numeric,@#\u0000"` + Home *Address + Work []Address +} + +type UserValid struct { + Name string `valid:"required"` + Email string `valid:"required,email"` + Password string `valid:"required"` + Age int `valid:"required"` + Home *Address + Work []Address `valid:"required"` +} + +type PrivateStruct struct { + privateField string `valid:"required,alpha,d_k"` + NonZero int + ListInt []int + ListString []string `valid:"alpha"` + Work [2]Address + Home Address + Map map[string]Address +} + +type NegationStruct struct { + NotInt string `valid:"!int"` + Int string `valid:"int"` +} + +type LengthStruct struct { + Length string `valid:"length(10|20)"` +} + +type StringLengthStruct struct { + Length string `valid:"stringlength(10|20)"` +} + +type StringMatchesStruct struct { + StringMatches string `valid:"matches(^[0-9]{3}$)"` +} + +// TODO: this testcase should be fixed +// type StringMatchesComplexStruct struct { +// StringMatches string `valid:"matches(^\\$\\([\"']\\w+[\"']\\)$)"` +// } + +type IsInStruct struct { + IsIn string `valid:"in(PRESENT|PRÉSENTE|NOTABSENT)"` +} + +type Post struct { + Title string `valid:"alpha,required"` + Message string `valid:"ascii"` + AuthorIP string `valid:"ipv4"` +} + +type MissingValidationDeclarationStruct struct { + Name string `` + Email string `valid:"required,email"` +} + +type FieldsRequiredByDefaultButExemptStruct struct { + Name string `valid:"-"` + Email string `valid:"email"` +} + +type FieldsRequiredByDefaultButExemptOrOptionalStruct struct { + Name string `valid:"-"` + Email string `valid:"optional,email"` +} + +type MessageWithSeveralFieldsStruct struct { + Title string `valid:"length(1|10)"` + Body string `valid:"length(1|10)"` +} + +func TestValidateMissingValidationDeclarationStruct(t *testing.T) { + var tests = []struct { + param MissingValidationDeclarationStruct + expected bool + }{ + {MissingValidationDeclarationStruct{}, false}, + {MissingValidationDeclarationStruct{Name: "TEST", Email: "test@example.com"}, false}, + } + SetFieldsRequiredByDefault(true) + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } + SetFieldsRequiredByDefault(false) +} + +func TestFieldsRequiredByDefaultButExemptStruct(t *testing.T) { + var tests = []struct { + param FieldsRequiredByDefaultButExemptStruct + expected bool + }{ + {FieldsRequiredByDefaultButExemptStruct{}, false}, + {FieldsRequiredByDefaultButExemptStruct{Name: "TEST"}, false}, + {FieldsRequiredByDefaultButExemptStruct{Email: ""}, false}, + {FieldsRequiredByDefaultButExemptStruct{Email: "test@example.com"}, true}, + } + SetFieldsRequiredByDefault(true) + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } + SetFieldsRequiredByDefault(false) +} + +func TestFieldsRequiredByDefaultButExemptOrOptionalStruct(t *testing.T) { + var tests = []struct { + param FieldsRequiredByDefaultButExemptOrOptionalStruct + expected bool + }{ + {FieldsRequiredByDefaultButExemptOrOptionalStruct{}, true}, + {FieldsRequiredByDefaultButExemptOrOptionalStruct{Name: "TEST"}, true}, + {FieldsRequiredByDefaultButExemptOrOptionalStruct{Email: ""}, true}, + {FieldsRequiredByDefaultButExemptOrOptionalStruct{Email: "test@example.com"}, true}, + {FieldsRequiredByDefaultButExemptOrOptionalStruct{Email: "test@example"}, false}, + } + SetFieldsRequiredByDefault(true) + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } + SetFieldsRequiredByDefault(false) +} + +func TestInvalidValidator(t *testing.T) { + type InvalidStruct struct { + Field int `valid:"someInvalidValidator"` + } + + invalidStruct := InvalidStruct{1} + if valid, err := ValidateStruct(&invalidStruct); valid || err == nil || + err.Error() != `Field: The following validator is invalid or can't be applied to the field: "someInvalidValidator"` { + t.Errorf("Got an unexpected result for struct with invalid validator: %t %s", valid, err) + } +} + +func TestCustomValidator(t *testing.T) { + type ValidStruct struct { + Field int `valid:"customTrueValidator"` + } + + type InvalidStruct struct { + Field int `valid:"customFalseValidator~Custom validator error"` + } + + type StructWithCustomAndBuiltinValidator struct { + Field int `valid:"customTrueValidator,required"` + } + + if valid, err := ValidateStruct(&ValidStruct{Field: 1}); !valid || err != nil { + t.Errorf("Got an unexpected result for struct with custom always true validator: %t %s", valid, err) + } + + if valid, err := ValidateStruct(&InvalidStruct{Field: 1}); valid || err == nil || err.Error() != "Custom validator error" { + t.Errorf("Got an unexpected result for struct with custom always false validator: %t %s", valid, err) + } + + mixedStruct := StructWithCustomAndBuiltinValidator{} + if valid, err := ValidateStruct(&mixedStruct); valid || err == nil || err.Error() != "Field: non zero value required" { + t.Errorf("Got an unexpected result for invalid struct with custom and built-in validators: %t %s", valid, err) + } + + mixedStruct.Field = 1 + if valid, err := ValidateStruct(&mixedStruct); !valid || err != nil { + t.Errorf("Got an unexpected result for valid struct with custom and built-in validators: %t %s", valid, err) + } +} + +type CustomByteArray [6]byte + +type StructWithCustomByteArray struct { + ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` + Email string `valid:"email"` + CustomMinLength int `valid:"-"` +} + +func TestStructWithCustomByteArray(t *testing.T) { + t.Parallel() + + // add our custom byte array validator that fails when the byte array is pristine (all zeroes) + CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + switch v := o.(type) { + case StructWithCustomByteArray: + if len(v.Email) > 0 { + if v.Email != "test@example.com" { + t.Errorf("v.Email should have been 'test@example.com' but was '%s'", v.Email) + } + } + default: + t.Errorf("Context object passed to custom validator should have been a StructWithCustomByteArray but was %T (%+v)", o, o) + } + + switch v := i.(type) { + case CustomByteArray: + for _, e := range v { // check if v is empty, i.e. all zeroes + if e != 0 { + return true + } + } + } + return false + })) + CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + switch v := o.(type) { + case StructWithCustomByteArray: + return len(v.ID) >= v.CustomMinLength + } + return false + })) + testCustomByteArray := CustomByteArray{'1', '2', '3', '4', '5', '6'} + var tests = []struct { + param StructWithCustomByteArray + expected bool + }{ + {StructWithCustomByteArray{}, false}, + {StructWithCustomByteArray{Email: "test@example.com"}, false}, + {StructWithCustomByteArray{ID: testCustomByteArray, Email: "test@example.com"}, true}, + {StructWithCustomByteArray{ID: testCustomByteArray, Email: "test@example.com", CustomMinLength: 7}, false}, + } + SetFieldsRequiredByDefault(true) + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } + SetFieldsRequiredByDefault(false) +} + +func TestValidateNegationStruct(t *testing.T) { + var tests = []struct { + param NegationStruct + expected bool + }{ + {NegationStruct{"a1", "11"}, true}, + {NegationStruct{"email@email.email", "11"}, true}, + {NegationStruct{"123456----", "11"}, true}, + {NegationStruct{"::1", "11"}, true}, + {NegationStruct{"123.123", "11"}, true}, + {NegationStruct{"a1", "a1"}, false}, + {NegationStruct{"11", "a1"}, false}, + {NegationStruct{"11", "11"}, false}, + } + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestLengthStruct(t *testing.T) { + var tests = []struct { + param interface{} + expected bool + }{ + {LengthStruct{"11111"}, false}, + {LengthStruct{"11111111111111111110000000000000000"}, false}, + {LengthStruct{"11dfffdf0099"}, true}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestStringLengthStruct(t *testing.T) { + var tests = []struct { + param interface{} + expected bool + }{ + {StringLengthStruct{"11111"}, false}, + {StringLengthStruct{"11111111111111111110000000000000000"}, false}, + {StringLengthStruct{"11dfffdf0099"}, true}, + {StringLengthStruct{"あいうえお"}, false}, + {StringLengthStruct{"あいうえおかきくけこ"}, true}, + {StringLengthStruct{"あいうえおかきくけこさしすせそたちつてと"}, true}, + {StringLengthStruct{"あいうえおかきくけこさしすせそたちつてとな"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestStringMatchesStruct(t *testing.T) { + var tests = []struct { + param interface{} + expected bool + }{ + {StringMatchesStruct{"123"}, true}, + {StringMatchesStruct{"123456"}, false}, + {StringMatchesStruct{"123abcd"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestIsInStruct(t *testing.T) { + var tests = []struct { + param interface{} + expected bool + }{ + {IsInStruct{"PRESENT"}, true}, + {IsInStruct{""}, true}, + {IsInStruct{" "}, false}, + {IsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestRequiredIsInStruct(t *testing.T) { + type RequiredIsInStruct struct { + IsIn string `valid:"in(PRESENT|PRÉSENTE|NOTABSENT),required"` + } + + var tests = []struct { + param interface{} + expected bool + }{ + {RequiredIsInStruct{"PRESENT"}, true}, + {RequiredIsInStruct{""}, false}, + {RequiredIsInStruct{" "}, false}, + {RequiredIsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestEmptyRequiredIsInStruct(t *testing.T) { + type EmptyRequiredIsInStruct struct { + IsIn string `valid:"in(),required"` + } + + var tests = []struct { + param interface{} + expected bool + }{ + {EmptyRequiredIsInStruct{"PRESENT"}, false}, + {EmptyRequiredIsInStruct{""}, false}, + {EmptyRequiredIsInStruct{" "}, false}, + {EmptyRequiredIsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestFunkyIsInStruct(t *testing.T) { + type FunkyIsInStruct struct { + IsIn string `valid:"in(PRESENT|| |PRÉSENTE|NOTABSENT)"` + } + + var tests = []struct { + param interface{} + expected bool + }{ + {FunkyIsInStruct{"PRESENT"}, true}, + {FunkyIsInStruct{""}, true}, + {FunkyIsInStruct{" "}, true}, + {FunkyIsInStruct{"ABSENT"}, false}, + } + + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +// TODO: test case broken +// func TestStringMatchesComplexStruct(t *testing.T) { +// var tests = []struct { +// param interface{} +// expected bool +// }{ +// {StringMatchesComplexStruct{"$()"}, false}, +// {StringMatchesComplexStruct{"$('AZERTY')"}, true}, +// {StringMatchesComplexStruct{`$("AZERTY")`}, true}, +// {StringMatchesComplexStruct{`$("")`}, false}, +// {StringMatchesComplexStruct{"AZERTY"}, false}, +// {StringMatchesComplexStruct{"$AZERTY"}, false}, +// } + +// for _, test := range tests { +// actual, err := ValidateStruct(test.param) +// if actual != test.expected { +// t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) +// if err != nil { +// t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) +// } +// } +// } +// } + +func TestValidateStruct(t *testing.T) { + + var tests = []struct { + param interface{} + expected bool + }{ + {User{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{{"Street", "123456"}, {"Street", "123456"}}}, false}, + {User{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, + {User{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, + {UserValid{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{{"Street", "123456"}, {"Street", "123456"}}}, true}, + {UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{}}, false}, + {UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, + {UserValid{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{{"Street", "ABC456D89"}, {"Street", "123456"}}}, false}, + {nil, true}, + {User{"John", "john@yahoo.com", "123G#678", 0, &Address{"Street", "123456"}, []Address{}}, false}, + {"im not a struct", false}, + } + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } + + TagMap["d_k"] = Validator(func(str string) bool { + return str == "d_k" + }) + result, err := ValidateStruct(PrivateStruct{"d_k", 0, []int{1, 2}, []string{"hi", "super"}, [2]Address{{"Street", "123456"}, + {"Street", "123456"}}, Address{"Street", "123456"}, map[string]Address{"address": {"Street", "123456"}}}) + if result != true { + t.Log("Case ", 6, ": expected ", true, " when result is ", result) + t.Error(err) + t.FailNow() + } +} + +type testByteArray [8]byte +type testByteMap map[byte]byte +type testByteSlice []byte +type testStringStringMap map[string]string +type testStringIntMap map[string]int + +func TestRequired(t *testing.T) { + + testString := "foobar" + var tests = []struct { + param interface{} + expected bool + }{ + { + struct { + Pointer *string `valid:"required"` + }{}, + false, + }, + { + struct { + Pointer *string `valid:"required"` + }{ + Pointer: &testString, + }, + true, + }, + { + struct { + Addr Address `valid:"required"` + }{}, + false, + }, + { + struct { + Addr Address `valid:"required"` + }{ + Addr: Address{"", "123"}, + }, + true, + }, + { + struct { + Pointer *Address `valid:"required"` + }{}, + false, + }, + { + struct { + Pointer *Address `valid:"required"` + }{ + Pointer: &Address{"", "123"}, + }, + true, + }, + { + struct { + TestByteArray testByteArray `valid:"required"` + }{}, + false, + }, + { + struct { + TestByteArray testByteArray `valid:"required"` + }{ + testByteArray{}, + }, + false, + }, + { + struct { + TestByteArray testByteArray `valid:"required"` + }{ + testByteArray{'1', '2', '3', '4', '5', '6', '7', 'A'}, + }, + true, + }, + { + struct { + TestByteMap testByteMap `valid:"required"` + }{}, + false, + }, + { + struct { + TestByteSlice testByteSlice `valid:"required"` + }{}, + false, + }, + { + struct { + TestStringStringMap testStringStringMap `valid:"required"` + }{ + testStringStringMap{"test": "test"}, + }, + true, + }, + { + struct { + TestIntMap testStringIntMap `valid:"required"` + }{ + testStringIntMap{"test": 42}, + }, + true, + }, + } + for _, test := range tests { + actual, err := ValidateStruct(test.param) + if actual != test.expected { + t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual) + if err != nil { + t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err) + } + } + } +} + +func TestErrorByField(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {"message", ""}, + {"Message", ""}, + {"title", ""}, + {"Title", "My123 does not validate as alpha"}, + {"AuthorIP", "123 does not validate as ipv4"}, + } + post := &Post{"My123", "duck13126", "123"} + _, err := ValidateStruct(post) + + for _, test := range tests { + actual := ErrorByField(err, test.param) + if actual != test.expected { + t.Errorf("Expected ErrorByField(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestErrorsByField(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected string + }{ + {"Title", "My123 does not validate as alpha"}, + {"AuthorIP", "123 does not validate as ipv4"}, + } + post := &Post{Title: "My123", Message: "duck13126", AuthorIP: "123"} + _, err := ValidateStruct(post) + errs := ErrorsByField(err) + if len(errs) != 2 { + t.Errorf("There should only be 2 errors but got %v", len(errs)) + } + + for _, test := range tests { + if actual, ok := errs[test.param]; !ok || actual != test.expected { + t.Errorf("Expected ErrorsByField(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + tests = []struct { + param string + expected string + }{ + {"Title", ";:;message;:; does not validate as length(1|10)"}, + {"Body", ";:;message;:; does not validate as length(1|10)"}, + } + + message := &MessageWithSeveralFieldsStruct{Title: ";:;message;:;", Body: ";:;message;:;"} + _, err = ValidateStruct(message) + errs = ErrorsByField(err) + if len(errs) != 2 { + t.Errorf("There should only be 2 errors but got %v", len(errs)) + } + + for _, test := range tests { + if actual, ok := errs[test.param]; !ok || actual != test.expected { + t.Errorf("Expected ErrorsByField(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + tests = []struct { + param string + expected string + }{ + {"CustomField", "An error occurred"}, + } + + err = Error{"CustomField", fmt.Errorf("An error occurred"), false, "hello"} + errs = ErrorsByField(err) + + if len(errs) != 1 { + t.Errorf("There should only be 1 errors but got %v", len(errs)) + } + + for _, test := range tests { + if actual, ok := errs[test.param]; !ok || actual != test.expected { + t.Errorf("Expected ErrorsByField(%q) to be %v, got %v", test.param, test.expected, actual) + } + } + + type StructWithCustomValidation struct { + Email string `valid:"email"` + ID string `valid:"falseValidation"` + } + + CustomTypeTagMap.Set("falseValidation", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return false + })) + + tests = []struct { + param string + expected string + }{ + {"Email", "My123 does not validate as email"}, + {"ID", "duck13126 does not validate as falseValidation"}, + } + s := &StructWithCustomValidation{Email: "My123", ID: "duck13126"} + _, err = ValidateStruct(s) + errs = ErrorsByField(err) + if len(errs) != 2 { + t.Errorf("There should only be 2 errors but got %v", len(errs)) + } + + for _, test := range tests { + if actual, ok := errs[test.param]; !ok || actual != test.expected { + t.Errorf("Expected ErrorsByField(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestValidateStructPointers(t *testing.T) { + // Struct which uses pointers for values + type UserWithPointers struct { + Name *string `valid:"-"` + Email *string `valid:"email"` + FavoriteFood *string `valid:"length(0|32)"` + Nerd *bool `valid:"-"` + } + + var tests = []struct { + param string + expected string + }{ + {"Name", ""}, + {"Email", "invalid does not validate as email"}, + {"FavoriteFood", ""}, + {"Nerd", ""}, + } + + name := "Herman" + email := "invalid" + food := "Pizza" + nerd := true + user := &UserWithPointers{&name, &email, &food, &nerd} + _, err := ValidateStruct(user) + + for _, test := range tests { + actual := ErrorByField(err, test.param) + if actual != test.expected { + t.Errorf("Expected ErrorByField(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func ExampleValidateStruct() { + type Post struct { + Title string `valid:"alphanum,required"` + Message string `valid:"duck,ascii"` + AuthorIP string `valid:"ipv4"` + } + post := &Post{"My Example Post", "duck", "123.234.54.3"} + + //Add your own struct validation tags + TagMap["duck"] = Validator(func(str string) bool { + return str == "duck" + }) + + result, err := ValidateStruct(post) + if err != nil { + println("error: " + err.Error()) + } + println(result) +} + +func TestIsCIDR(t *testing.T) { + t.Parallel() + + var tests = []struct { + param string + expected bool + }{ + {"193.168.3.20/7", true}, + {"2001:db8::/32", true}, + {"2001:0db8:85a3:0000:0000:8a2e:0370:7334/64", true}, + {"193.138.3.20/60", false}, + {"500.323.2.23/43", false}, + {"", false}, + } + for _, test := range tests { + actual := IsCIDR(test.param) + if actual != test.expected { + t.Errorf("Expected IsCIDR(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} + +func TestOptionalCustomValidators(t *testing.T) { + + CustomTypeTagMap.Set("f2", CustomTypeValidator(func(i interface{}, o interface{}) bool { + return false + })) + + var val struct { + WithCustomError string `valid:"f2~boom,optional"` + WithoutCustomError string `valid:"f2,optional"` + OptionalFirst string `valid:"optional,f2"` + } + + ok, err := ValidateStruct(val) + + if err != nil { + t.Errorf("Expected nil err with optional validation, got %v", err) + } + + if !ok { + t.Error("Expected validation to return true, got false") + } +} + +func TestJSONValidator(t *testing.T) { + + var val struct { + WithJSONName string `json:"with_json_name" valid:"-,required"` + WithoutJSONName string `valid:"-,required"` + WithJSONOmit string `json:"with_other_json_name,omitempty" valid:"-,required"` + WithJSONOption string `json:",omitempty" valid:"-,required"` + WithEmptyJSONName string `json:"-" valid:"-,required"` + } + + _, err := ValidateStruct(val) + + if err == nil { + t.Error("Expected error but got no error") + } + + if Contains(err.Error(), "WithJSONName") { + t.Errorf("Expected error message to contain with_json_name but actual error is: %s", err.Error()) + } + + if Contains(err.Error(), "WithoutJSONName") == false { + t.Errorf("Expected error message to contain WithoutJSONName but actual error is: %s", err.Error()) + } + + if Contains(err.Error(), "omitempty") { + t.Errorf("Expected error message to not contain ',omitempty' but actual error is: %s", err.Error()) + } + + if !Contains(err.Error(), "WithEmptyJSONName") { + t.Errorf("Expected error message to contain WithEmptyJSONName but actual error is: %s", err.Error()) + } +} + +func TestValidatorIncludedInError(t *testing.T) { + post := Post{ + Title: "", + Message: "👍", + AuthorIP: "xyz", + } + + validatorMap := map[string]string{ + "Title": "required", + "Message": "ascii", + "AuthorIP": "ipv4", + } + + ok, errors := ValidateStruct(post) + if ok { + t.Errorf("expected validation to fail %v", ok) + } + + for _, e := range errors.(Errors) { + casted := e.(Error) + if validatorMap[casted.Name] != casted.Validator { + t.Errorf("expected validator for %s to be %s, but was %s", casted.Name, validatorMap[casted.Name], casted.Validator) + } + } + + // check to make sure that validators with arguments (like length(1|10)) don't include the arguments + // in the validator name + message := MessageWithSeveralFieldsStruct{ + Title: "", + Body: "asdfasdfasdfasdfasdf", + } + + validatorMap = map[string]string{ + "Title": "length", + "Body": "length", + } + + ok, errors = ValidateStruct(message) + if ok { + t.Errorf("expected validation to fail, %v", ok) + } + + for _, e := range errors.(Errors) { + casted := e.(Error) + if validatorMap[casted.Name] != casted.Validator { + t.Errorf("expected validator for %s to be %s, but was %s", casted.Name, validatorMap[casted.Name], casted.Validator) + } + } + + // make sure validators with custom messages don't show up in the validator string + type CustomMessage struct { + Text string `valid:"length(1|10)~Custom message"` + } + cs := CustomMessage{Text: "asdfasdfasdfasdf"} + + ok, errors = ValidateStruct(&cs) + if ok { + t.Errorf("expected validation to fail, %v", ok) + } + + validator := errors.(Errors)[0].(Error).Validator + if validator != "length" { + t.Errorf("expected validator for Text to be length, but was %s", validator) + } + +} + +func TestIsRsaPublicKey(t *testing.T) { + var tests = []struct { + rsastr string + keylen int + expected bool + }{ + {`fubar`, 2048, false}, + {`MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvncDCeibmEkabJLmFec7x9y86RP6dIvkVxxbQoOJo06E+p7tH6vCmiGHKnuu +XwKYLq0DKUE3t/HHsNdowfD9+NH8caLzmXqGBx45/Dzxnwqz0qYq7idK+Qff34qrk/YFoU7498U1Ee7PkKb7/VE9BmMEcI3uoKbeXCbJRI +HoTp8bUXOpNTSUfwUNwJzbm2nsHo2xu6virKtAZLTsJFzTUmRd11MrWCvj59lWzt1/eIMN+ekjH8aXeLOOl54CL+kWp48C+V9BchyKCShZ +B7ucimFvjHTtuxziXZQRO7HlcsBOa0WwvDJnRnskdyoD31s4F4jpKEYBJNWTo63v6lUvbQIDAQAB`, 2048, true}, + {`MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvncDCeibmEkabJLmFec7x9y86RP6dIvkVxxbQoOJo06E+p7tH6vCmiGHKnuu +XwKYLq0DKUE3t/HHsNdowfD9+NH8caLzmXqGBx45/Dzxnwqz0qYq7idK+Qff34qrk/YFoU7498U1Ee7PkKb7/VE9BmMEcI3uoKbeXCbJRI +HoTp8bUXOpNTSUfwUNwJzbm2nsHo2xu6virKtAZLTsJFzTUmRd11MrWCvj59lWzt1/eIMN+ekjH8aXeLOOl54CL+kWp48C+V9BchyKCShZ +B7ucimFvjHTtuxziXZQRO7HlcsBOa0WwvDJnRnskdyoD31s4F4jpKEYBJNWTo63v6lUvbQIDAQAB`, 1024, false}, + {`-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvncDCeibmEkabJLmFec7 +x9y86RP6dIvkVxxbQoOJo06E+p7tH6vCmiGHKnuuXwKYLq0DKUE3t/HHsNdowfD9 ++NH8caLzmXqGBx45/Dzxnwqz0qYq7idK+Qff34qrk/YFoU7498U1Ee7PkKb7/VE9 +BmMEcI3uoKbeXCbJRIHoTp8bUXOpNTSUfwUNwJzbm2nsHo2xu6virKtAZLTsJFzT +UmRd11MrWCvj59lWzt1/eIMN+ekjH8aXeLOOl54CL+kWp48C+V9BchyKCShZB7uc +imFvjHTtuxziXZQRO7HlcsBOa0WwvDJnRnskdyoD31s4F4jpKEYBJNWTo63v6lUv +bQIDAQAB +-----END PUBLIC KEY-----`, 2048, true}, + {`-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvncDCeibmEkabJLmFec7 +x9y86RP6dIvkVxxbQoOJo06E+p7tH6vCmiGHKnuuXwKYLq0DKUE3t/HHsNdowfD9 ++NH8caLzmXqGBx45/Dzxnwqz0qYq7idK+Qff34qrk/YFoU7498U1Ee7PkKb7/VE9 +BmMEcI3uoKbeXCbJRIHoTp8bUXOpNTSUfwUNwJzbm2nsHo2xu6virKtAZLTsJFzT +UmRd11MrWCvj59lWzt1/eIMN+ekjH8aXeLOOl54CL+kWp48C+V9BchyKCShZB7uc +imFvjHTtuxziXZQRO7HlcsBOa0WwvDJnRnskdyoD31s4F4jpKEYBJNWTo63v6lUv +bQIDAQAB +-----END PUBLIC KEY-----`, 4096, false}, + } + for i, test := range tests { + actual := IsRsaPublicKey(test.rsastr, test.keylen) + if actual != test.expected { + t.Errorf("Expected TestIsRsaPublicKey(%d, %d) to be %v, got %v", i, test.keylen, test.expected, actual) + } + } +} diff --git a/vendor/github.com/asaskevich/govalidator/wercker.yml b/vendor/github.com/asaskevich/govalidator/wercker.yml new file mode 100644 index 000000000..cac7a5fcf --- /dev/null +++ b/vendor/github.com/asaskevich/govalidator/wercker.yml @@ -0,0 +1,15 @@ +box: golang +build: + steps: + - setup-go-workspace + + - script: + name: go get + code: | + go version + go get -t ./... + + - script: + name: go test + code: | + go test -race ./...