aboutsummaryrefslogtreecommitdiff
path: root/vendor/gopkg.in/bluesuncorp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gopkg.in/bluesuncorp')
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/.gitignore26
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/.travis.yml13
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/LICENSE22
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/README.md44
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/baked_in.go999
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/doc.go474
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/regexes.go64
-rw-r--r--vendor/gopkg.in/bluesuncorp/validator.v5/validator.go935
8 files changed, 2577 insertions, 0 deletions
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/.gitignore b/vendor/gopkg.in/bluesuncorp/validator.v5/.gitignore
new file mode 100644
index 0000000..4159020
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/.gitignore
@@ -0,0 +1,26 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+*.test
+*.out \ No newline at end of file
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/.travis.yml b/vendor/gopkg.in/bluesuncorp/validator.v5/.travis.yml
new file mode 100644
index 0000000..68398d9
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/.travis.yml
@@ -0,0 +1,13 @@
+language: go
+
+notificaitons:
+ email:
+ recipients: bluesuncorp01@gmail.com
+ on_success: change
+ on_failure: always
+
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - tip \ No newline at end of file
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/LICENSE b/vendor/gopkg.in/bluesuncorp/validator.v5/LICENSE
new file mode 100644
index 0000000..6a2ae9a
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Dean Karn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/README.md b/vendor/gopkg.in/bluesuncorp/validator.v5/README.md
new file mode 100644
index 0000000..c1e9d00
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/README.md
@@ -0,0 +1,44 @@
+Package validator
+================
+[![Build Status](https://travis-ci.org/bluesuncorp/validator.svg?branch=v5.1)](https://travis-ci.org/bluesuncorp/validator)
+[![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v5?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v5)
+
+Package validator implements value validations for structs and individual fields based on tags.
+It is also capable of Cross Field and Cross Struct validations.
+
+Installation
+============
+
+Use go get.
+
+ go get gopkg.in/bluesuncorp/validator.v5
+
+or to update
+
+ go get -u gopkg.in/bluesuncorp/validator.v5
+
+Then import the validator package into your own code.
+
+ import "gopkg.in/bluesuncorp/validator.v5"
+
+Usage and documentation
+=======================
+
+Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v5 for detailed usage docs.
+
+How to Contribute
+=================
+
+There will always be a development branch for each version i.e. `v1-development`. In order to contribute,
+please make your pull requests against those branches.
+
+If the changes being proposed or requested are breaking changes, please create an issue, for discussion
+or create a pull request against the highest development branch for example this package has a
+v1 and v1-development branch however, there will also be a v2-development brach even though v2 doesn't exist yet.
+
+I strongly encourage everyone whom creates a custom validation function to contribute them and
+help make this package even better.
+
+License
+=======
+Distributed under MIT License, please see license file in code for more details.
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/baked_in.go b/vendor/gopkg.in/bluesuncorp/validator.v5/baked_in.go
new file mode 100644
index 0000000..22746ad
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/baked_in.go
@@ -0,0 +1,999 @@
+package validator
+
+import (
+ "fmt"
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+)
+
+// BakedInValidators is the default map of ValidationFunc
+// you can add, remove or even replace items to suite your needs,
+// or even disregard and use your own map if so desired.
+var BakedInValidators = map[string]Func{
+ "required": hasValue,
+ "len": hasLengthOf,
+ "min": hasMinOf,
+ "max": hasMaxOf,
+ "eq": isEq,
+ "ne": isNe,
+ "lt": isLt,
+ "lte": isLte,
+ "gt": isGt,
+ "gte": isGte,
+ "eqfield": isEqField,
+ "nefield": isNeField,
+ "gtefield": isGteField,
+ "gtfield": isGtField,
+ "ltefield": isLteField,
+ "ltfield": isLtField,
+ "alpha": isAlpha,
+ "alphanum": isAlphanum,
+ "numeric": isNumeric,
+ "number": isNumber,
+ "hexadecimal": isHexadecimal,
+ "hexcolor": isHexcolor,
+ "rgb": isRgb,
+ "rgba": isRgba,
+ "hsl": isHsl,
+ "hsla": isHsla,
+ "email": isEmail,
+ "url": isURL,
+ "uri": isURI,
+ "base64": isBase64,
+ "contains": contains,
+ "containsany": containsAny,
+ "containsrune": containsRune,
+ "excludes": excludes,
+ "excludesall": excludesAll,
+ "excludesrune": excludesRune,
+ "isbn": isISBN,
+ "isbn10": isISBN10,
+ "isbn13": isISBN13,
+ "uuid": isUUID,
+ "uuid3": isUUID3,
+ "uuid4": isUUID4,
+ "uuid5": isUUID5,
+ "ascii": isASCII,
+ "printascii": isPrintableASCII,
+ "multibyte": hasMultiByteCharacter,
+ "datauri": isDataURI,
+ "latitude": isLatitude,
+ "longitude": isLongitude,
+ "ssn": isSSN,
+}
+
+func isSSN(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if len(field.(string)) != 11 {
+ return false
+ }
+
+ return matchesRegex(sSNRegex, field)
+}
+
+func isLongitude(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(longitudeRegex, field)
+}
+
+func isLatitude(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(latitudeRegex, field)
+}
+
+func isDataURI(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ uri := strings.SplitN(field.(string), ",", 2)
+
+ if len(uri) != 2 {
+ return false
+ }
+
+ if !matchesRegex(dataURIRegex, uri[0]) {
+ return false
+ }
+
+ return isBase64(top, current, uri[1], param)
+}
+
+func hasMultiByteCharacter(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if len(field.(string)) == 0 {
+ return true
+ }
+
+ return matchesRegex(multibyteRegex, field)
+}
+
+func isPrintableASCII(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(printableASCIIRegex, field)
+}
+
+func isASCII(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(aSCIIRegex, field)
+}
+
+func isUUID5(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(uUID5Regex, field)
+}
+
+func isUUID4(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(uUID4Regex, field)
+}
+
+func isUUID3(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(uUID3Regex, field)
+}
+
+func isUUID(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(uUIDRegex, field)
+}
+
+func isISBN(top interface{}, current interface{}, field interface{}, param string) bool {
+ return isISBN10(top, current, field, param) || isISBN13(top, current, field, param)
+}
+
+func isISBN13(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ s := strings.Replace(strings.Replace(field.(string), "-", "", 4), " ", "", 4)
+
+ if !matchesRegex(iSBN13Regex, s) {
+ return false
+ }
+
+ var checksum int32
+ var i int32
+
+ factor := []int32{1, 3}
+
+ for i = 0; i < 12; i++ {
+ checksum += factor[i%2] * int32(s[i]-'0')
+ }
+
+ if (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0 {
+ return true
+ }
+
+ return false
+}
+
+func isISBN10(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ s := strings.Replace(strings.Replace(field.(string), "-", "", 3), " ", "", 3)
+
+ if !matchesRegex(iSBN10Regex, s) {
+ return false
+ }
+
+ var checksum int32
+ var i int32
+
+ for i = 0; i < 9; i++ {
+ checksum += (i + 1) * int32(s[i]-'0')
+ }
+
+ if s[9] == 'X' {
+ checksum += 10 * 10
+ } else {
+ checksum += 10 * int32(s[9]-'0')
+ }
+
+ if checksum%11 == 0 {
+ return true
+ }
+
+ return false
+}
+
+func excludesRune(top interface{}, current interface{}, field interface{}, param string) bool {
+ return !containsRune(top, current, field, param)
+}
+
+func excludesAll(top interface{}, current interface{}, field interface{}, param string) bool {
+ return !containsAny(top, current, field, param)
+}
+
+func excludes(top interface{}, current interface{}, field interface{}, param string) bool {
+ return !contains(top, current, field, param)
+}
+
+func containsRune(top interface{}, current interface{}, field interface{}, param string) bool {
+ r, _ := utf8.DecodeRuneInString(param)
+
+ return strings.ContainsRune(field.(string), r)
+}
+
+func containsAny(top interface{}, current interface{}, field interface{}, param string) bool {
+ return strings.ContainsAny(field.(string), param)
+}
+
+func contains(top interface{}, current interface{}, field interface{}, param string) bool {
+ return strings.Contains(field.(string), param)
+}
+
+func isNeField(top interface{}, current interface{}, field interface{}, param string) bool {
+ return !isEqField(top, current, field, param)
+}
+
+func isNe(top interface{}, current interface{}, field interface{}, param string) bool {
+ return !isEq(top, current, field, param)
+}
+
+func isEqField(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if current == nil {
+ panic("struct not passed for cross validation")
+ }
+
+ currentVal := reflect.ValueOf(current)
+
+ if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
+ currentVal = reflect.ValueOf(currentVal.Elem().Interface())
+ }
+
+ var currentFielVal reflect.Value
+
+ switch currentVal.Kind() {
+
+ case reflect.Struct:
+
+ if currentVal.Type() == reflect.TypeOf(time.Time{}) {
+ currentFielVal = currentVal
+ break
+ }
+
+ f := currentVal.FieldByName(param)
+
+ if f.Kind() == reflect.Invalid {
+ panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
+ }
+
+ currentFielVal = f
+
+ default:
+
+ currentFielVal = currentVal
+ }
+
+ if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
+
+ currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
+ }
+
+ fv := reflect.ValueOf(field)
+
+ switch fv.Kind() {
+
+ case reflect.String:
+ return fv.String() == currentFielVal.String()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return fv.Int() == currentFielVal.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return fv.Uint() == currentFielVal.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return fv.Float() == currentFielVal.Float()
+ case reflect.Slice, reflect.Map, reflect.Array:
+
+ return int64(fv.Len()) == int64(currentFielVal.Len())
+ case reflect.Struct:
+
+ if fv.Type() == reflect.TypeOf(time.Time{}) {
+
+ if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
+ panic("Bad Top Level field type")
+ }
+
+ t := currentFielVal.Interface().(time.Time)
+ fieldTime := field.(time.Time)
+
+ return fieldTime.Equal(t)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isEq(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+
+ return st.String() == param
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(st.Len()) == p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asInt(param)
+
+ return st.Int() == p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return st.Uint() == p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return st.Float() == p
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isBase64(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(base64Regex, field)
+}
+
+func isURI(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+ _, err := url.ParseRequestURI(field.(string))
+
+ return err == nil
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isURL(top interface{}, current interface{}, field interface{}, param string) bool {
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+ url, err := url.ParseRequestURI(field.(string))
+
+ if err != nil {
+ return false
+ }
+
+ if len(url.Scheme) == 0 {
+ return false
+ }
+
+ return err == nil
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isEmail(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(emailRegex, field)
+}
+
+func isHsla(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(hslaRegex, field)
+}
+
+func isHsl(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(hslRegex, field)
+}
+
+func isRgba(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(rgbaRegex, field)
+}
+
+func isRgb(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(rgbRegex, field)
+}
+
+func isHexcolor(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(hexcolorRegex, field)
+}
+
+func isHexadecimal(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(hexadecimalRegex, field)
+}
+
+func isNumber(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(numberRegex, field)
+}
+
+func isNumeric(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(numericRegex, field)
+}
+
+func isAlphanum(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(alphaNumericRegex, field)
+}
+
+func isAlpha(top interface{}, current interface{}, field interface{}, param string) bool {
+ return matchesRegex(alphaRegex, field)
+}
+
+func hasValue(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ return field != nil && int64(st.Len()) > 0
+
+ default:
+ return field != nil && field != reflect.Zero(reflect.TypeOf(field)).Interface()
+ }
+}
+
+func isGteField(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if current == nil {
+ panic("struct not passed for cross validation")
+ }
+
+ currentVal := reflect.ValueOf(current)
+
+ if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
+ currentVal = reflect.ValueOf(currentVal.Elem().Interface())
+ }
+
+ var currentFielVal reflect.Value
+
+ switch currentVal.Kind() {
+
+ case reflect.Struct:
+
+ if currentVal.Type() == reflect.TypeOf(time.Time{}) {
+ currentFielVal = currentVal
+ break
+ }
+
+ f := currentVal.FieldByName(param)
+
+ if f.Kind() == reflect.Invalid {
+ panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
+ }
+
+ currentFielVal = f
+
+ default:
+
+ currentFielVal = currentVal
+ }
+
+ if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
+
+ currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
+ }
+
+ fv := reflect.ValueOf(field)
+
+ switch fv.Kind() {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return fv.Int() >= currentFielVal.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return fv.Uint() >= currentFielVal.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return fv.Float() >= currentFielVal.Float()
+
+ case reflect.Struct:
+
+ if fv.Type() == reflect.TypeOf(time.Time{}) {
+
+ if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
+ panic("Bad Top Level field type")
+ }
+
+ t := currentFielVal.Interface().(time.Time)
+ fieldTime := field.(time.Time)
+
+ return fieldTime.After(t) || fieldTime.Equal(t)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isGtField(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if current == nil {
+ panic("struct not passed for cross validation")
+ }
+
+ currentVal := reflect.ValueOf(current)
+
+ if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
+ currentVal = reflect.ValueOf(currentVal.Elem().Interface())
+ }
+
+ var currentFielVal reflect.Value
+
+ switch currentVal.Kind() {
+
+ case reflect.Struct:
+
+ if currentVal.Type() == reflect.TypeOf(time.Time{}) {
+ currentFielVal = currentVal
+ break
+ }
+
+ f := currentVal.FieldByName(param)
+
+ if f.Kind() == reflect.Invalid {
+ panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
+ }
+
+ currentFielVal = f
+
+ default:
+
+ currentFielVal = currentVal
+ }
+
+ if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
+
+ currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
+ }
+
+ fv := reflect.ValueOf(field)
+
+ switch fv.Kind() {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return fv.Int() > currentFielVal.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return fv.Uint() > currentFielVal.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return fv.Float() > currentFielVal.Float()
+
+ case reflect.Struct:
+
+ if fv.Type() == reflect.TypeOf(time.Time{}) {
+
+ if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
+ panic("Bad Top Level field type")
+ }
+
+ t := currentFielVal.Interface().(time.Time)
+ fieldTime := field.(time.Time)
+
+ return fieldTime.After(t)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isGte(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(len(st.String())) >= p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(st.Len()) >= p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asInt(param)
+
+ return st.Int() >= p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return st.Uint() >= p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return st.Float() >= p
+
+ case reflect.Struct:
+
+ if st.Type() == reflect.TypeOf(time.Time{}) {
+
+ now := time.Now().UTC()
+ t := field.(time.Time)
+
+ return t.After(now) || t.Equal(now)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isGt(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(len(st.String())) > p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(st.Len()) > p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asInt(param)
+
+ return st.Int() > p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return st.Uint() > p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return st.Float() > p
+ case reflect.Struct:
+
+ if st.Type() == reflect.TypeOf(time.Time{}) {
+
+ return field.(time.Time).After(time.Now().UTC())
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+// 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 hasLengthOf(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(len(st.String())) == p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(st.Len()) == p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asInt(param)
+
+ return st.Int() == p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return st.Uint() == p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return st.Float() == p
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+// 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 hasMinOf(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ return isGte(top, current, field, param)
+}
+
+func isLteField(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if current == nil {
+ panic("struct not passed for cross validation")
+ }
+
+ currentVal := reflect.ValueOf(current)
+
+ if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
+ currentVal = reflect.ValueOf(currentVal.Elem().Interface())
+ }
+
+ var currentFielVal reflect.Value
+
+ switch currentVal.Kind() {
+
+ case reflect.Struct:
+
+ if currentVal.Type() == reflect.TypeOf(time.Time{}) {
+ currentFielVal = currentVal
+ break
+ }
+
+ f := currentVal.FieldByName(param)
+
+ if f.Kind() == reflect.Invalid {
+ panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
+ }
+
+ currentFielVal = f
+
+ default:
+
+ currentFielVal = currentVal
+ }
+
+ if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
+
+ currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
+ }
+
+ fv := reflect.ValueOf(field)
+
+ switch fv.Kind() {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return fv.Int() <= currentFielVal.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return fv.Uint() <= currentFielVal.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return fv.Float() <= currentFielVal.Float()
+
+ case reflect.Struct:
+
+ if fv.Type() == reflect.TypeOf(time.Time{}) {
+
+ if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
+ panic("Bad Top Level field type")
+ }
+
+ t := currentFielVal.Interface().(time.Time)
+ fieldTime := field.(time.Time)
+
+ return fieldTime.Before(t) || fieldTime.Equal(t)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isLtField(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if current == nil {
+ panic("struct not passed for cross validation")
+ }
+
+ currentVal := reflect.ValueOf(current)
+
+ if currentVal.Kind() == reflect.Ptr && !currentVal.IsNil() {
+ currentVal = reflect.ValueOf(currentVal.Elem().Interface())
+ }
+
+ var currentFielVal reflect.Value
+
+ switch currentVal.Kind() {
+
+ case reflect.Struct:
+
+ if currentVal.Type() == reflect.TypeOf(time.Time{}) {
+ currentFielVal = currentVal
+ break
+ }
+
+ f := currentVal.FieldByName(param)
+
+ if f.Kind() == reflect.Invalid {
+ panic(fmt.Sprintf("Field \"%s\" not found in struct", param))
+ }
+
+ currentFielVal = f
+
+ default:
+
+ currentFielVal = currentVal
+ }
+
+ if currentFielVal.Kind() == reflect.Ptr && !currentFielVal.IsNil() {
+
+ currentFielVal = reflect.ValueOf(currentFielVal.Elem().Interface())
+ }
+
+ fv := reflect.ValueOf(field)
+
+ switch fv.Kind() {
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+
+ return fv.Int() < currentFielVal.Int()
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+
+ return fv.Uint() < currentFielVal.Uint()
+
+ case reflect.Float32, reflect.Float64:
+
+ return fv.Float() < currentFielVal.Float()
+
+ case reflect.Struct:
+
+ if fv.Type() == reflect.TypeOf(time.Time{}) {
+
+ if currentFielVal.Type() != reflect.TypeOf(time.Time{}) {
+ panic("Bad Top Level field type")
+ }
+
+ t := currentFielVal.Interface().(time.Time)
+ fieldTime := field.(time.Time)
+
+ return fieldTime.Before(t)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isLte(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(len(st.String())) <= p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(st.Len()) <= p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asInt(param)
+
+ return st.Int() <= p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return st.Uint() <= p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return st.Float() <= p
+
+ case reflect.Struct:
+
+ if st.Type() == reflect.TypeOf(time.Time{}) {
+
+ now := time.Now().UTC()
+ t := field.(time.Time)
+
+ return t.Before(now) || t.Equal(now)
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+func isLt(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ st := reflect.ValueOf(field)
+
+ switch st.Kind() {
+
+ case reflect.String:
+ p := asInt(param)
+
+ return int64(len(st.String())) < p
+
+ case reflect.Slice, reflect.Map, reflect.Array:
+ p := asInt(param)
+
+ return int64(st.Len()) < p
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ p := asInt(param)
+
+ return st.Int() < p
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ p := asUint(param)
+
+ return st.Uint() < p
+
+ case reflect.Float32, reflect.Float64:
+ p := asFloat(param)
+
+ return st.Float() < p
+
+ case reflect.Struct:
+
+ if st.Type() == reflect.TypeOf(time.Time{}) {
+
+ return field.(time.Time).Before(time.Now().UTC())
+ }
+ }
+
+ panic(fmt.Sprintf("Bad field type %T", field))
+}
+
+// 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 hasMaxOf(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ return isLte(top, current, field, param)
+}
+
+// asInt retuns the parameter as a int64
+// or panics if it can't convert
+func asInt(param string) int64 {
+
+ i, err := strconv.ParseInt(param, 0, 64)
+ panicIf(err)
+
+ return i
+}
+
+// asUint returns the parameter as a uint64
+// or panics if it can't convert
+func asUint(param string) uint64 {
+
+ i, err := strconv.ParseUint(param, 0, 64)
+ panicIf(err)
+
+ return i
+}
+
+// asFloat returns the parameter as a float64
+// or panics if it can't convert
+func asFloat(param string) float64 {
+
+ i, err := strconv.ParseFloat(param, 64)
+ panicIf(err)
+
+ return i
+}
+
+func panicIf(err error) {
+ if err != nil {
+ panic(err.Error())
+ }
+}
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/doc.go b/vendor/gopkg.in/bluesuncorp/validator.v5/doc.go
new file mode 100644
index 0000000..06c940b
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/doc.go
@@ -0,0 +1,474 @@
+/*
+Package validator implements value validations for structs and individual fields based on tags. It can also handle Cross Field and Cross Struct validation for nested structs.
+
+Validate
+
+ validate := validator.New("validate", validator.BakedInValidators)
+
+ errs := validate.Struct(//your struct)
+ valErr := validate.Field(field, "omitempty,min=1,max=10")
+
+A simple example usage:
+
+ type UserDetail struct {
+ Details string `validate:"-"`
+ }
+
+ type User struct {
+ Name string `validate:"required,max=60"`
+ PreferedName string `validate:"omitempty,max=60"`
+ Sub UserDetail
+ }
+
+ user := &User {
+ Name: "",
+ }
+
+ // errs will contain a hierarchical list of errors
+ // using the StructErrors struct
+ // or nil if no errors exist
+ errs := validate.Struct(user)
+
+ // in this case 1 error Name is required
+ errs.Struct will be "User"
+ errs.StructErrors will be empty <-- fields that were structs
+ errs.Errors will have 1 error of type FieldError
+
+ NOTE: Anonymous Structs - they don't have names so expect the Struct name
+ within StructErrors to be blank.
+
+Error Handling
+
+The error can be used like so
+
+ fieldErr, _ := errs["Name"]
+ fieldErr.Field // "Name"
+ fieldErr.ErrorTag // "required"
+
+Both StructErrors and FieldError implement the Error interface but it's
+intended use is for development + debugging, not a production error message.
+
+ fieldErr.Error() // Field validation for "Name" failed on the "required" tag
+ errs.Error()
+ // Struct: User
+ // Field validation for "Name" failed on the "required" tag
+
+Why not a better error message? because this library intends for you to handle your own error messages
+
+Why should I handle my own errors? Many reasons, for us building an internationalized application
+I needed to know the field and what validation failed so that I could provide an error in the users specific language.
+
+ if fieldErr.Field == "Name" {
+ switch fieldErr.ErrorTag
+ case "required":
+ return "Translated string based on field + error"
+ default:
+ return "Translated string based on field"
+ }
+
+The hierarchical error structure is hard to work with sometimes.. Agreed Flatten function to the rescue!
+Flatten will return a map of FieldError's but the field name will be namespaced.
+
+ // if UserDetail Details field failed validation
+ Field will be "Sub.Details"
+
+ // for Name
+ Field will be "Name"
+
+Custom Functions
+
+Custom functions can be added
+
+ //Structure
+ func customFunc(top interface{}, current interface{}, field interface{}, param string) bool {
+
+ if whatever {
+ return false
+ }
+
+ return true
+ }
+
+ validate.AddFunction("custom tag name", customFunc)
+ // NOTES: using the same tag name as an existing function
+ // will overwrite the existing one
+
+Cross Field Validation
+
+Cross Field Validation can be implemented, for example Start & End Date range validation
+
+ // NOTE: when calling validate.Struct(val) val will be the top level struct passed
+ // into the function
+ // when calling validate.FieldWithValue(val, field, tag) val will be
+ // whatever you pass, struct, field...
+ // when calling validate.Field(field, tag) val will be nil
+ //
+ // Because of the specific requirements and field names within each persons project that
+ // uses this library it is likely that custom functions will need to be created for your
+ // Cross Field Validation needs, however there are some build in Generic Cross Field validations,
+ // see Baked In Validators and Tags below
+
+ func isDateRangeValid(val interface{}, field interface{}, param string) bool {
+
+ myStruct := val.(myStructType)
+
+ if myStruct.Start.After(field.(time.Time)) {
+ return false
+ }
+
+ return true
+ }
+
+Multiple Validators
+
+Multiple validators on a field will process in the order defined
+
+ type Test struct {
+ Field `validate:"max=10,min=1"`
+ }
+
+ // max will be checked then min
+
+Bad Validator definitions are not handled by the library
+
+ type Test struct {
+ Field `validate:"min=10,max=0"`
+ }
+
+ // this definition of min max will never validate
+
+Baked In Validators and Tags
+
+NOTE: Baked In Cross field validation only compares fields on the same struct,
+if cross field + cross struct validation is needed your own custom validator
+should be implemented.
+
+NOTE2: comma is the default separator of validation tags, if you wish to have a comma
+included within the parameter i.e. excludesall=, you will need to use the UTF-8 hex
+representation 0x2C, which is replaced in the code as a comma, so the above will
+become excludesall=0x2C
+
+Here is a list of the current built in validators:
+
+ -
+ Tells the validation to skip this struct field; this is particularily
+ handy in ignoring embedded structs from being validated. (Usage: -)
+
+ |
+ This is the 'or' operator allowing multiple validators to be used and
+ accepted. (Usage: rbg|rgba) <-- this would allow either rgb or rgba
+ colors to be accepted. This can also be combined with 'and' for example
+ ( Usage: omitempty,rgb|rgba)
+
+ structonly
+ When a field that is a nest struct in encountered and contains this flag
+ any validation on the nested struct such as "required" will be run, but
+ none of the nested struct fields will be validated. This is usefull if
+ inside of you program you know the struct will be valid, but need to
+ verify it has been assigned.
+
+ omitempty
+ Allows conditional validation, for example if a field is not set with
+ a value (Determined by the required validator) then other validation
+ such as min or max won't run, but if a value is set validation will run.
+ (Usage: omitempty)
+
+ dive
+ This tells the validator to dive into a slice, array or map and validate that
+ level of the slice, array or map with the validation tags that follow.
+ Multidimensional nesting is also supported, each level you with to dive will
+ require another dive tag. (Usage: dive)
+ Example: [][]string with validation tag "gt=0,dive,len=1,dive,required"
+ gt=0 will be applied to []
+ len=1 will be applied to []string
+ required will be applied to string
+ Example2: [][]string with validation tag "gt=0,dive,dive,required"
+ gt=0 will be applied to []
+ []string will be spared validation
+ required will be applied to string
+ NOTE: in Example2 if the required validation failed, but all others passed
+ the hierarchy of FieldError's in the middle with have their IsPlaceHolder field
+ set to true. If a FieldError has IsSliceOrMap=true or IsMap=true then the
+ FieldError is a Slice or Map field and if IsPlaceHolder=true then contains errors
+ within its SliceOrArrayErrs or MapErrs fields.
+
+ required
+ This validates that the value is not the data types default value.
+ For numbers ensures value is not zero. For strings ensures value is
+ not "". For slices, arrays, and maps, ensures the length is not zero.
+ (Usage: required)
+
+ len
+ For numbers, max will ensure that the value is
+ equal to the parameter given. For strings, it checks that
+ the string length is exactly that number of characters. For slices,
+ arrays, and maps, validates the number of items. (Usage: len=10)
+
+ max
+ For numbers, max will ensure that the value is
+ less than or equal to the parameter given. For strings, it checks
+ that the string length is at most that number of characters. For
+ slices, arrays, and maps, validates the number of items. (Usage: max=10)
+
+ min
+ For numbers, min will ensure that the value is
+ greater or equal to the parameter given. For strings, it checks that
+ the string length is at least that number of characters. For slices,
+ arrays, and maps, validates the number of items. (Usage: min=10)
+
+ eq
+ For strings & numbers, eq will ensure that the value is
+ equal to the parameter given. For slices, arrays, and maps,
+ validates the number of items. (Usage: eq=10)
+
+ ne
+ For strings & numbers, eq will ensure that the value is not
+ equal to the parameter given. For slices, arrays, and maps,
+ validates the number of items. (Usage: eq=10)
+
+ gt
+ For numbers, this will ensure that the value is greater than the
+ parameter given. For strings, it checks that the string length
+ is greater than that number of characters. For slices, arrays
+ and maps it validates the number of items. (Usage: gt=10)
+ For time.Time ensures the time value is greater than time.Now.UTC()
+ (Usage: gt)
+
+ gte
+ Same as 'min' above. Kept both to make terminology with 'len' easier
+ (Usage: gte=10)
+ For time.Time ensures the time value is greater than or equal to time.Now.UTC()
+ (Usage: gte)
+
+ lt
+ For numbers, this will ensure that the value is
+ less than the parameter given. For strings, it checks
+ that the string length is less than that number of characters.
+ For slices, arrays, and maps it validates the number of items.
+ (Usage: lt=10)
+ For time.Time ensures the time value is less than time.Now.UTC()
+ (Usage: lt)
+
+ lte
+ Same as 'max' above. Kept both to make terminology with 'len' easier
+ (Usage: lte=10)
+ For time.Time ensures the time value is less than or equal to time.Now.UTC()
+ (Usage: lte)
+
+ eqfield
+ This will validate the field value against another fields value either within
+ a struct or passed in field.
+ usage examples are for validation of a password and confirm password:
+ Validation on Password field using validate.Struct Usage(eqfield=ConfirmPassword)
+ Validating by field validate.FieldWithValue(password, confirmpassword, "eqfield")
+
+ nefield
+ This will validate the field value against another fields value either within
+ a struct or passed in field.
+ usage examples are for ensuring two colors are not the same:
+ Validation on Color field using validate.Struct Usage(nefield=Color2)
+ Validating by field validate.FieldWithValue(color1, color2, "nefield")
+
+ gtfield
+ Only valid for Numbers and time.Time types, this will validate the field value
+ against another fields value either within a struct or passed in field.
+ usage examples are for validation of a Start and End date:
+ Validation on End field using validate.Struct Usage(gtfield=Start)
+ Validating by field validate.FieldWithValue(start, end, "gtfield")
+
+ gtefield
+ Only valid for Numbers and time.Time types, this will validate the field value
+ against another fields value either within a struct or passed in field.
+ usage examples are for validation of a Start and End date:
+ Validation on End field using validate.Struct Usage(gtefield=Start)
+ Validating by field validate.FieldWithValue(start, end, "gtefield")
+
+ ltfield
+ Only valid for Numbers and time.Time types, this will validate the field value
+ against another fields value either within a struct or passed in field.
+ usage examples are for validation of a Start and End date:
+ Validation on End field using validate.Struct Usage(ltfield=Start)
+ Validating by field validate.FieldWithValue(start, end, "ltfield")
+
+ ltefield
+ Only valid for Numbers and time.Time types, this will validate the field value
+ against another fields value either within a struct or passed in field.
+ usage examples are for validation of a Start and End date:
+ Validation on End field using validate.Struct Usage(ltefield=Start)
+ Validating by field validate.FieldWithValue(start, end, "ltefield")
+
+ alpha
+ This validates that a string value contains alpha characters only
+ (Usage: alpha)
+
+ alphanum
+ This validates that a string value contains alphanumeric characters only
+ (Usage: alphanum)
+
+ numeric
+ This validates that a string value contains a basic numeric value.
+ basic excludes exponents etc...
+ (Usage: numeric)
+
+ hexadecimal
+ This validates that a string value contains a valid hexadecimal.
+ (Usage: hexadecimal)
+
+ hexcolor
+ This validates that a string value contains a valid hex color including
+ hashtag (#)
+ (Usage: hexcolor)
+
+ rgb
+ This validates that a string value contains a valid rgb color
+ (Usage: rgb)
+
+ rgba
+ This validates that a string value contains a valid rgba color
+ (Usage: rgba)
+
+ hsl
+ This validates that a string value contains a valid hsl color
+ (Usage: hsl)
+
+ hsla
+ This validates that a string value contains a valid hsla color
+ (Usage: hsla)
+
+ email
+ This validates that a string value contains a valid email
+ This may not conform to all possibilities of any rfc standard, but neither
+ does any email provider accept all posibilities...
+ (Usage: email)
+
+ url
+ This validates that a string value contains a valid url
+ This will accept any url the golang request uri accepts but must contain
+ a schema for example http:// or rtmp://
+ (Usage: url)
+
+ uri
+ This validates that a string value contains a valid uri
+ This will accept any uri the golang request uri accepts (Usage: uri)
+
+ base64
+ This validates that a string value contains a valid base64 value.
+ Although an empty string is valid base64 this will report an empty string
+ as an error, if you wish to accept an empty string as valid you can use
+ this with the omitempty tag. (Usage: base64)
+
+ contains
+ This validates that a string value contains the substring value.
+ (Usage: contains=@)
+
+ containsany
+ This validates that a string value contains any Unicode code points
+ in the substring value. (Usage: containsany=!@#?)
+
+ containsrune
+ This validates that a string value contains the supplied rune value.
+ (Usage: containsrune=@)
+
+ excludes
+ This validates that a string value does not contain the substring value.
+ (Usage: excludes=@)
+
+ excludesall
+ This validates that a string value does not contain any Unicode code
+ points in the substring value. (Usage: excludesall=!@#?)
+
+ excludesrune
+ This validates that a string value does not contain the supplied rune value.
+ (Usage: excludesrune=@)
+
+ isbn
+ This validates that a string value contains a valid isbn10 or isbn13 value.
+ (Usage: isbn)
+
+ isbn10
+ This validates that a string value contains a valid isbn10 value.
+ (Usage: isbn10)
+
+ isbn13
+ This validates that a string value contains a valid isbn13 value.
+ (Usage: isbn13)
+
+ uuid
+ This validates that a string value contains a valid UUID.
+ (Usage: uuid)
+
+ uuid3
+ This validates that a string value contains a valid version 3 UUID.
+ (Usage: uuid3)
+
+ uuid4
+ This validates that a string value contains a valid version 4 UUID.
+ (Usage: uuid4)
+
+ uuid5
+ This validates that a string value contains a valid version 5 UUID.
+ (Usage: uuid5)
+
+ ascii
+ This validates that a string value contains only ASCII characters.
+ NOTE: if the string is blank, this validates as true.
+ (Usage: ascii)
+
+ asciiprint
+ This validates that a string value contains only printable ASCII characters.
+ NOTE: if the string is blank, this validates as true.
+ (Usage: asciiprint)
+
+ multibyte
+ This validates that a string value contains one or more multibyte characters.
+ NOTE: if the string is blank, this validates as true.
+ (Usage: multibyte)
+
+ datauri
+ This validates that a string value contains a valid DataURI.
+ NOTE: this will also validate that the data portion is valid base64
+ (Usage: datauri)
+
+ latitude
+ This validates that a string value contains a valid latitude.
+ (Usage: latitude)
+
+ longitude
+ This validates that a string value contains a valid longitude.
+ (Usage: longitude)
+
+ ssn
+ This validates that a string value contains a valid U.S. Social Security Number.
+ (Usage: ssn)
+
+Validator notes:
+
+ regex
+ a regex validator won't be added because commas and = signs can be part of
+ a regex which conflict with the validation definitions, although workarounds
+ can be made, they take away from using pure regex's. Furthermore it's quick
+ and dirty but the regex's become harder to maintain and are not reusable, so
+ it's as much a programming philosiphy as anything.
+
+ In place of this new validator functions should be created; a regex can be
+ used within the validator function and even be precompiled for better efficiency
+ within regexes.go.
+
+ And the best reason, you can submit a pull request and we can keep on adding to the
+ validation library of this package!
+
+Panics
+
+This package panics when bad input is provided, this is by design, bad code like that should not make it to production.
+
+ type Test struct {
+ TestField string `validate:"nonexistantfunction=1"`
+ }
+
+ t := &Test{
+ TestField: "Test"
+ }
+
+ validate.Struct(t) // this will panic
+*/
+package validator
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/regexes.go b/vendor/gopkg.in/bluesuncorp/validator.v5/regexes.go
new file mode 100644
index 0000000..d3e8d80
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/regexes.go
@@ -0,0 +1,64 @@
+package validator
+
+import "regexp"
+
+const (
+ alphaRegexString = "^[a-zA-Z]+$"
+ alphaNumericRegexString = "^[a-zA-Z0-9]+$"
+ numericRegexString = "^[-+]?[0-9]+(?:\\.[0-9]+)?$"
+ numberRegexString = "^[0-9]+$"
+ hexadecimalRegexString = "^[0-9a-fA-F]+$"
+ hexcolorRegexString = "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
+ rgbRegexString = "^rgb\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*\\)$"
+ rgbaRegexString = "^rgba\\(\\s*(?:(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])|(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%\\s*,\\s*(?:0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
+ hslRegexString = "^hsl\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*\\)$"
+ hslaRegexString = "^hsla\\(\\s*(?:0|[1-9]\\d?|[12]\\d\\d|3[0-5]\\d|360)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0|[1-9]\\d?|100)%)\\s*,\\s*(?:(?:0.[1-9]*)|[01])\\s*\\)$"
+ emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:\\(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22)))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
+ base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
+ iSBN10RegexString = "^(?:[0-9]{9}X|[0-9]{10})$"
+ iSBN13RegexString = "^(?:(?:97(?:8|9))[0-9]{10})$"
+ uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
+ uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
+ uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
+ uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
+ aSCIIRegexString = "^[\x00-\x7F]*$"
+ printableASCIIRegexString = "^[\x20-\x7E]*$"
+ multibyteRegexString = "[^\x00-\x7F]"
+ dataURIRegexString = "^data:.+\\/(.+);base64$"
+ latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
+ longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
+ sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
+)
+
+var (
+ alphaRegex = regexp.MustCompile(alphaRegexString)
+ alphaNumericRegex = regexp.MustCompile(alphaNumericRegexString)
+ numericRegex = regexp.MustCompile(numericRegexString)
+ numberRegex = regexp.MustCompile(numberRegexString)
+ hexadecimalRegex = regexp.MustCompile(hexadecimalRegexString)
+ hexcolorRegex = regexp.MustCompile(hexcolorRegexString)
+ rgbRegex = regexp.MustCompile(rgbRegexString)
+ rgbaRegex = regexp.MustCompile(rgbaRegexString)
+ hslRegex = regexp.MustCompile(hslRegexString)
+ hslaRegex = regexp.MustCompile(hslaRegexString)
+ emailRegex = regexp.MustCompile(emailRegexString)
+ base64Regex = regexp.MustCompile(base64RegexString)
+ iSBN10Regex = regexp.MustCompile(iSBN10RegexString)
+ iSBN13Regex = regexp.MustCompile(iSBN13RegexString)
+ uUID3Regex = regexp.MustCompile(uUID3RegexString)
+ uUID4Regex = regexp.MustCompile(uUID4RegexString)
+ uUID5Regex = regexp.MustCompile(uUID5RegexString)
+ uUIDRegex = regexp.MustCompile(uUIDRegexString)
+ aSCIIRegex = regexp.MustCompile(aSCIIRegexString)
+ printableASCIIRegex = regexp.MustCompile(printableASCIIRegexString)
+ multibyteRegex = regexp.MustCompile(multibyteRegexString)
+ dataURIRegex = regexp.MustCompile(dataURIRegexString)
+ latitudeRegex = regexp.MustCompile(latitudeRegexString)
+ longitudeRegex = regexp.MustCompile(longitudeRegexString)
+ sSNRegex = regexp.MustCompile(sSNRegexString)
+)
+
+func matchesRegex(regex *regexp.Regexp, field interface{}) bool {
+ fieldAsString := field.(string) //this will panic inherently
+ return regex.MatchString(fieldAsString)
+}
diff --git a/vendor/gopkg.in/bluesuncorp/validator.v5/validator.go b/vendor/gopkg.in/bluesuncorp/validator.v5/validator.go
new file mode 100644
index 0000000..db01f1f
--- /dev/null
+++ b/vendor/gopkg.in/bluesuncorp/validator.v5/validator.go
@@ -0,0 +1,935 @@
+/**
+ * Package validator
+ *
+ * MISC:
+ * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank
+ *
+ */
+
+package validator
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+ "sync"
+ "time"
+ "unicode"
+)
+
+const (
+ utf8HexComma = "0x2C"
+ tagSeparator = ","
+ orSeparator = "|"
+ noValidationTag = "-"
+ tagKeySeparator = "="
+ structOnlyTag = "structonly"
+ omitempty = "omitempty"
+ required = "required"
+ fieldErrMsg = "Field validation for \"%s\" failed on the \"%s\" tag"
+ sliceErrMsg = "Field validation for \"%s\" failed at index \"%d\" with error(s): %s"
+ mapErrMsg = "Field validation for \"%s\" failed on key \"%v\" with error(s): %s"
+ structErrMsg = "Struct:%s\n"
+ diveTag = "dive"
+ // diveSplit = "," + diveTag
+ arrayIndexFieldName = "%s[%d]"
+ mapIndexFieldName = "%s[%v]"
+)
+
+var structPool *pool
+
+// Pool holds a channelStructErrors.
+type pool struct {
+ pool chan *StructErrors
+}
+
+// NewPool creates a new pool of Clients.
+func newPool(max int) *pool {
+ return &pool{
+ pool: make(chan *StructErrors, max),
+ }
+}
+
+// Borrow a StructErrors from the pool.
+func (p *pool) Borrow() *StructErrors {
+ var c *StructErrors
+
+ select {
+ case c = <-p.pool:
+ default:
+ c = &StructErrors{
+ Errors: map[string]*FieldError{},
+ StructErrors: map[string]*StructErrors{},
+ }
+ }
+
+ return c
+}
+
+// Return returns a StructErrors to the pool.
+func (p *pool) Return(c *StructErrors) {
+
+ select {
+ case p.pool <- c:
+ default:
+ // let it go, let it go...
+ }
+}
+
+type cachedTags struct {
+ keyVals [][]string
+ isOrVal bool
+}
+
+type cachedField struct {
+ index int
+ name string
+ tags []*cachedTags
+ tag string
+ kind reflect.Kind
+ typ reflect.Type
+ isTime bool
+ isSliceOrArray bool
+ isMap bool
+ isTimeSubtype bool
+ sliceSubtype reflect.Type
+ mapSubtype reflect.Type
+ sliceSubKind reflect.Kind
+ mapSubKind reflect.Kind
+ dive bool
+ diveTag string
+}
+
+type cachedStruct struct {
+ children int
+ name string
+ kind reflect.Kind
+ fields []*cachedField
+}
+
+type structsCacheMap struct {
+ lock sync.RWMutex
+ m map[reflect.Type]*cachedStruct
+}
+
+func (s *structsCacheMap) Get(key reflect.Type) (*cachedStruct, bool) {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ value, ok := s.m[key]
+ return value, ok
+}
+
+func (s *structsCacheMap) Set(key reflect.Type, value *cachedStruct) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.m[key] = value
+}
+
+var structCache = &structsCacheMap{m: map[reflect.Type]*cachedStruct{}}
+
+type fieldsCacheMap struct {
+ lock sync.RWMutex
+ m map[string][]*cachedTags
+}
+
+func (s *fieldsCacheMap) Get(key string) ([]*cachedTags, bool) {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ value, ok := s.m[key]
+ return value, ok
+}
+
+func (s *fieldsCacheMap) Set(key string, value []*cachedTags) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.m[key] = value
+}
+
+var fieldsCache = &fieldsCacheMap{m: map[string][]*cachedTags{}}
+
+// FieldError contains a single field's validation error along
+// with other properties that may be needed for error message creation
+type FieldError struct {
+ Field string
+ Tag string
+ Kind reflect.Kind
+ Type reflect.Type
+ Param string
+ Value interface{}
+ IsPlaceholderErr bool
+ IsSliceOrArray bool
+ IsMap bool
+ SliceOrArrayErrs map[int]error // counld be FieldError, StructErrors
+ MapErrs map[interface{}]error // counld be FieldError, StructErrors
+}
+
+// This is intended for use in development + debugging and not intended to be a production error message.
+// it also allows FieldError to be used as an Error interface
+func (e *FieldError) Error() string {
+
+ if e.IsPlaceholderErr {
+
+ buff := bytes.NewBufferString("")
+
+ if e.IsSliceOrArray {
+
+ for j, err := range e.SliceOrArrayErrs {
+ buff.WriteString("\n")
+ buff.WriteString(fmt.Sprintf(sliceErrMsg, e.Field, j, "\n"+err.Error()))
+ }
+
+ } else if e.IsMap {
+
+ for key, err := range e.MapErrs {
+ buff.WriteString(fmt.Sprintf(mapErrMsg, e.Field, key, "\n"+err.Error()))
+ }
+ }
+
+ return strings.TrimSpace(buff.String())
+ }
+
+ return fmt.Sprintf(fieldErrMsg, e.Field, e.Tag)
+}
+
+// StructErrors is hierarchical list of field and struct validation errors
+// for a non hierarchical representation please see the Flatten method for StructErrors
+type StructErrors struct {
+ // Name of the Struct
+ Struct string
+ // Struct Field Errors
+ Errors map[string]*FieldError
+ // Struct Fields of type struct and their errors
+ // key = Field Name of current struct, but internally Struct will be the actual struct name unless anonymous struct, it will be blank
+ StructErrors map[string]*StructErrors
+}
+
+// This is intended for use in development + debugging and not intended to be a production error message.
+// it also allows StructErrors to be used as an Error interface
+func (e *StructErrors) Error() string {
+ buff := bytes.NewBufferString(fmt.Sprintf(structErrMsg, e.Struct))
+
+ for _, err := range e.Errors {
+ buff.WriteString(err.Error())
+ buff.WriteString("\n")
+ }
+
+ for _, err := range e.StructErrors {
+ buff.WriteString(err.Error())
+ }
+
+ return strings.TrimSpace(buff.String())
+}
+
+// Flatten flattens the StructErrors hierarchical structure into a flat namespace style field name
+// for those that want/need it
+func (e *StructErrors) Flatten() map[string]*FieldError {
+
+ if e == nil {
+ return nil
+ }
+
+ errs := map[string]*FieldError{}
+
+ for _, f := range e.Errors {
+
+ errs[f.Field] = f
+ }
+
+ for key, val := range e.StructErrors {
+
+ otherErrs := val.Flatten()
+
+ for _, f2 := range otherErrs {
+
+ f2.Field = fmt.Sprintf("%s.%s", key, f2.Field)
+ errs[f2.Field] = f2
+ }
+ }
+
+ return errs
+}
+
+// Func accepts all values needed for file and cross field validation
+// top = top level struct when validating by struct otherwise nil
+// current = current level struct when validating by struct otherwise optional comparison value
+// f = field value for validation
+// param = parameter used in validation i.e. gt=0 param would be 0
+type Func func(top interface{}, current interface{}, f interface{}, param string) bool
+
+// Validate implements the Validate Struct
+// NOTE: Fields within are not thread safe and that is on purpose
+// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
+// or make it thread safe on your end
+type Validate struct {
+ // tagName being used.
+ tagName string
+ // validateFuncs is a map of validation functions and the tag keys
+ validationFuncs map[string]Func
+}
+
+// New creates a new Validate instance for use.
+func New(tagName string, funcs map[string]Func) *Validate {
+
+ structPool = newPool(10)
+
+ return &Validate{
+ tagName: tagName,
+ validationFuncs: funcs,
+ }
+}
+
+// SetTag sets tagName of the Validator to one of your choosing after creation
+// perhaps to dodge a tag name conflict in a specific section of code
+// NOTE: this method is not thread-safe
+func (v *Validate) SetTag(tagName string) {
+ v.tagName = tagName
+}
+
+// SetMaxStructPoolSize sets the struct pools max size. this may be usefull for fine grained
+// performance tuning towards your application, however, the default should be fine for
+// nearly all cases. only increase if you have a deeply nested struct structure.
+// NOTE: this method is not thread-safe
+// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed
+// and the max pool size will be passed into the New function
+func (v *Validate) SetMaxStructPoolSize(max int) {
+ structPool = newPool(max)
+}
+
+// AddFunction adds a validation Func to a Validate's map of validators denoted by the key
+// NOTE: if the key already exists, it will get replaced.
+// NOTE: this method is not thread-safe
+func (v *Validate) AddFunction(key string, f Func) error {
+
+ if len(key) == 0 {
+ return errors.New("Function Key cannot be empty")
+ }
+
+ if f == nil {
+ return errors.New("Function cannot be empty")
+ }
+
+ v.validationFuncs[key] = f
+
+ return nil
+}
+
+// Struct validates a struct, even it's nested structs, and returns a struct containing the errors
+// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
+// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
+// the Array or Map.
+func (v *Validate) Struct(s interface{}) *StructErrors {
+
+ return v.structRecursive(s, s, s)
+}
+
+// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors
+func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors {
+
+ structValue := reflect.ValueOf(s)
+
+ if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
+ return v.structRecursive(top, current, structValue.Elem().Interface())
+ }
+
+ if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
+ panic("interface passed for validation is not a struct")
+ }
+
+ structType := reflect.TypeOf(s)
+
+ var structName string
+ var numFields int
+ var cs *cachedStruct
+ var isCached bool
+
+ cs, isCached = structCache.Get(structType)
+
+ if isCached {
+ structName = cs.name
+ numFields = cs.children
+ } else {
+ structName = structType.Name()
+ numFields = structValue.NumField()
+ cs = &cachedStruct{name: structName, children: numFields}
+ }
+
+ validationErrors := structPool.Borrow()
+ validationErrors.Struct = structName
+
+ for i := 0; i < numFields; i++ {
+
+ var valueField reflect.Value
+ var cField *cachedField
+ var typeField reflect.StructField
+
+ if isCached {
+ cField = cs.fields[i]
+ valueField = structValue.Field(cField.index)
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ }
+ } else {
+ valueField = structValue.Field(i)
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ }
+
+ typeField = structType.Field(i)
+
+ cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName), isTime: (valueField.Type() == reflect.TypeOf(time.Time{}) || valueField.Type() == reflect.TypeOf(&time.Time{}))}
+
+ if cField.tag == noValidationTag {
+ cs.children--
+ continue
+ }
+
+ // if no validation and not a struct (which may containt fields for validation)
+ if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) {
+ cs.children--
+ continue
+ }
+
+ cField.name = typeField.Name
+ cField.kind = valueField.Kind()
+ cField.typ = valueField.Type()
+ }
+
+ // this can happen if the first cache value was nil
+ // but the second actually has a value
+ if cField.kind == reflect.Ptr {
+ cField.kind = valueField.Kind()
+ }
+
+ switch cField.kind {
+
+ case reflect.Struct, reflect.Interface:
+
+ if !unicode.IsUpper(rune(cField.name[0])) {
+ cs.children--
+ continue
+ }
+
+ if cField.isTime {
+
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ } else {
+
+ if strings.Contains(cField.tag, structOnlyTag) {
+ cs.children--
+ continue
+ }
+
+ if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {
+
+ if strings.Contains(cField.tag, omitempty) {
+ goto CACHEFIELD
+ }
+
+ tags := strings.Split(cField.tag, tagSeparator)
+
+ if len(tags) > 0 {
+
+ var param string
+ vals := strings.SplitN(tags[0], tagKeySeparator, 2)
+
+ if len(vals) > 1 {
+ param = vals[1]
+ }
+
+ validationErrors.Errors[cField.name] = &FieldError{
+ Field: cField.name,
+ Tag: vals[0],
+ Param: param,
+ Value: valueField.Interface(),
+ Kind: valueField.Kind(),
+ Type: valueField.Type(),
+ }
+
+ goto CACHEFIELD
+ }
+ }
+
+ // if we get here, the field is interface and could be a struct or a field
+ // and we need to check the inner type and validate
+ if cField.kind == reflect.Interface {
+
+ valueField = valueField.Elem()
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ }
+
+ if valueField.Kind() == reflect.Struct {
+ goto VALIDATESTRUCT
+ }
+
+ // sending nil for cField as it was type interface and could be anything
+ // each time and so must be calculated each time and can't be cached reliably
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ goto CACHEFIELD
+ }
+
+ VALIDATESTRUCT:
+ if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
+ validationErrors.StructErrors[cField.name] = structErrors
+ // free up memory map no longer needed
+ structErrors = nil
+ }
+ }
+
+ case reflect.Slice, reflect.Array:
+ cField.isSliceOrArray = true
+ cField.sliceSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{}))
+ cField.sliceSubKind = cField.sliceSubtype.Kind()
+
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ case reflect.Map:
+ cField.isMap = true
+ cField.mapSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{}))
+ cField.mapSubKind = cField.mapSubtype.Kind()
+
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+
+ default:
+ if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
+ validationErrors.Errors[fieldError.Field] = fieldError
+ // free up memory reference
+ fieldError = nil
+ }
+ }
+
+ CACHEFIELD:
+ if !isCached {
+ cs.fields = append(cs.fields, cField)
+ }
+ }
+
+ structCache.Set(structType, cs)
+
+ if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
+ structPool.Return(validationErrors)
+ return nil
+ }
+
+ return validationErrors
+}
+
+// Field allows validation of a single field, still using tag style validation to check multiple errors
+func (v *Validate) Field(f interface{}, tag string) *FieldError {
+ return v.FieldWithValue(nil, f, tag)
+}
+
+// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
+func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError {
+ return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil)
+}
+
+func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError {
+
+ var cField *cachedField
+ var isCached bool
+ var valueField reflect.Value
+
+ // This is a double check if coming from validate.Struct but need to be here in case function is called directly
+ if tag == noValidationTag {
+ return nil
+ }
+
+ if strings.Contains(tag, omitempty) && !hasValue(val, current, f, "") {
+ return nil
+ }
+
+ valueField = reflect.ValueOf(f)
+
+ if cacheField == nil {
+
+ if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
+ valueField = valueField.Elem()
+ f = valueField.Interface()
+ }
+
+ cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()}
+
+ switch cField.kind {
+ case reflect.Slice, reflect.Array:
+ isSingleField = false // cached tags mean nothing because it will be split up while diving
+ cField.isSliceOrArray = true
+ cField.sliceSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{}))
+ cField.sliceSubKind = cField.sliceSubtype.Kind()
+ case reflect.Map:
+ isSingleField = false // cached tags mean nothing because it will be split up while diving
+ cField.isMap = true
+ cField.mapSubtype = cField.typ.Elem()
+ cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{}))
+ cField.mapSubKind = cField.mapSubtype.Kind()
+ }
+ } else {
+ cField = cacheField
+ }
+
+ switch cField.kind {
+
+ case reflect.Struct, reflect.Interface, reflect.Invalid:
+
+ if cField.typ != reflect.TypeOf(time.Time{}) {
+ panic("Invalid field passed to fieldWithNameAndValue")
+ }
+ }
+
+ if len(cField.tags) == 0 {
+
+ if isSingleField {
+ cField.tags, isCached = fieldsCache.Get(tag)
+ }
+
+ if !isCached {
+
+ for _, t := range strings.Split(tag, tagSeparator) {
+
+ if t == diveTag {
+
+ cField.dive = true
+ cField.diveTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",")
+ break
+ }
+
+ orVals := strings.Split(t, orSeparator)
+ cTag := &cachedTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))}
+ cField.tags = append(cField.tags, cTag)
+
+ for i, val := range orVals {
+ vals := strings.SplitN(val, tagKeySeparator, 2)
+
+ key := strings.TrimSpace(vals[0])
+
+ if len(key) == 0 {
+ panic(fmt.Sprintf("Invalid validation tag on field %s", name))
+ }
+
+ param := ""
+ if len(vals) > 1 {
+ param = strings.Replace(vals[1], utf8HexComma, ",", -1)
+ }
+
+ cTag.keyVals[i] = []string{key, param}
+ }
+ }
+
+ if isSingleField {
+ fieldsCache.Set(cField.tag, cField.tags)
+ }
+ }
+ }
+
+ var fieldErr *FieldError
+ var err error
+
+ for _, cTag := range cField.tags {
+
+ if cTag.isOrVal {
+
+ errTag := ""
+
+ for _, val := range cTag.keyVals {
+
+ fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, val[0], val[1], name)
+
+ if err == nil {
+ return nil
+ }
+
+ errTag += orSeparator + fieldErr.Tag
+ }
+
+ errTag = strings.TrimLeft(errTag, orSeparator)
+
+ fieldErr.Tag = errTag
+ fieldErr.Kind = cField.kind
+ fieldErr.Type = cField.typ
+
+ return fieldErr
+ }
+
+ if fieldErr, err = v.fieldWithNameAndSingleTag(val, current, f, cTag.keyVals[0][0], cTag.keyVals[0][1], name); err != nil {
+
+ fieldErr.Kind = cField.kind
+ fieldErr.Type = cField.typ
+
+ return fieldErr
+ }
+ }
+
+ if cField.dive {
+
+ if cField.isSliceOrArray {
+
+ if errs := v.traverseSliceOrArray(val, current, valueField, cField); errs != nil && len(errs) > 0 {
+
+ return &FieldError{
+ Field: cField.name,
+ Kind: cField.kind,
+ Type: cField.typ,
+ Value: f,
+ IsPlaceholderErr: true,
+ IsSliceOrArray: true,
+ SliceOrArrayErrs: errs,
+ }
+ }
+
+ } else if cField.isMap {
+ if errs := v.traverseMap(val, current, valueField, cField); errs != nil && len(errs) > 0 {
+
+ return &FieldError{
+ Field: cField.name,
+ Kind: cField.kind,
+ Type: cField.typ,
+ Value: f,
+ IsPlaceholderErr: true,
+ IsMap: true,
+ MapErrs: errs,
+ }
+ }
+ } else {
+ // throw error, if not a slice or map then should not have gotten here
+ panic("dive error! can't dive on a non slice or map")
+ }
+ }
+
+ return nil
+}
+
+func (v *Validate) traverseMap(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[interface{}]error {
+
+ errs := map[interface{}]error{}
+
+ for _, key := range valueField.MapKeys() {
+
+ idxField := valueField.MapIndex(key)
+
+ if cField.mapSubKind == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ cField.mapSubKind = idxField.Kind()
+ }
+
+ switch cField.mapSubKind {
+ case reflect.Struct, reflect.Interface:
+
+ if cField.isTimeSubtype {
+
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
+ errs[key.Interface()] = fieldError
+ }
+
+ continue
+ }
+
+ if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
+
+ if strings.Contains(cField.diveTag, omitempty) {
+ continue
+ }
+
+ tags := strings.Split(cField.diveTag, tagSeparator)
+
+ if len(tags) > 0 {
+
+ var param string
+ vals := strings.SplitN(tags[0], tagKeySeparator, 2)
+
+ if len(vals) > 1 {
+ param = vals[1]
+ }
+
+ errs[key.Interface()] = &FieldError{
+ Field: fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()),
+ Tag: vals[0],
+ Param: param,
+ Value: idxField.Interface(),
+ Kind: idxField.Kind(),
+ Type: cField.mapSubtype,
+ }
+ }
+
+ continue
+ }
+
+ // if we get here, the field is interface and could be a struct or a field
+ // and we need to check the inner type and validate
+ if idxField.Kind() == reflect.Interface {
+
+ idxField = idxField.Elem()
+
+ if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ }
+
+ if idxField.Kind() == reflect.Struct {
+ goto VALIDATESTRUCT
+ }
+
+ // sending nil for cField as it was type interface and could be anything
+ // each time and so must be calculated each time and can't be cached reliably
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
+ errs[key.Interface()] = fieldError
+ }
+
+ continue
+ }
+
+ VALIDATESTRUCT:
+ if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
+ errs[key.Interface()] = structErrors
+ }
+
+ default:
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(mapIndexFieldName, cField.name, key.Interface()), false, nil); fieldError != nil {
+ errs[key.Interface()] = fieldError
+ }
+ }
+ }
+
+ return errs
+}
+
+func (v *Validate) traverseSliceOrArray(val interface{}, current interface{}, valueField reflect.Value, cField *cachedField) map[int]error {
+
+ errs := map[int]error{}
+
+ for i := 0; i < valueField.Len(); i++ {
+
+ idxField := valueField.Index(i)
+
+ if cField.sliceSubKind == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ cField.sliceSubKind = idxField.Kind()
+ }
+
+ switch cField.sliceSubKind {
+ case reflect.Struct, reflect.Interface:
+
+ if cField.isTimeSubtype {
+
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
+ errs[i] = fieldError
+ }
+
+ continue
+ }
+
+ if (idxField.Kind() == reflect.Ptr || idxField.Kind() == reflect.Interface) && idxField.IsNil() {
+
+ if strings.Contains(cField.diveTag, omitempty) {
+ continue
+ }
+
+ tags := strings.Split(cField.diveTag, tagSeparator)
+
+ if len(tags) > 0 {
+
+ var param string
+ vals := strings.SplitN(tags[0], tagKeySeparator, 2)
+
+ if len(vals) > 1 {
+ param = vals[1]
+ }
+
+ errs[i] = &FieldError{
+ Field: fmt.Sprintf(arrayIndexFieldName, cField.name, i),
+ Tag: vals[0],
+ Param: param,
+ Value: idxField.Interface(),
+ Kind: idxField.Kind(),
+ Type: cField.sliceSubtype,
+ }
+ }
+
+ continue
+ }
+
+ // if we get here, the field is interface and could be a struct or a field
+ // and we need to check the inner type and validate
+ if idxField.Kind() == reflect.Interface {
+
+ idxField = idxField.Elem()
+
+ if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
+ idxField = idxField.Elem()
+ }
+
+ if idxField.Kind() == reflect.Struct {
+ goto VALIDATESTRUCT
+ }
+
+ // sending nil for cField as it was type interface and could be anything
+ // each time and so must be calculated each time and can't be cached reliably
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
+ errs[i] = fieldError
+ }
+
+ continue
+ }
+
+ VALIDATESTRUCT:
+ if structErrors := v.structRecursive(val, current, idxField.Interface()); structErrors != nil {
+ errs[i] = structErrors
+ }
+
+ default:
+ if fieldError := v.fieldWithNameAndValue(val, current, idxField.Interface(), cField.diveTag, fmt.Sprintf(arrayIndexFieldName, cField.name, i), false, nil); fieldError != nil {
+ errs[i] = fieldError
+ }
+ }
+ }
+
+ return errs
+}
+
+func (v *Validate) fieldWithNameAndSingleTag(val interface{}, current interface{}, f interface{}, key string, param string, name string) (*FieldError, error) {
+
+ // OK to continue because we checked it's existance before getting into this loop
+ if key == omitempty {
+ return nil, nil
+ }
+
+ valFunc, ok := v.validationFuncs[key]
+ if !ok {
+ panic(fmt.Sprintf("Undefined validation function on field %s", name))
+ }
+
+ if err := valFunc(val, current, f, param); err {
+ return nil, nil
+ }
+
+ return &FieldError{
+ Field: name,
+ Tag: key,
+ Value: f,
+ Param: param,
+ }, errors.New(key)
+}