From 9c2ad91230d72fe6d661450cc78300ea223ae2bc Mon Sep 17 00:00:00 2001 From: Vidhu Kant Sharma Date: Mon, 19 Oct 2020 11:05:36 +0530 Subject: make it better --- .../gopkg.in/bluesuncorp/validator.v5/.gitignore | 26 + .../gopkg.in/bluesuncorp/validator.v5/.travis.yml | 13 + vendor/gopkg.in/bluesuncorp/validator.v5/LICENSE | 22 + vendor/gopkg.in/bluesuncorp/validator.v5/README.md | 44 + .../gopkg.in/bluesuncorp/validator.v5/baked_in.go | 999 +++++++++++++++++++++ vendor/gopkg.in/bluesuncorp/validator.v5/doc.go | 474 ++++++++++ .../gopkg.in/bluesuncorp/validator.v5/regexes.go | 64 ++ .../gopkg.in/bluesuncorp/validator.v5/validator.go | 935 +++++++++++++++++++ 8 files changed, 2577 insertions(+) create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/.gitignore create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/.travis.yml create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/LICENSE create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/README.md create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/baked_in.go create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/doc.go create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/regexes.go create mode 100644 vendor/gopkg.in/bluesuncorp/validator.v5/validator.go (limited to 'vendor/gopkg.in/bluesuncorp') 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) +} -- cgit v1.2.3