diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 41af5cc..988a9d3 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -5,6 +5,11 @@ "./..." ], "Deps": [ + { + "ImportPath": "github.com/asaskevich/govalidator", + "Comment": "v2-37-gedd46cd", + "Rev": "edd46cdac249b001c7b7d88c6d43993ea875e8d8" + }, { "ImportPath": "github.com/jtolds/gls", "Rev": "9a4a02dbe491bef4bab3c24fd9f3087d6c4c6690" @@ -18,10 +23,6 @@ "ImportPath": "github.com/smartystreets/goconvey/convey", "Comment": "1.5.0-398-gab7641a", "Rev": "ab7641a0db76b0e88765294b1377b026ec01f26b" - }, - { - "ImportPath": "gopkg.in/validator.v2", - "Rev": "8e445b9dc14ab1a1a78cc1f712df991307173ee6" } ] } diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/.travis.yml b/Godeps/_workspace/src/github.com/asaskevich/govalidator/.travis.yml new file mode 100644 index 0000000..119d829 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/.travis.yml @@ -0,0 +1,10 @@ +language: go + +go: + - 1.1 + - 1.2 + - 1.3 + +notifications: + email: + - bwatas@gmail.com diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/LICENSE b/Godeps/_workspace/src/github.com/asaskevich/govalidator/LICENSE new file mode 100644 index 0000000..2f9a31f --- /dev/null +++ b/Godeps/_workspace/src/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/Godeps/_workspace/src/github.com/asaskevich/govalidator/README.md b/Godeps/_workspace/src/github.com/asaskevich/govalidator/README.md new file mode 100644 index 0000000..eed636b --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/README.md @@ -0,0 +1,281 @@ +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) [![views](https://sourcegraph.com/api/repos/github.com/asaskevich/govalidator/.counters/views.png)](https://sourcegraph.com/github.com/asaskevich/govalidator) +[![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) + +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 + +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 unhappy to use long `govalidator`, you can do something like this: +```go +import ( + valid "github.com/asaskevich/govalidator" +) +``` + +#### List of functions: +```go +func Abs(value float64) float64 +func BlackList(str, chars string) string +func ByteLength(str string, params ...string) bool +func StringLength(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 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 IsCreditCard(str string) bool +func IsDataURI(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 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 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 IsPositive(value float64) bool +func IsPrintableASCII(str string) bool +func IsRGBcolor(str string) bool +func IsRequestURI(rawurl string) bool +func IsRequestURL(rawurl string) bool +func IsSSN(str 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 RemoveTags(s string) string +func ReplacePattern(str, pattern, replace string) string +func Reverse(s string) string +func RightTrim(str, chars string) string +func SafeFileName(str string) string +func Sign(value float64) float64 +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 Error +func (e Error) Error() string +type Errors +func (es Errors) Error() string +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" +}) +``` +Here is a list of available validators for struct fields (validator - used function): +```go +"alpha": IsAlpha, +"alphanum": IsAlphanumeric, +"ascii": IsASCII, +"base64": IsBase64, +"creditcard": IsCreditCard, +"datauri": IsDataURI, +"email": IsEmail, +"float": IsFloat, +"fullwidth": IsFullWidth, +"halfwidth": IsHalfWidth, +"hexadecimal": IsHexadecimal, +"hexcolor": IsHexcolor, +"int": IsInt, +"ip": IsIP, +"ipv4": IsIPv4, +"ipv6": IsIPv6, +"isbn10": IsISBN10, +"isbn13": IsISBN13, +"json": IsJSON, +"latitude": IsLatitude, +"longitude": IsLongitude, +"lowercase": IsLowerCase, +"mac": IsMAC, +"multibyte": IsMultibyte, +"null": IsNull, +"numeric": IsNumeric, +"printableascii": IsPrintableASCII, +"requri": IsRequestURI, +"requrl": IsRequestURL, +"rgbcolor": IsRGBcolor, +"ssn": IsSSN +"uppercase": IsUpperCase, +"url": IsURL, +"utfdigit": IsUTFDigit, +"utfletter": IsUTFLetter, +"utfletternum": IsUTFLetterNumeric, +"utfnumeric": IsUTFNumeric, +"uuid": IsUUID, +"uuidv3": IsUUIDv3, +"uuidv4": IsUUIDv4, +"uuidv5": IsUUIDv5, +"variablewidth": IsVariableWidth, +``` +Validators with parameters + +```go +"length(min|max)": ByteLength, +``` + +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") +``` + +#### 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 for the package feel free to put up a Pull Request or open Issue. + +#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors) +* [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) + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/asaskevich/govalidator/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/arrays.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/arrays.go new file mode 100644 index 0000000..55b8aac --- /dev/null +++ b/Godeps/_workspace/src/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 []interface{} = 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 []interface{} = 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/Godeps/_workspace/src/github.com/asaskevich/govalidator/arrays_test.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/arrays_test.go new file mode 100644 index 0000000..8f61fc1 --- /dev/null +++ b/Godeps/_workspace/src/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/Godeps/_workspace/src/github.com/asaskevich/govalidator/converter.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/converter.go new file mode 100644 index 0000000..737a1f1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/converter.go @@ -0,0 +1,49 @@ +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) { + res, err := strconv.ParseBool(str) + if err != nil { + res = false + } + return res, err +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/converter_test.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/converter_test.go new file mode 100644 index 0000000..bb70279 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/converter_test.go @@ -0,0 +1,57 @@ +package govalidator + +import "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() + } + } +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/error.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/error.go new file mode 100644 index 0000000..bdd5dc7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/error.go @@ -0,0 +1,24 @@ +package govalidator + +type Errors []error + +func (es Errors) Errors() []error { + return es +} + +func (es Errors) Error() string { + var err string + for _, e := range es { + err += e.Error() + ";" + } + return err +} + +type Error struct { + Name string + Err error +} + +func (e Error) Error() string { + return e.Name + ": " + e.Err.Error() +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics.go new file mode 100644 index 0000000..737cd47 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics.go @@ -0,0 +1,57 @@ +package govalidator + +import "math" + +// Abs returns absolute value of number +func Abs(value float64) float64 { + return value * Sign(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 InRange(value, left, right float64) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// IsWhole returns true if value is whole number +func IsWhole(value float64) bool { + return Abs(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/Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics_test.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics_test.go new file mode 100644 index 0000000..1de43b4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics_test.go @@ -0,0 +1,204 @@ +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(%q) 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(%q) 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(%q) 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(%q) 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(%q) 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(%q) 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(%q) 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(%q) to be %v, got %v", test.param, test.expected, actual) + } + } +} +func TestInRange(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 := InRange(test.param, test.left, test.right) + if actual != test.expected { + t.Errorf("Expected InRange(%q, %q, %q) to be %v, got %v", test.param, test.left, test.right, test.expected, actual) + } + } +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/patterns.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/patterns.go new file mode 100644 index 0000000..cbd8045 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/patterns.go @@ -0,0 +1,79 @@ +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+)?)$" + URL string = `^((ftp|https?):\/\/)?(\S+(:\S*)?@)?((([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]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$` + SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` + WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` + UnixPath string = `^((?:\/[a-zA-Z0-9\.\:]+(?:_[a-zA-Z0-9\:\.]+)*(?:\-[\:a-zA-Z0-9\.]+)*)+\/?)$` + 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) + rxURL = regexp.MustCompile(URL) + rxSSN = regexp.MustCompile(SSN) + rxWinPath = regexp.MustCompile(WinPath) + rxUnixPath = regexp.MustCompile(UnixPath) +) diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/types.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/types.go new file mode 100644 index 0000000..1d9d932 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/types.go @@ -0,0 +1,349 @@ +package govalidator + +import ( + "reflect" + "regexp" +) + +// 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. +type CustomTypeValidator func(i interface{}) bool + +// ParamValidator is a wrapper for validator functions that accepts additional parameters. +type ParamValidator func(str string, params ...string) bool +type tagOptions []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, + "stringlength": StringLength, +} + +var ParamTagRegexMap = map[string]*regexp.Regexp{ + "length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"), + "stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"), +} + +// 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 = 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, + "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, + "ipv4": IsIPv4, + "ipv6": IsIPv6, + "mac": IsMAC, + "latitude": IsLatitude, + "longitude": IsLongitude, + "ssn": IsSSN, +} + +// 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"}, +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/utils.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/utils.go new file mode 100644 index 0000000..0eff62f --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/utils.go @@ -0,0 +1,214 @@ +package govalidator + +import ( + "errors" + "fmt" + "html" + "path" + "regexp" + "strings" + "unicode" +) + +// 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 { + pattern := "" + if chars == "" { + pattern = "^\\s+" + } else { + pattern = "^[" + chars + "]+" + } + r, _ := regexp.Compile(pattern) + return string(r.ReplaceAll([]byte(str), []byte(""))) +} + +// 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 { + pattern := "" + if chars == "" { + pattern = "\\s+$" + } else { + pattern = "[" + chars + "]+$" + } + r, _ := regexp.Compile(pattern) + return string(r.ReplaceAll([]byte(str), []byte(""))) +} + +// 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 string(r.ReplaceAll([]byte(str), []byte(""))) +} + +// BlackList remove characters that appear in the blacklist. +func BlackList(str, chars string) string { + pattern := "[" + chars + "]+" + r, _ := regexp.Compile(pattern) + return string(r.ReplaceAll([]byte(str), []byte(""))) +} + +// 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 string(r.ReplaceAll([]byte(str), []byte(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) { + 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 +} + +// Will truncate a string 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 + } else { + return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending + } + } + } + } + + return str +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/utils_test.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/utils_test.go new file mode 100644 index 0000000..be3e33c --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/utils_test.go @@ -0,0 +1,426 @@ +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"}, + } + 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`}, + } + 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...`}, + } + 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) + } + } +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/validator.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/validator.go new file mode 100644 index 0000000..5234fb9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/validator.go @@ -0,0 +1,854 @@ +// Package govalidator is package of validators and sanitizers for strings, structs and collections. +package govalidator + +import ( + "encoding/json" + "fmt" + "net" + "net/url" + "reflect" + "regexp" + "sort" + "strings" + "unicode" + "unicode/utf8" +) + +var fieldsRequiredByDefault bool + +// 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 == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") { + return false + } + u, err := url.Parse(str) + 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 recieved 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 recieved 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 { + r, _ := regexp.Compile("[^0-9]+") + sanitized := r.ReplaceAll([]byte(str), []byte("")) + if !rxCreditCard.MatchString(string(sanitized)) { + return false + } + var sum int64 + var digit string + var tmpNum int64 + var shouldDouble bool + for i := len(sanitized) - 1; i >= 0; i-- { + digit = string(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 { + r, _ := regexp.Compile("[\\s-]+") + sanitized := r.ReplaceAll([]byte(str), []byte("")) + var checksum int32 + var i int32 + if version == 10 { + if !rxISBN10.MatchString(string(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(string(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 +} + +// IsIP checks if a string is either IP version 4 or 6. +func IsIP(str string) bool { + return net.ParseIP(str) != nil +} + +// IsIPv4 check if the string is an IP version 4. +func IsIPv4(str string) bool { + ip := net.ParseIP(str) + return ip != nil && ip.To4() != nil +} + +// IsIPv6 check if the string is an IP version 6. +func IsIPv6(str string) bool { + ip := net.ParseIP(str) + return ip != nil && ip.To4() == 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 +} + +// 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) +} + +// ValidateStruct use tags for fields +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 + } + resultField, err := typeCheck(valueField, typeField) + if err != nil { + errs = append(errs, err) + } + result = result && resultField + } + if len(errs) > 0 { + err = errs + } + return result, err +} + +// parseTag splits a struct field's tag into its +// comma-separated options. +func parseTag(tag string) tagOptions { + split := strings.SplitN(tag, ",", -1) + return tagOptions(split) +} + +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) +} + +// 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 +} + +// 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 +} + +// Contains returns whether checks that a comma-separated list of options +// contains a particular substr flag. substr must be surrounded by a +// string boundary or commas. +func (opts tagOptions) contains(optionName string) bool { + for i := range opts { + tagOpt := opts[i] + if tagOpt == optionName { + return true + } + } + return false +} + +func checkRequired(v reflect.Value, t reflect.StructField, options tagOptions) (bool, error) { + if options.contains("required") { + err := fmt.Errorf("non zero value required") + return false, Error{t.Name, err} + } else if fieldsRequiredByDefault && !options.contains("optional") { + err := fmt.Errorf("All fields are required to at least have one validation defined") + return false, Error{t.Name, err} + } + // not required and empty is valid + return true, nil +} + +func typeCheck(v reflect.Value, t reflect.StructField) (bool, 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 + } + err := fmt.Errorf("All fields are required to at least have one validation defined") + return false, Error{t.Name, err} + case "-": + return true, nil + } + + options := parseTag(tag) + for i := range options { + tagOpt := options[i] + if ok := isValidTag(tagOpt); !ok { + continue + } + if validatefunc, ok := CustomTypeTagMap[tagOpt]; ok { + options = append(options[:i], options[i+1:]...) // we found our custom validator, so remove it from the options + if result := validatefunc(v.Interface()); !result { + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), tagOpt)} + } + return true, nil + } + } + + if isEmptyValue(v) { + // an empty value is not validated, check only required + return checkRequired(v, t, options) + } + + 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 i := range options { + tagOpt := options[i] + negate := false + // Check wether the tag looks like '!something' or 'something' + if len(tagOpt) > 0 && tagOpt[0] == '!' { + tagOpt = string(tagOpt[1:]) + negate = true + } + if ok := isValidTag(tagOpt); !ok { + err := fmt.Errorf("Unkown Validator %s", tagOpt) + return false, Error{t.Name, err} + } + + // Check for param validators + for key, value := range ParamTagRegexMap { + ps := value.FindStringSubmatch(tagOpt) + if len(ps) > 0 { + if validatefunc, ok := ParamTagMap[key]; ok { + 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 { + var err error + if !negate { + err = fmt.Errorf("%s does not validate as %s", field, tagOpt) + } else { + err = fmt.Errorf("%s does validate as %s", field, tagOpt) + } + return false, Error{t.Name, err} + } + default: + //Not Yet Supported Types (Fail here!) + err := fmt.Errorf("Validator %s doesn't support kind %s", tagOpt, v.Kind()) + return false, Error{t.Name, err} + } + } + } + } + + if validatefunc, ok := TagMap[tagOpt]; ok { + 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 { + var err error + if !negate { + err = fmt.Errorf("%s does not validate as %s", field, tagOpt) + } else { + err = fmt.Errorf("%s does validate as %s", field, tagOpt) + } + return false, Error{t.Name, err} + } + default: + //Not Yet Supported Types (Fail here!) + err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", tagOpt, v.Kind(), v) + return false, Error{t.Name, err} + } + } + } + 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 { + resultItem, err := ValidateStruct(v.MapIndex(k).Interface()) + if err != nil { + return false, err + } + result = result && resultItem + } + return result, nil + case reflect.Slice: + 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) + 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.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) + 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) + case reflect.Struct: + return ValidateStruct(v.Interface()) + default: + return false, &UnsupportedTypeError{v.Type()} + } +} + +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() { + m[item.(Error).Name] = item.(Error).Err.Error() + } + } + + 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/Godeps/_workspace/src/github.com/asaskevich/govalidator/validator_test.go b/Godeps/_workspace/src/github.com/asaskevich/govalidator/validator_test.go new file mode 100644 index 0000000..685e9fa --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/validator_test.go @@ -0,0 +1,2159 @@ +package govalidator + +import ( + "fmt" + "strings" + "testing" +) + +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", true}, + {"+00123", true}, + {"0", true}, + {"-0", true}, + {"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", true}, + } + 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 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.中文网", 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: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}, + {".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}, + {"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}, + // according to issues #62 #66 + {"https://pbs.twimg.com/profile_images/560826135676588032/j8fWrmYY_normal.jpeg", 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}, + } + 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}, + } + 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 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 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}, + } + 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 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}, + } + 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 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) + } + } +} + +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 +} + +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 Post struct { + Title string `valid:"alpha,required"` + Message string `valid:"ascii"` + AuthorIP string `valid:"ipv4"` +} + +type MissingValidationDeclationStruct 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 TestValidateMissingValidationDeclationStruct(t *testing.T) { + var tests = []struct { + param MissingValidationDeclationStruct + expected bool + }{ + {MissingValidationDeclationStruct{}, false}, + {MissingValidationDeclationStruct{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) +} + +type CustomByteArray [6]byte + +type StructWithCustomByteArray struct { + ID CustomByteArray `valid:"customByteArrayValidator"` + Email string `valid:"email"` +} + +func TestStructWithCustomByteArray(t *testing.T) { + // add our custom byte array validator that fails when the byte array is pristine (all zeroes) + CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}) bool { + 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 + }) + 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}, + } + 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 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 TestValidateStruct(t *testing.T) { + + var tests = []struct { + param interface{} + expected bool + }{ + {User{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, false}, + {User{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false}, + {User{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false}, + {UserValid{"John", "john@yahoo.com", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, true}, + {UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false}, + {UserValid{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"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{Address{"Street", "123456"}, + Address{"Street", "123456"}}, Address{"Street", "123456"}, map[string]Address{"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 + +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, + }, + } + 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 occured"}, + } + + err = Error{"CustomField", fmt.Errorf("An error occured")} + 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) + } + } +} + +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) +} diff --git a/Godeps/_workspace/src/github.com/asaskevich/govalidator/wercker.yml b/Godeps/_workspace/src/github.com/asaskevich/govalidator/wercker.yml new file mode 100644 index 0000000..4840449 --- /dev/null +++ b/Godeps/_workspace/src/github.com/asaskevich/govalidator/wercker.yml @@ -0,0 +1,15 @@ +box: wercker/golang +build: + steps: + - setup-go-workspace + + - script: + name: go get + code: | + go version + go get -t ./... + + - script: + name: go test + code: | + go test -race ./... diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/.gitignore b/Godeps/_workspace/src/gopkg.in/validator.v2/.gitignore deleted file mode 100644 index 8365624..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/LICENSE b/Godeps/_workspace/src/gopkg.in/validator.v2/LICENSE deleted file mode 100644 index ad410e1..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/README.md b/Godeps/_workspace/src/gopkg.in/validator.v2/README.md deleted file mode 100644 index 41fabf7..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/README.md +++ /dev/null @@ -1,164 +0,0 @@ -Package validator -================ - -Package validator implements variable validations - -Installation -============ - -Just use go get. - - go get gopkg.in/validator.v2 - -And then just import the package into your own code. - - import ( - "gopkg.in/validator.v2" - ) - -Usage -===== - -Please see http://godoc.org/gopkg.in/validator.v2 for detailed usage docs. -A simple example would be. - - type NewUserRequest struct { - Username string `validate:"min=3,max=40,regexp=^[a-zA-Z]$"` - Name string `validate:"nonzero"` - Age int `validate:"min=21"` - Password string `validate:"min=8"` - } - - nur := NewUserRequest{Username: "something", Age: 20} - if valid, errs := validator.Validate(nur); !valid { - // values not valid, deal with errors here - } - - -Builtin validators - -Here is the list of validators buildin in the package. - - len - For numeric numbers, max will simply make sure that the - value is equal to the parameter given. For strings, it - checks that the string length is exactly that number of - characters. For slices, arrays, and maps, validates the - number of items. (Usage: len=10) - - max - For numeric numbers, max will simply make sure that the - value is lesser or equal to the parameter given. For strings, - it checks that the string length is at most that number of - characters. For slices, arrays, and maps, validates the - number of items. (Usage: max=10) - - min - For numeric numbers, min will simply make sure that the value - is greater or equal to the parameter given. For strings, it - checks that the string length is at least that number of - characters. For slices, arrays, and maps, validates the - number of items. (Usage: min=10) - - nonzero - This validates that the value is not zero. The appropriate - zero value is given by the Go spec (e.g. for int it's 0, for - string it's "", for pointers is nil, etc.) Usage: nonzero - - regexp - Only valid for string types, it will validator that the - value matches the regular expression provided as parameter. - (Usage: regexp=^a.*b$) - -Custom validators - -It is possible to define custom validators by using SetValidationFunc. -First, one needs to create a validation function. - - // Very simple validator - func notZZ(v interface{}, param string) error { - st := reflect.ValueOf(v) - if st.Kind() != reflect.String { - return errors.New("notZZ only validates strings") - } - if st.String() == "ZZ" { - return errors.New("value cannot be ZZ") - } - return nil - } - -Then one needs to add it to the list of validators and give it a "tag" -name. - - validator.SetValidationFunc("notzz", notZZ) - -Then it is possible to use the notzz validation tag. This will print -"Field A error: value cannot be ZZ" - - type T struct { - A string `validate:"nonzero,notzz"` - } - t := T{"ZZ"} - if valid, errs := validator.Validate(t); !valid { - fmt.Printf("Field A error: %s\n", errs["A"][0]) - } - -You can also have multiple sets of validator rules with SetTag(). - - type T struct { - A int `foo:"nonzero" bar:"min=10"` - } - t := T{5} - SetTag("foo") - validator.Validate(t) // valid as it's nonzero - SetTag("bar") - validator.Validate(t) // invalid as it's less than 10 - -SetTag is probably better used with multiple validators. - - fooValidator := validator.NewValidator() - fooValidator.SetTag("foo") - barValidator := validator.NewValidator() - barValidator.SetTag("bar") - fooValidator.Validate(t) - barValidator.Validate(t) - -This keeps the default validator's tag clean. Again, please refer to -godocs for a lot of more examples and different uses. - -Pull requests policy -==================== - -tl;dr. Contributions are welcome. - -The repository is organized in version branches. Pull requests to, say, the -`v2` branch that break API compatibility will not be accepted. It is okay to -break the API in master, *not in the branches*. - -As for validation functions, the preference is to keep the main code simple -and add most new functions to the validator-contrib repository. - -https://github.com/go-validator/validator-contrib - -For improvements and/or fixes to the builtin validation functions, please -make sure the behaviour will not break existing functionality in the branches. -If you see a case where the functionality of the builtin will change -significantly, please send a pull request against `master`. We can discuss then -whether the changes should be incorporated in the version branches as well. - -License -======= - -Copyright 2014 Roberto Teixeira - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/builtins.go b/Godeps/_workspace/src/gopkg.in/validator.v2/builtins.go deleted file mode 100644 index 8d977bc..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/builtins.go +++ /dev/null @@ -1,244 +0,0 @@ -// Package validator implements value validations -// -// Copyright 2014 Roberto Teixeira -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validator - -import ( - "reflect" - "regexp" - "strconv" -) - -// nonzero tests whether a variable value non-zero -// as defined by the golang spec. -func nonzero(v interface{}, param string) error { - st := reflect.ValueOf(v) - valid := true - switch st.Kind() { - case reflect.String: - valid = len(st.String()) != 0 - case reflect.Ptr, reflect.Interface: - valid = !st.IsNil() - case reflect.Slice, reflect.Map, reflect.Array: - valid = st.Len() != 0 - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - valid = st.Int() != 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - valid = st.Uint() != 0 - case reflect.Float32, reflect.Float64: - valid = st.Float() != 0 - case reflect.Bool: - valid = st.Bool() - case reflect.Invalid: - valid = false // always invalid - default: - return ErrUnsupported - } - - if !valid { - return ErrZeroValue - } - return nil -} - -// length tests whether a variable's length is equal to a given -// value. For strings it tests the number of characters whereas -// for maps and slices it tests the number of items. -func length(v interface{}, param string) error { - st := reflect.ValueOf(v) - valid := true - switch st.Kind() { - case reflect.String: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - valid = int64(len(st.String())) == p - case reflect.Slice, reflect.Map, reflect.Array: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - valid = int64(st.Len()) == p - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - valid = st.Int() == p - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p, err := asUint(param) - if err != nil { - return ErrBadParameter - } - valid = st.Uint() == p - case reflect.Float32, reflect.Float64: - p, err := asFloat(param) - if err != nil { - return ErrBadParameter - } - valid = st.Float() == p - default: - return ErrUnsupported - } - if !valid { - return ErrLen - } - return nil -} - -// min tests whether a variable value is larger or equal to a given -// number. For number types, it's a simple lesser-than test; for -// strings it tests the number of characters whereas for maps -// and slices it tests the number of items. -func min(v interface{}, param string) error { - st := reflect.ValueOf(v) - invalid := false - switch st.Kind() { - case reflect.String: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - invalid = int64(len(st.String())) < p - case reflect.Slice, reflect.Map, reflect.Array: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - invalid = int64(st.Len()) < p - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - invalid = st.Int() < p - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p, err := asUint(param) - if err != nil { - return ErrBadParameter - } - invalid = st.Uint() < p - case reflect.Float32, reflect.Float64: - p, err := asFloat(param) - if err != nil { - return ErrBadParameter - } - invalid = st.Float() < p - default: - return ErrUnsupported - } - if invalid { - return ErrMin - } - return nil -} - -// max tests whether a variable value is lesser than a given -// value. For numbers, it's a simple lesser-than test; for -// strings it tests the number of characters whereas for maps -// and slices it tests the number of items. -func max(v interface{}, param string) error { - st := reflect.ValueOf(v) - var invalid bool - switch st.Kind() { - case reflect.String: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - invalid = int64(len(st.String())) > p - case reflect.Slice, reflect.Map, reflect.Array: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - invalid = int64(st.Len()) > p - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p, err := asInt(param) - if err != nil { - return ErrBadParameter - } - invalid = st.Int() > p - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p, err := asUint(param) - if err != nil { - return ErrBadParameter - } - invalid = st.Uint() > p - case reflect.Float32, reflect.Float64: - p, err := asFloat(param) - if err != nil { - return ErrBadParameter - } - invalid = st.Float() > p - default: - return ErrUnsupported - } - if invalid { - return ErrMax - } - return nil -} - -// regex is the builtin validation function that checks -// whether the string variable matches a regular expression -func regex(v interface{}, param string) error { - s, ok := v.(string) - if !ok { - return ErrUnsupported - } - - re, err := regexp.Compile(param) - if err != nil { - return ErrBadParameter - } - - if !re.MatchString(s) { - return ErrRegexp - } - return nil -} - -// asInt retuns the parameter as a int64 -// or panics if it can't convert -func asInt(param string) (int64, error) { - i, err := strconv.ParseInt(param, 0, 64) - if err != nil { - return 0, ErrBadParameter - } - return i, nil -} - -// asUint retuns the parameter as a uint64 -// or panics if it can't convert -func asUint(param string) (uint64, error) { - i, err := strconv.ParseUint(param, 0, 64) - if err != nil { - return 0, ErrBadParameter - } - return i, nil -} - -// asFloat retuns the parameter as a float64 -// or panics if it can't convert -func asFloat(param string) (float64, error) { - i, err := strconv.ParseFloat(param, 64) - if err != nil { - return 0.0, ErrBadParameter - } - return i, nil -} diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/doc.go b/Godeps/_workspace/src/gopkg.in/validator.v2/doc.go deleted file mode 100644 index df2b55e..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/doc.go +++ /dev/null @@ -1,265 +0,0 @@ -// Package validator implements value validations -// -// Copyright 2014 Roberto Teixeira -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* -Package validator implements value validations based on struct tags. - -In code it is often necessary to validate that a given value is valid before -using it for something. A typical example might be something like this. - - if age < 18 { - return error.New("age cannot be under 18") - } - -This is a simple enough example, but it can get significantly more complex, -especially when dealing with structs. - - l := len(strings.Trim(s.Username)) - if l < 3 || l > 40 || !regexp.MatchString("^[a-zA-Z]$", s.Username) || s.Age < 18 || s.Password { - return errors.New("Invalid request") - } - -You get the idea. Package validator allows one to define valid values as -struct tags when defining a new struct type. - - type NewUserRequest struct { - Username string `validate:"min=3,max=40,regexp=^[a-zA-Z]$"` - Name string `validate:"nonzero"` - Age int `validate:"min=18"` - Password string `validate:"min=8"` - } - -Then validating a variable of type NewUserRequest becomes trivial. - - nur := NewUserRequest{Username: "something", ...} - if valid, _ := validator.Validate(nur); valid { - // do something - } - -Builtin validator functions - -Here is the list of validator functions builtin in the package. - - max - For numeric numbers, max will simply make sure that the value is - equal to the parameter given. For strings, it checks that - the string length is exactly that number of characters. For slices, - arrays, and maps, validates the number of items. (Usage: len=10) - - max - For numeric numbers, max will simply make sure that the value is - lesser or equal to the parameter given. For strings, it checks that - the string length is at most that number of characters. For slices, - arrays, and maps, validates the number of items. (Usage: max=10) - - min - For numeric numbers, min will simply make sure that the value is - greater or equal to the parameter given. For strings, it checks that - the string length is at least that number of characters. For slices, - arrays, and maps, validates the number of items. (Usage: min=10) - - nonzero - This validates that the value is not zero. The appropriate zero value - is given by the Go spec (e.g. for int it's 0, for string it's "", for - pointers is nil, etc.) Usage: nonzero - - regexp - Only valid for string types, it will validate that the value matches - the regular expression provided as parameter. (Usage: regexp=^a.*b$) - - -Note that there are no tests to prevent conflicting validator parameters. For -instance, these fields will never be valid. - - ... - A int `validate:"max=0,min=1"` - B string `validate:"len=10,regexp=^$" - ... - -Custom validation functions - -It is possible to define custom validation functions by using SetValidationFunc. -First, one needs to create a validation function. - - // Very simple validation func - func notZZ(v interface{}, param string) error { - st := reflect.ValueOf(v) - if st.Kind() != reflect.String { - return validate.ErrUnsupported - } - if st.String() == "ZZ" { - return errors.New("value cannot be ZZ") - } - return nil - } - -Then one needs to add it to the list of validation funcs and give it a "tag" name. - - validate.SetValidationFunc("notzz", notZZ) - -Then it is possible to use the notzz validation tag. This will print -"Field A error: value cannot be ZZ" - - type T struct { - A string `validate:"nonzero,notzz"` - } - t := T{"ZZ"} - if valid, errs := validator.Validate(t); !valid { - fmt.Printf("Field A error: %s\n", errs["A"][0]) - } - -To use parameters, it is very similar. - - // Very simple validator with parameter - func notSomething(v interface{}, param string) error { - st := reflect.ValueOf(v) - if st.Kind() != reflect.String { - return validate.ErrUnsupported - } - if st.String() == param { - return errors.New("value cannot be " + param) - } - return nil - } - -And then the code below should print "Field A error: value cannot be ABC". - - validator.SetValidationFunc("notsomething", notSomething) - type T struct { - A string `validate:"notsomething=ABC"` - } - t := T{"ABC"} - if valid, errs := validator.Validate(t); !valid { - fmt.Printf("Field A error: %s\n", errs["A"][0]) - } - -As well, it is possible to overwrite builtin validation functions. - - validate.SetValidationFunc("min", myMinFunc) - -And you can delete a validation function by setting it to nil. - - validate.SetValidationFunc("notzz", nil) - validate.SetValidationFunc("nonzero", nil) - -Using a non-existing validation func in a field tag will always return -false and with error validate.ErrUnknownTag. - -Finally, package validator also provides a helper function that can be used -to validate simple variables/values. - - // valid: true, errs: [] - valid, errs = validator.Valid(42, "min=10, max=50") - - // valid: false, errs: [validate.ErrZeroValue] - valid, errs = validator.Valid(nil, "nonzero") - - // valid: false, errs: [validate.ErrMin,validate.ErrMax] - valid, errs = validator.Valid("hi", "nonzero,min=3,max=2") - -Custom tag name - -In case there is a reason why one would not wish to use tag 'validate' (maybe due to -a conflict with a different package), it is possible to tell the package to use -a different tag. - - validator.SetTag("valid") - -Then. - - Type T struct { - A int `valid:"min=8, max=10"` - B string `valid:"nonzero"` - } - -SetTag is permanent. The new tag name will be used until it is again changed -with a new call to SetTag. A way to temporarily use a different tag exists. - - validator.WithTag("foo").Validate(t) - validator.WithTag("bar").Validate(t) - // But this will go back to using 'validate' - validator.Validate(t) - -Multiple validators - -You may often need to have a different set of validation -rules for different situations. In all the examples above, -we only used the default validator but you could create a -new one and set specific rules for it. - -For instance, you might use the same struct to decode incoming JSON for a REST API -but your needs will change when you're using it to, say, create a new instance -in storage vs. when you need to change something. - - type User struct { - Username string `validate:"nonzero"` - Name string `validate:"nonzero"` - Age int `validate:"nonzero"` - Password string `validate:"nonzero"` - } - -Maybe when creating a new user, you need to make sure all values in the struct are filled, -but then you use the same struct to handle incoming requests to, say, change the password, -in which case you only need the Username and the Password and don't care for the others. -You might use two different validators. - - type User struct { - Username string `creating:"nonzero" chgpw:"nonzero"` - Name string `creating:"nonzero"` - Age int `creating:"nonzero"` - Password string `creating:"nonzero" chgpw:"nonzero"` - } - - var ( - creationValidator = validator.NewValidator() - chgPwValidator = validator.NewValidator() - ) - - func init() { - creationValidator.SetTag("creating") - chgPwValidator.SetTag("chgpw") - } - - ... - - func CreateUserHandler(w http.ResponseWriter, r *http.Request) { - var u User - json.NewDecoder(r.Body).Decode(&user) - if valid, _ := creationValidator.Validate(user); !valid { - // the request did not include all of the User - // struct fields, so send a http.StatusBadRequest - // back or something - } - // create the new user - } - - func SetNewUserPasswordHandler(w http.ResponseWriter, r *http.Request) { - var u User - json.NewDecoder(r.Body).Decode(&user) - if valid, _ := chgPwValidator.Validate(user); !valid { - // the request did not Username and Password, - // so send a http.StatusBadRequest - // back or something - } - // save the new password - } - -It is also possible to do all of that using only the default validator as long -as SetTag is always called before calling validator.Validate() or you chain the -with WithTag(). - -*/ -package validator diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/examplevalidate_test.go b/Godeps/_workspace/src/gopkg.in/validator.v2/examplevalidate_test.go deleted file mode 100644 index f51a163..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/examplevalidate_test.go +++ /dev/null @@ -1,145 +0,0 @@ -// Package validator implements value validations -// -// Copyright 2014 Roberto Teixeira -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validator_test - -import ( - "fmt" - "sort" - - "gopkg.in/validator.v2" -) - -// This example demonstrates a custom function to process template text. -// It installs the strings.Title function and uses it to -// Make Title Text Look Good In Our Template's Output. -func ExampleValidate() { - // First create a struct to be validated - // according to the validator tags. - type ValidateExample struct { - Name string `validate:"nonzero"` - Description string - Age int `validate:"min=18"` - Email string `validate:"regexp=^[0-9a-z]+@[0-9a-z]+(\\.[0-9a-z]+)+$"` - Address struct { - Street string `validate:"nonzero"` - City string `validate:"nonzero"` - } - } - - // Fill in some values - ve := ValidateExample{ - Name: "Joe Doe", // valid as it's nonzero - Description: "", // valid no validation tag exists - Age: 17, // invalid as age is less than required 18 - } - // invalid as Email won't match the regular expression - ve.Email = "@not.a.valid.email" - ve.Address.City = "Some City" // valid - ve.Address.Street = "" // invalid - - err := validator.Validate(ve) - if err == nil { - fmt.Println("Values are valid.") - } else { - errs := err.(validator.ErrorMap) - // See if Address was empty - if errs["Address.Street"][0] == validator.ErrZeroValue { - fmt.Println("Street cannot be empty.") - } - - // Iterate through the list of fields and respective errors - fmt.Println("Invalid due to fields:") - - // Here we have to sort the arrays to ensure map ordering does not - // fail our example, typically it's ok to just range through the err - // list when order is not important. - var errOuts []string - for f, e := range errs { - errOuts = append(errOuts, fmt.Sprintf("\t - %s (%v)\n", f, e)) - } - - // Again this part is extraneous and you should not need this in real - // code. - sort.Strings(errOuts) - for _, str := range errOuts { - fmt.Print(str) - } - } - - // Output: - // Street cannot be empty. - // Invalid due to fields: - // - Address.Street (zero value) - // - Age (less than min) - // - Email (regular expression mismatch) -} - -// This example shows how to use the Valid helper -// function to validator any number of values -func ExampleValid() { - err := validator.Valid(42, "min=10,max=100,nonzero") - fmt.Printf("42: valid=%v, errs=%v\n", err == nil, err) - - var ptr *int - if err := validator.Valid(ptr, "nonzero"); err != nil { - fmt.Println("ptr: Invalid nil pointer.") - } - - err = validator.Valid("ABBA", "regexp=[ABC]*") - fmt.Printf("ABBA: valid=%v\n", err == nil) - - // Output: - // 42: valid=true, errs= - // ptr: Invalid nil pointer. - // ABBA: valid=true -} - -// This example shows you how to change the tag name -func ExampleSetTag() { - type T struct { - A int `foo:"nonzero" bar:"min=10"` - } - t := T{5} - v := validator.NewValidator() - v.SetTag("foo") - err := v.Validate(t) - fmt.Printf("foo --> valid: %v, errs: %v\n", err == nil, err) - v.SetTag("bar") - err = v.Validate(t) - errs := err.(validator.ErrorMap) - fmt.Printf("bar --> valid: %v, errs: %v\n", err == nil, errs) - - // Output: - // foo --> valid: true, errs: - // bar --> valid: false, errs: A: less than min -} - -// This example shows you how to change the tag name -func ExampleWithTag() { - type T struct { - A int `foo:"nonzero" bar:"min=10"` - } - t := T{5} - err := validator.WithTag("foo").Validate(t) - fmt.Printf("foo --> valid: %v, errs: %v\n", err == nil, err) - err = validator.WithTag("bar").Validate(t) - fmt.Printf("bar --> valid: %v, errs: %v\n", err == nil, err) - - // Output: - // foo --> valid: true, errs: - // bar --> valid: false, errs: A: less than min -} diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/validator.go b/Godeps/_workspace/src/gopkg.in/validator.v2/validator.go deleted file mode 100644 index 4ecfe64..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/validator.go +++ /dev/null @@ -1,341 +0,0 @@ -// Package validator implements value validations -// -// Copyright 2014 Roberto Teixeira -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validator - -import ( - "errors" - "fmt" - "reflect" - "strings" - "unicode" -) - -// TextErr is an error that also implements the TextMarshaller interface for -// serializing out to various plain text encodings. Packages creating their -// own custom errors should use TextErr if they're intending to use serializing -// formats like json, msgpack etc. -type TextErr struct { - Err error -} - -// Error implements the error interface. -func (t TextErr) Error() string { - return t.Err.Error() -} - -// MarshalText implements the TextMarshaller -func (t TextErr) MarshalText() ([]byte, error) { - return []byte(t.Err.Error()), nil -} - -var ( - // ErrZeroValue is the error returned when variable has zero valud - // and nonzero was specified - ErrZeroValue = TextErr{errors.New("zero value")} - // ErrMin is the error returned when variable is less than mininum - // value specified - ErrMin = TextErr{errors.New("less than min")} - // ErrMax is the error returned when variable is more than - // maximum specified - ErrMax = TextErr{errors.New("greater than max")} - // ErrLen is the error returned when length is not equal to - // param specified - ErrLen = TextErr{errors.New("invalid length")} - // ErrRegexp is the error returned when the value does not - // match the provided regular expression parameter - ErrRegexp = TextErr{errors.New("regular expression mismatch")} - // ErrUnsupported is the error error returned when a validation rule - // is used with an unsupported variable type - ErrUnsupported = TextErr{errors.New("unsupported type")} - // ErrBadParameter is the error returned when an invalid parameter - // is provided to a validation rule (e.g. a string where an int was - // expected (max=foo,len=bar) or missing a parameter when one is required (len=)) - ErrBadParameter = TextErr{errors.New("bad parameter")} - // ErrUnknownTag is the error returned when an unknown tag is found - ErrUnknownTag = TextErr{errors.New("unknown tag")} - // ErrInvalid is the error returned when variable is invalid - // (normally a nil pointer) - ErrInvalid = TextErr{errors.New("invalid value")} -) - -// ErrorMap is a map which contains all errors from validating a struct. -type ErrorMap map[string]ErrorArray - -// ErrorMap implements the Error interface so we can check error against nil. -// The returned error is if existent the first error which was added to the map. -func (err ErrorMap) Error() string { - for k, errs := range err { - if len(errs) > 0 { - return fmt.Sprintf("%s: %s", k, errs.Error()) - } - } - - return "" -} - -// ErrorArray is a slice of errors returned by the Validate function. -type ErrorArray []error - -// ErrorArray implements the Error interface and returns the first error as -// string if existent. -func (err ErrorArray) Error() string { - if len(err) > 0 { - return err[0].Error() - } - return "" -} - -// ValidationFunc is a function that receives the value of a -// field and a parameter used for the respective validation tag. -type ValidationFunc func(v interface{}, param string) error - -// Validator implements a validator -type Validator struct { - // Tag name being used. - tagName string - // validationFuncs is a map of ValidationFuncs indexed - // by their name. - validationFuncs map[string]ValidationFunc -} - -// Helper validator so users can use the -// functions directly from the package -var defaultValidator = NewValidator() - -// NewValidator creates a new Validator -func NewValidator() *Validator { - return &Validator{ - tagName: "validate", - validationFuncs: map[string]ValidationFunc{ - "nonzero": nonzero, - "len": length, - "min": min, - "max": max, - "regexp": regex, - }, - } -} - -// SetTag allows you to change the tag name used in structs -func SetTag(tag string) { - defaultValidator.SetTag(tag) -} - -// SetTag allows you to change the tag name used in structs -func (mv *Validator) SetTag(tag string) { - mv.tagName = tag -} - -// WithTag creates a new Validator with the new tag name. It is -// useful to chain-call with Validate so we don't change the tag -// name permanently: validator.WithTag("foo").Validate(t) -func WithTag(tag string) *Validator { - return defaultValidator.WithTag(tag) -} - -// WithTag creates a new Validator with the new tag name. It is -// useful to chain-call with Validate so we don't change the tag -// name permanently: validator.WithTag("foo").Validate(t) -func (mv *Validator) WithTag(tag string) *Validator { - v := mv.copy() - v.SetTag(tag) - return v -} - -// Copy a validator -func (mv *Validator) copy() *Validator { - return &Validator{ - tagName: mv.tagName, - validationFuncs: mv.validationFuncs, - } -} - -// SetValidationFunc sets the function to be used for a given -// validation constraint. Calling this function with nil vf -// is the same as removing the constraint function from the list. -func SetValidationFunc(name string, vf ValidationFunc) error { - return defaultValidator.SetValidationFunc(name, vf) -} - -// SetValidationFunc sets the function to be used for a given -// validation constraint. Calling this function with nil vf -// is the same as removing the constraint function from the list. -func (mv *Validator) SetValidationFunc(name string, vf ValidationFunc) error { - if name == "" { - return errors.New("name cannot be empty") - } - if vf == nil { - delete(mv.validationFuncs, name) - return nil - } - mv.validationFuncs[name] = vf - return nil -} - -// Validate validates the fields of a struct based -// on 'validator' tags and returns errors found indexed -// by the field name. -func Validate(v interface{}) error { - return defaultValidator.Validate(v) -} - -// Validate validates the fields of a struct based -// on 'validator' tags and returns errors found indexed -// by the field name. -func (mv *Validator) Validate(v interface{}) error { - sv := reflect.ValueOf(v) - st := reflect.TypeOf(v) - if sv.Kind() == reflect.Ptr && !sv.IsNil() { - return mv.Validate(sv.Elem().Interface()) - } - if sv.Kind() != reflect.Struct { - return ErrUnsupported - } - - nfields := sv.NumField() - m := make(ErrorMap) - for i := 0; i < nfields; i++ { - f := sv.Field(i) - // deal with pointers - for f.Kind() == reflect.Ptr && !f.IsNil() { - f = f.Elem() - } - tag := st.Field(i).Tag.Get(mv.tagName) - if f.Kind() == reflect.Ptr { - ff := f.Elem() - if ff.Kind() == reflect.Struct { - - } - } - if tag == "-" { - continue - } - if tag == "" && f.Kind() != reflect.Struct { - continue - } - fname := st.Field(i).Name - var errs ErrorArray - switch f.Kind() { - case reflect.Struct: - if !unicode.IsUpper(rune(fname[0])) { - continue - } - e := mv.Validate(f.Interface()) - if e, ok := e.(ErrorMap); ok && len(e) > 0 { - for j, k := range e { - m[fname+"."+j] = k - } - } - - default: - err := mv.Valid(f.Interface(), tag) - if errors, ok := err.(ErrorArray); ok { - errs = errors - } else { - if err != nil { - errs = ErrorArray{err} - } - } - } - - if len(errs) > 0 { - m[st.Field(i).Name] = errs - } - } - if len(m) > 0 { - return m - } - return nil -} - -// Valid validates a value based on the provided -// tags and returns errors found or nil. -func Valid(val interface{}, tags string) error { - return defaultValidator.Valid(val, tags) -} - -// Valid validates a value based on the provided -// tags and returns errors found or nil. -func (mv *Validator) Valid(val interface{}, tags string) error { - if tags == "-" { - return nil - } - v := reflect.ValueOf(val) - if v.Kind() == reflect.Ptr && !v.IsNil() { - return mv.Valid(v.Elem().Interface(), tags) - } - var err error - switch v.Kind() { - case reflect.Struct: - return ErrUnsupported - case reflect.Invalid: - err = mv.validateVar(nil, tags) - default: - err = mv.validateVar(val, tags) - } - return err -} - -// validateVar validates one single variable -func (mv *Validator) validateVar(v interface{}, tag string) error { - tags, err := mv.parseTags(tag) - if err != nil { - // unknown tag found, give up. - return err - } - errs := make(ErrorArray, 0, len(tags)) - for _, t := range tags { - if err := t.Fn(v, t.Param); err != nil { - errs = append(errs, err) - } - } - if len(errs) > 0 { - return errs - } - return nil -} - -// tag represents one of the tag items -type tag struct { - Name string // name of the tag - Fn ValidationFunc // validation function to call - Param string // parameter to send to the validation function -} - -// parseTags parses all individual tags found within a struct tag. -func (mv *Validator) parseTags(t string) ([]tag, error) { - tl := strings.Split(t, ",") - tags := make([]tag, 0, len(tl)) - for _, i := range tl { - tg := tag{} - v := strings.SplitN(i, "=", 2) - tg.Name = strings.Trim(v[0], " ") - if tg.Name == "" { - return []tag{}, ErrUnknownTag - } - if len(v) > 1 { - tg.Param = strings.Trim(v[1], " ") - } - var found bool - if tg.Fn, found = mv.validationFuncs[tg.Name]; !found { - return []tag{}, ErrUnknownTag - } - tags = append(tags, tg) - - } - return tags, nil -} diff --git a/Godeps/_workspace/src/gopkg.in/validator.v2/validator_test.go b/Godeps/_workspace/src/gopkg.in/validator.v2/validator_test.go deleted file mode 100644 index 6076024..0000000 --- a/Godeps/_workspace/src/gopkg.in/validator.v2/validator_test.go +++ /dev/null @@ -1,300 +0,0 @@ -// Package validator implements value validations -// -// Copyright 2014 Roberto Teixeira -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package validator_test - -import ( - "testing" - - . "gopkg.in/check.v1" - "gopkg.in/validator.v2" -) - -func Test(t *testing.T) { - TestingT(t) -} - -type MySuite struct{} - -var _ = Suite(&MySuite{}) - -type Simple struct { - A int `validate:"min=10"` -} - -type TestStruct struct { - A int `validate:"nonzero"` - B string `validate:"len=8,min=6,max=4"` - Sub struct { - A int `validate:"nonzero"` - B string - C float64 `validate:"nonzero,min=1"` - D *string `validate:"nonzero"` - } - D *Simple `validate:"nonzero"` -} - -func (ms *MySuite) TestValidate(c *C) { - t := TestStruct{ - A: 0, - B: "12345", - } - t.Sub.A = 1 - t.Sub.B = "" - t.Sub.C = 0.0 - t.D = &Simple{10} - - err := validator.Validate(t) - c.Assert(err, NotNil) - - errs, ok := err.(validator.ErrorMap) - c.Assert(ok, Equals, true) - c.Assert(errs["A"], HasError, validator.ErrZeroValue) - c.Assert(errs["B"], HasError, validator.ErrLen) - c.Assert(errs["B"], HasError, validator.ErrMin) - c.Assert(errs["B"], HasError, validator.ErrMax) - c.Assert(errs["Sub.A"], HasLen, 0) - c.Assert(errs["Sub.B"], HasLen, 0) - c.Assert(errs["Sub.C"], HasLen, 2) - c.Assert(errs["Sub.D"], HasError, validator.ErrZeroValue) -} - -func (ms *MySuite) TestValidSlice(c *C) { - s := make([]int, 0, 10) - err := validator.Valid(s, "nonzero") - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrZeroValue) - - for i := 0; i < 10; i++ { - s = append(s, i) - } - - err = validator.Valid(s, "min=11,max=5,len=9,nonzero") - c.Assert(err, NotNil) - errs, ok = err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrMin) - c.Assert(errs, HasError, validator.ErrMax) - c.Assert(errs, HasError, validator.ErrLen) - c.Assert(errs, Not(HasError), validator.ErrZeroValue) -} - -func (ms *MySuite) TestValidMap(c *C) { - m := make(map[string]string) - err := validator.Valid(m, "nonzero") - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrZeroValue) - - err = validator.Valid(m, "min=1") - c.Assert(err, NotNil) - errs, ok = err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrMin) - - m = map[string]string{"A": "a", "B": "a"} - err = validator.Valid(m, "max=1") - c.Assert(err, NotNil) - errs, ok = err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrMax) - - err = validator.Valid(m, "min=2, max=5") - c.Assert(err, IsNil) - - m = map[string]string{ - "1": "a", - "2": "b", - "3": "c", - "4": "d", - "5": "e", - } - err = validator.Valid(m, "len=4,min=6,max=1,nonzero") - c.Assert(err, NotNil) - errs, ok = err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrLen) - c.Assert(errs, HasError, validator.ErrMin) - c.Assert(errs, HasError, validator.ErrMax) - c.Assert(errs, Not(HasError), validator.ErrZeroValue) - -} - -func (ms *MySuite) TestValidFloat(c *C) { - err := validator.Valid(12.34, "nonzero") - c.Assert(err, IsNil) - - err = validator.Valid(0.0, "nonzero") - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrZeroValue) -} - -func (ms *MySuite) TestValidInt(c *C) { - i := 123 - err := validator.Valid(i, "nonzero") - c.Assert(err, IsNil) - - err = validator.Valid(i, "min=1") - c.Assert(err, IsNil) - - err = validator.Valid(i, "min=124, max=122") - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrMin) - c.Assert(errs, HasError, validator.ErrMax) - - err = validator.Valid(i, "max=10") - c.Assert(err, NotNil) - errs, ok = err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrMax) -} - -func (ms *MySuite) TestValidString(c *C) { - s := "test1234" - err := validator.Valid(s, "len=8") - c.Assert(err, IsNil) - - err = validator.Valid(s, "len=0") - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasError, validator.ErrLen) - - err = validator.Valid(s, "regexp=^[tes]{4}.*") - c.Assert(err, IsNil) - - err = validator.Valid(s, "regexp=^.*[0-9]{5}$") - c.Assert(errs, NotNil) - - err = validator.Valid("", "nonzero,len=3,max=1") - c.Assert(err, NotNil) - errs, ok = err.(validator.ErrorArray) - c.Assert(ok, Equals, true) - c.Assert(errs, HasLen, 2) - c.Assert(errs, HasError, validator.ErrZeroValue) - c.Assert(errs, HasError, validator.ErrLen) - c.Assert(errs, Not(HasError), validator.ErrMax) -} - -func (ms *MySuite) TestValidateStructVar(c *C) { - type test struct { - A int - } - t := test{} - err := validator.Valid(t, "") - c.Assert(err, Equals, validator.ErrUnsupported) -} - -func (ms *MySuite) TestValidateOmittedStructVar(c *C) { - type test2 struct { - B int `validate:"min=1"` - } - type test1 struct { - A test2 `validate:"-"` - } - - t := test1{} - err := validator.Validate(t) - c.Assert(err, IsNil) - - errs := validator.Valid(test2{}, "-") - c.Assert(errs, IsNil) -} - -func (ms *MySuite) TestUnknownTag(c *C) { - type test struct { - A int `validate:"foo"` - } - t := test{} - err := validator.Validate(t) - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorMap) - c.Assert(ok, Equals, true) - c.Assert(errs, HasLen, 1) - c.Assert(errs["A"], HasError, validator.ErrUnknownTag) -} - -func (ms *MySuite) TestUnsupported(c *C) { - type test struct { - A int `validate:"regexp=a.*b"` - B float64 `validate:"regexp=.*"` - } - t := test{} - err := validator.Validate(t) - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorMap) - c.Assert(ok, Equals, true) - c.Assert(errs, HasLen, 2) - c.Assert(errs["A"], HasError, validator.ErrUnsupported) - c.Assert(errs["B"], HasError, validator.ErrUnsupported) -} - -func (ms *MySuite) TestBadParameter(c *C) { - type test struct { - A string `validate:"min="` - B string `validate:"len=="` - C string `validate:"max=foo"` - } - t := test{} - err := validator.Validate(t) - c.Assert(err, NotNil) - errs, ok := err.(validator.ErrorMap) - c.Assert(ok, Equals, true) - c.Assert(errs, HasLen, 3) - c.Assert(errs["A"], HasError, validator.ErrBadParameter) - c.Assert(errs["B"], HasError, validator.ErrBadParameter) - c.Assert(errs["C"], HasError, validator.ErrBadParameter) -} - -type hasErrorChecker struct { - *CheckerInfo -} - -func (c *hasErrorChecker) Check(params []interface{}, names []string) (bool, string) { - var ( - ok bool - slice []error - value error - ) - slice, ok = params[0].(validator.ErrorArray) - if !ok { - return false, "First parameter is not an Errorarray" - } - value, ok = params[1].(error) - if !ok { - return false, "Second parameter is not an error" - } - - for _, v := range slice { - if v == value { - return true, "" - } - } - return false, "" -} - -func (c *hasErrorChecker) Info() *CheckerInfo { - return c.CheckerInfo -} - -var HasError = &hasErrorChecker{&CheckerInfo{Name: "HasError", Params: []string{"HasError", "expected to contain"}}} diff --git a/README.md b/README.md index 4ceeb46..641ae66 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ Go JSON API Specification Handler --- -[![GoDoc](https://godoc.org/github.com/derekdowling/go-json-spec-handler?status.png)](https://godoc.org/github.com/derekdowling/go-json-spec-handler) [![Build Status](https://img.shields.io/travis/derekdowling/go-json-spec-handler.svg)](https://travis-ci.org/derekdowling/go-json-spec-handler) +[![GoDoc](https://godoc.org/github.com/derekdowling/go-json-spec-handler?status.png)](https://godoc.org/github.com/derekdowling/go-json-spec-handler) +[![Build +Status](https://img.shields.io/travis/derekdowling/go-json-spec-handler.svg)](https://travis-ci.org/derekdowling/go-json-spec-handler) +[TestCoverage](http://gocover.io/github.com/derekdowling/go-json-spec-handler?version=1.5rc1) A Golang API helper that deals with request serialization and response sending for creating a [JSON API Specification](http://jsonapi.org/) compatible Golang API. Great for Ember.js! diff --git a/error.go b/error.go index 082d975..7d3a51f 100644 --- a/error.go +++ b/error.go @@ -3,6 +3,7 @@ package jsh import ( "fmt" "net/http" + "strings" ) // DefaultError can be customized in order to provide a more customized error @@ -13,6 +14,13 @@ var DefaultErrorDetail = "Request failed, something went wrong." // DefaultTitle can be customized to provide a more customized ISE Title var DefaultErrorTitle = "Internal Server Error" +// SendableError conforms to a standard error format for logging, but can also +// be sent as a JSON response +type SendableError interface { + Sendable + Error() string +} + // Error represents a JSON Specification Error. Error.Source.Pointer is used in 422 // status responses to indicate validation errors on a JSON Object attribute. // @@ -31,9 +39,13 @@ type Error struct { ISE string `json:"-"` } +func (e *Error) Error() string { + return fmt.Sprintf("%s: %s. %s", e.Title, e.Detail, e.Source.Pointer) +} + // Prepare returns a response containing a prepared error list since the JSON // API specification requires that errors are returned as a list -func (e *Error) Prepare(req *http.Request) (*Response, *Error) { +func (e *Error) Prepare(req *http.Request) (*Response, SendableError) { list := &ErrorList{Errors: []*Error{e}} return list.Prepare(req) } @@ -43,8 +55,17 @@ type ErrorList struct { Errors []*Error } +// Error allows ErrorList to conform to the default Go error interface +func (e *ErrorList) Error() string { + err := "Errors: " + for _, e := range e.Errors { + err = fmt.Sprintf("%s%s;", err, e.Error()) + } + return err +} + // Add first validates the error, and then appends it to the ErrorList -func (e *ErrorList) Add(newError *Error) error { +func (e *ErrorList) Add(newError *Error) *Error { err := validateError(newError) if err != nil { return err @@ -55,7 +76,7 @@ func (e *ErrorList) Add(newError *Error) error { } // Prepare first validates the errors, and then returns an appropriate response -func (e *ErrorList) Prepare(req *http.Request) (*Response, *Error) { +func (e *ErrorList) Prepare(req *http.Request) (*Response, SendableError) { if len(e.Errors) == 0 { return nil, ISE("No errors provided for attempted error response.") } @@ -64,12 +85,12 @@ func (e *ErrorList) Prepare(req *http.Request) (*Response, *Error) { } // validateError ensures that the error is ready for a response in it's current state -func validateError(err *Error) error { +func validateError(err *Error) *Error { if err.Status < 400 || err.Status > 600 { - return fmt.Errorf("Invalid HTTP Status for error %+v\n", err) + return ISE(fmt.Sprintf("Invalid HTTP Status for error %+v\n", err)) } else if err.Status == 422 && err.Source.Pointer == "" { - return fmt.Errorf("Source Pointer must be set for 422 Status errors") + return ISE(fmt.Sprintf("Source Pointer must be set for 422 Status errors")) } return nil @@ -97,7 +118,7 @@ func InputError(attribute string, detail string) *Error { } // Assign this after the fact, easier to do - err.Source.Pointer = fmt.Sprintf("data/attributes/%s", attribute) + err.Source.Pointer = fmt.Sprintf("data/attributes/%s", strings.ToLower(attribute)) return err } diff --git a/error_test.go b/error_test.go index 5d45797..9d53ac8 100644 --- a/error_test.go +++ b/error_test.go @@ -1,7 +1,6 @@ package jsh import ( - "log" "net/http" "net/http/httptest" "strconv" @@ -55,33 +54,55 @@ func TestError(t *testing.T) { }) Convey("->Send()", func() { + err := Send(request, writer, testError) + So(err, ShouldBeNil) + So(writer.Code, ShouldEqual, http.StatusBadRequest) + }) - testErrors := &ErrorList{Errors: []*Error{&Error{ - Status: http.StatusForbidden, - Title: "Forbidden", - Detail: "Can't Go Here", - }, testError}} + Convey("Error List Tests", func() { - Convey("->Send(Error)", func() { - err := Send(request, writer, testError) - So(err, ShouldBeNil) - log.Printf("err = %+v\n", err) - log.Printf("writer = %+v\n", writer) - So(writer.Code, ShouldEqual, http.StatusBadRequest) - }) + Convey("->Add()", func() { - Convey("should send a properly formatted JSON Error", func() { - err := Send(request, writer, testErrors) + list := &ErrorList{} - So(err, ShouldBeNil) - So(writer.Code, ShouldEqual, http.StatusForbidden) + Convey("should successfully add a valid error", func() { + err := list.Add(testError) + So(err, ShouldBeNil) + So(len(list.Errors), ShouldEqual, 1) + }) - contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length")) - So(convErr, ShouldBeNil) - So(contentLength, ShouldBeGreaterThan, 0) - So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType) + Convey("should error if validation fails while adding an error", func() { + badError := &Error{ + Title: "Invalid", + Detail: "So badly", + } + + err := list.Add(badError) + So(err.Status, ShouldEqual, 500) + So(list.Errors, ShouldBeEmpty) + }) }) + Convey("->Send(ErrorList)", func() { + + testErrors := &ErrorList{Errors: []*Error{&Error{ + Status: http.StatusForbidden, + Title: "Forbidden", + Detail: "Can't Go Here", + }, testError}} + + Convey("should send a properly formatted JSON error list", func() { + err := Send(request, writer, testErrors) + + So(err, ShouldBeNil) + So(writer.Code, ShouldEqual, http.StatusForbidden) + + contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length")) + So(convErr, ShouldBeNil) + So(contentLength, ShouldBeGreaterThan, 0) + So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType) + }) + }) }) }) } diff --git a/list.go b/list.go index 640631e..5a6796d 100644 --- a/list.go +++ b/list.go @@ -8,6 +8,6 @@ type List struct { } // Prepare returns a success status response -func (l List) Prepare(r *http.Request) (*Response, *Error) { +func (l List) Prepare(r *http.Request) (*Response, SendableError) { return &Response{Data: l.Objects, HTTPStatus: http.StatusOK}, nil } diff --git a/object.go b/object.go index bd282f5..93034e3 100644 --- a/object.go +++ b/object.go @@ -3,16 +3,15 @@ package jsh import ( "encoding/json" "fmt" - "log" "net/http" - "gopkg.in/validator.v2" + "github.com/asaskevich/govalidator" ) // Object represents the default JSON spec for objects type Object struct { - Type string `json:"type"` - ID string `json:"id"` + Type string `json:"type" valid:"alpha,required"` + ID string `json:"id" valid:"required"` Attributes json.RawMessage `json:"attributes,omitempty"` Links map[string]*Link `json:"links,omitempty"` Relationships map[string]*Object `json:"relationships,omitempty"` @@ -20,7 +19,7 @@ type Object struct { // NewObject prepares a new JSON Object for an API response. Whatever is provided // as attributes will be marshalled to JSON. -func NewObject(id string, objType string, attributes interface{}) (*Object, error) { +func NewObject(id string, objType string, attributes interface{}) (*Object, SendableError) { object := &Object{ ID: id, Type: objType, @@ -30,7 +29,7 @@ func NewObject(id string, objType string, attributes interface{}) (*Object, erro rawJSON, err := json.MarshalIndent(attributes, "", " ") if err != nil { - return nil, fmt.Errorf("Error marshaling attrs while creating a new JSON Object: %s", err) + return nil, ISE(fmt.Sprintf("Error marshaling attrs while creating a new JSON Object: %s", err)) } object.Attributes = rawJSON @@ -58,7 +57,7 @@ func NewObject(id string, objType string, attributes interface{}) (*Object, erro // // log errors via error.ISE // jsh.Send(r, w, errors) // } -func (o *Object) Unmarshal(objType string, target interface{}) (err Sendable) { +func (o *Object) Unmarshal(objType string, target interface{}) (err SendableError) { if objType != o.Type { err = ISE(fmt.Sprintf( @@ -80,17 +79,12 @@ func (o *Object) Unmarshal(objType string, target interface{}) (err Sendable) { return } - ok, errors := validateInput(target) - if !ok { - return errors - } - - return nil + return validateInput(target) } // Prepare creates a new JSON single object response with an appropriate HTTP status // to match the request method type. -func (o *Object) Prepare(r *http.Request) (*Response, *Error) { +func (o *Object) Prepare(r *http.Request) (*Response, SendableError) { var status int @@ -115,21 +109,28 @@ func (o *Object) Prepare(r *http.Request) (*Response, *Error) { // validateInput runs go-validator on each attribute on the struct and returns all // errors that it picks up -func validateInput(target interface{}) (ok bool, errors *ErrorList) { - ok = true - errors = &ErrorList{} - - err := validator.Validate(target) - if err != nil { - ok = false - log.Printf("errors = %+v\n", errors) - - // Each attribute can have multiple errors, only return the first one for each - // for attributeName, attributeErrors := range errs { - // attributeError := attributeErrors[0] - // errors.Add(InputError(attributeName, attributeError)) - // } +func validateInput(target interface{}) SendableError { + + _, validationError := govalidator.ValidateStruct(target) + if validationError != nil { + + manyErrors, isType := validationError.(govalidator.Errors) + if isType { + list := &ErrorList{} + for _, err := range manyErrors.Errors() { + singleErr, _ := err.(govalidator.Error) + list.Add(InputError(singleErr.Name, singleErr.Err.Error())) + } + + // Don't send back a list if it's just a single error, govalidator + // seems to always return an error Array even for a single error + if len(list.Errors) == 1 { + return list.Errors[0] + } + + return list + } } - return + return nil } diff --git a/object_test.go b/object_test.go index 98fb2d0..535500f 100644 --- a/object_test.go +++ b/object_test.go @@ -15,7 +15,7 @@ func TestObject(t *testing.T) { testObject := &Object{ ID: "ID123", - Type: "testConversion", + Type: "testObject", Attributes: json.RawMessage(`{"foo":"bar"}`), } @@ -35,31 +35,74 @@ func TestObject(t *testing.T) { }) Convey("->Unmarshal()", func() { - testConversion := struct { - ID string - Foo string `json:"foo"` - }{} - Convey("Should successfully populate a valid struct", func() { - err := testObject.Unmarshal("testConversion", &testConversion) - So(err, ShouldBeNil) - So(testConversion.Foo, ShouldEqual, "bar") - }) + Convey("non-govalidator structs", func() { + + testConversion := struct { + ID string + Foo string `json:"foo"` + }{} + + Convey("Should successfully populate a valid struct", func() { + err := testObject.Unmarshal("testObject", &testConversion) + So(err, ShouldBeNil) + So(testConversion.Foo, ShouldEqual, "bar") + }) + + Convey("Should reject a non-matching type", func() { + err := testObject.Unmarshal("badType", &testConversion) + So(err, ShouldNotBeNil) + }) - Convey("Should reject a non-matching type", func() { - err := testObject.Unmarshal("badType", &testConversion) - So(err, ShouldNotBeNil) }) - Convey("with input validation", func() { + Convey("govalidator struct unmarshals", func() { Convey("should not error if input validates properly", func() { + testValidation := struct { + Foo string `json:"foo" valid:"alphanum"` + }{} + err := testObject.Unmarshal("testObject", &testValidation) + So(err, ShouldBeNil) + So(testValidation.Foo, ShouldEqual, "bar") }) - Convey("should return a 422 Error correctly for validation failure", func() { + Convey("should return a 422 Error correctly for a validation failure", func() { + testValidation := struct { + Foo string `valid:"ipv4,required" json:"foo"` + }{} + unmarshalErr := testObject.Unmarshal("testObject", &testValidation) + So(unmarshalErr, ShouldNotBeNil) + + e, ok := unmarshalErr.(*Error) + So(ok, ShouldBeTrue) + So(e.Source.Pointer, ShouldEqual, "data/attributes/foo") + }) + + Convey("should return a 422 Error correctly for multiple validation failures", func() { + + testManyObject := &Object{ + ID: "ID123", + Type: "testObject", + Attributes: json.RawMessage(`{"foo":"bar", "baz":"4567"}`), + } + + testManyValidations := struct { + Foo string `valid:"ipv4,required" json:"foo"` + Baz string `valid:"alpha,required" json:"baz"` + }{} + + unmarshalErr := testManyObject.Unmarshal("testObject", &testManyValidations) + So(unmarshalErr, ShouldNotBeNil) + + errorList, ok := unmarshalErr.(*ErrorList) + So(ok, ShouldBeTrue) + So(errorList.Errors[0].Source.Pointer, ShouldEqual, "data/attributes/foo") + So(errorList.Errors[1].Source.Pointer, ShouldEqual, "data/attributes/baz") }) + }) }) diff --git a/parse.go b/parse.go index 701fa8f..f739f24 100644 --- a/parse.go +++ b/parse.go @@ -15,7 +15,7 @@ const ( // ParseObject returns a JSON object for a given io.ReadCloser containing // a raw JSON payload -func ParseObject(reader io.ReadCloser) (*Object, *Error) { +func ParseObject(reader io.ReadCloser) (*Object, SendableError) { defer closeReader(reader) byteData, err := ioutil.ReadAll(reader) @@ -35,12 +35,13 @@ func ParseObject(reader io.ReadCloser) (*Object, *Error) { )) } - return &data.Object, nil + object := &data.Object + return object, validateInput(object) } // ParseList returns a JSON List for a given io.ReadCloser containing // a raw JSON payload -func ParseList(reader io.ReadCloser) ([]*Object, *Error) { +func ParseList(reader io.ReadCloser) ([]*Object, SendableError) { defer closeReader(reader) byteData, err := ioutil.ReadAll(reader) @@ -60,6 +61,13 @@ func ParseList(reader io.ReadCloser) ([]*Object, *Error) { )) } + for _, object := range data.List { + err := validateInput(object) + if err != nil { + return nil, err + } + } + return data.List, nil } diff --git a/parse_test.go b/parse_test.go index 5edae0e..2e966d9 100644 --- a/parse_test.go +++ b/parse_test.go @@ -12,35 +12,74 @@ func TestParsing(t *testing.T) { Convey("Request Tests", t, func() { Convey("->ParseObject()", func() { - objectJSON := `{"data": {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}}` - closer := createIOCloser([]byte(objectJSON)) + Convey("should parse a valid object", func() { - object, err := ParseObject(closer) - So(err, ShouldBeNil) - So(object, ShouldNotBeEmpty) - So(object.Type, ShouldEqual, "user") - So(object.ID, ShouldEqual, "sweetID123") - So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"123"}`)) + objectJSON := `{"data": {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}}` + + closer := createIOCloser([]byte(objectJSON)) + + object, err := ParseObject(closer) + So(err, ShouldBeNil) + So(object, ShouldNotBeEmpty) + So(object.Type, ShouldEqual, "user") + So(object.ID, ShouldEqual, "sweetID123") + So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"123"}`)) + }) + + Convey("should reject an object with missing attributes", func() { + objectJSON := `{"data": {"id": "sweetID123", "attributes": {"ID":"123"}}}` + closer := createIOCloser([]byte(objectJSON)) + + _, err := ParseObject(closer) + So(err, ShouldNotBeNil) + + vErr, ok := err.(*Error) + So(ok, ShouldBeTrue) + So(vErr.Status, ShouldEqual, 422) + So(vErr.Source.Pointer, ShouldEqual, "data/attributes/type") + }) }) Convey("->ParseList()", func() { - listJSON := - `{"data": [ + + Convey("should parse a valid list", func() { + + listJSON := + `{"data": [ {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}, {"type": "user", "id": "sweetID456", "attributes": {"ID":"456"}} ]}` - closer := createIOCloser([]byte(listJSON)) + closer := createIOCloser([]byte(listJSON)) + + list, err := ParseList(closer) + So(err, ShouldBeNil) + So(len(list), ShouldEqual, 2) + + object := list[1] + So(object.Type, ShouldEqual, "user") + So(object.ID, ShouldEqual, "sweetID456") + So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"456"}`)) + }) + + Convey("should error for an invalid list", func() { + listJSON := + `{"data": [ + {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}, + {"type": "user", "attributes": {"ID":"456"}} +]}` + + closer := createIOCloser([]byte(listJSON)) - list, err := ParseList(closer) - So(err, ShouldBeNil) - So(len(list), ShouldEqual, 2) + _, err := ParseList(closer) + So(err, ShouldNotBeNil) - object := list[1] - So(object.Type, ShouldEqual, "user") - So(object.ID, ShouldEqual, "sweetID456") - So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"456"}`)) + vErr, ok := err.(*Error) + So(ok, ShouldBeTrue) + So(vErr.Status, ShouldEqual, 422) + So(vErr.Source.Pointer, ShouldEqual, "data/attributes/id") + }) }) }) } diff --git a/send.go b/response.go similarity index 93% rename from send.go rename to response.go index cf61294..b489b5e 100644 --- a/send.go +++ b/response.go @@ -11,6 +11,12 @@ import ( // http://jsonapi.org/format/1.1/ const JSONAPIVersion = "1.1" +// Sendable implements functions that allows different response types +// to produce a sendable JSON Response format +type Sendable interface { + Prepare(r *http.Request) (*Response, SendableError) +} + // Response represents the top level json format of incoming requests // and outgoing responses type Response struct { @@ -48,15 +54,9 @@ func (r *Response) Validate() *Error { return nil } -// Sendable implements functions that allow different data types -// to produce a sendable JSON Response format -type Sendable interface { - Prepare(r *http.Request) (*Response, *Error) -} - // Send fires a JSON response if the payload is prepared successfully, otherwise it // returns an Error which can also be sent. -func Send(r *http.Request, w http.ResponseWriter, payload Sendable) *Error { +func Send(r *http.Request, w http.ResponseWriter, payload Sendable) SendableError { response, err := payload.Prepare(r) if err != nil { return err @@ -67,7 +67,7 @@ func Send(r *http.Request, w http.ResponseWriter, payload Sendable) *Error { // SendResponse handles sending a fully packaged JSON Response allows API consumers // to more manually build their Responses in case they want to send Meta, Links, etc -func SendResponse(r *http.Request, w http.ResponseWriter, response *Response) *Error { +func SendResponse(r *http.Request, w http.ResponseWriter, response *Response) SendableError { err := response.Validate() if err != nil { diff --git a/send_test.go b/response_test.go similarity index 100% rename from send_test.go rename to response_test.go