mirror of https://github.com/prometheus/prometheus
parent
5e57fa85c0
commit
57b358b82a
|
@ -60,12 +60,17 @@ func TestParse(t *testing.T) {
|
|||
input: []string{"-alertmanager.url", "alertmanager.company.com"},
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
input: []string{"-alertmanager.url", "https://double--dash.de"},
|
||||
valid: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
// reset "immutable" config
|
||||
cfg.prometheusURL = ""
|
||||
cfg.influxdbURL = ""
|
||||
cfg.alertmanagerURLs = stringset{}
|
||||
|
||||
err := parse(test.input)
|
||||
if test.valid && err != nil {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
govalidator
|
||||
===========
|
||||
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![views](https://sourcegraph.com/api/repos/github.com/asaskevich/govalidator/.counters/views.png)](https://sourcegraph.com/github.com/asaskevich/govalidator)
|
||||
[![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
||||
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
||||
[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator)
|
||||
|
||||
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
|
||||
|
@ -12,20 +11,86 @@ Type the following command in your terminal:
|
|||
|
||||
go get github.com/asaskevich/govalidator
|
||||
|
||||
or you can get specified release of the package with `gopkg.in`:
|
||||
|
||||
go get gopkg.in/asaskevich/govalidator.v4
|
||||
|
||||
After it the package is ready to use.
|
||||
|
||||
|
||||
#### Import package in your project
|
||||
Add following line in your `*.go` file:
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
```
|
||||
If you unhappy to use long `govalidator`, you can do something like this:
|
||||
If you are unhappy to use long `govalidator`, you can do something like this:
|
||||
```go
|
||||
import (
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
)
|
||||
```
|
||||
|
||||
#### Activate behavior to require all fields have a validation tag by default
|
||||
`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function.
|
||||
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
func init() {
|
||||
govalidator.SetFieldsRequiredByDefault(true)
|
||||
}
|
||||
```
|
||||
|
||||
Here's some code to explain it:
|
||||
```go
|
||||
// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
||||
type exampleStruct struct {
|
||||
Name string ``
|
||||
Email string `valid:"email"`
|
||||
}
|
||||
|
||||
// this, however, will only fail when Email is empty or an invalid email address:
|
||||
type exampleStruct2 struct {
|
||||
Name string `valid:"-"`
|
||||
Email string `valid:"email"`
|
||||
}
|
||||
|
||||
// lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
||||
type exampleStruct2 struct {
|
||||
Name string `valid:"-"`
|
||||
Email string `valid:"email,optional"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123))
|
||||
##### Custom validator function signature
|
||||
A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible.
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
// old signature
|
||||
func(i interface{}) bool
|
||||
|
||||
// new signature
|
||||
func(i interface{}, o interface{}) bool
|
||||
```
|
||||
|
||||
##### Adding a custom validator
|
||||
This was changed to prevent data races when accessing custom validators.
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
// before
|
||||
govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||
// ...
|
||||
})
|
||||
|
||||
// after
|
||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||
// ...
|
||||
}))
|
||||
```
|
||||
|
||||
#### List of functions:
|
||||
```go
|
||||
func Abs(value float64) float64
|
||||
|
@ -184,6 +249,8 @@ govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
|||
return str == "duck"
|
||||
})
|
||||
```
|
||||
For completely custom validators (interface-based), see below.
|
||||
|
||||
Here is a list of available validators for struct fields (validator - used function):
|
||||
```go
|
||||
"alpha": IsAlpha,
|
||||
|
@ -272,6 +339,49 @@ println(result)
|
|||
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
||||
```
|
||||
|
||||
###### Custom validation functions
|
||||
Custom validation using your own domain specific validators is also available - here's an example of how to use it:
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
type CustomByteArray [6]byte // custom types are supported and can be validated
|
||||
|
||||
type StructWithCustomByteArray struct {
|
||||
ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
|
||||
Email string `valid:"email"`
|
||||
CustomMinLength int `valid:"-"`
|
||||
}
|
||||
|
||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||
switch v := context.(type) { // you can type switch on the context interface being validated
|
||||
case StructWithCustomByteArray:
|
||||
// you can check and validate against some other field in the context,
|
||||
// return early or not validate against the context at all – your choice
|
||||
case SomeOtherType:
|
||||
// ...
|
||||
default:
|
||||
// expecting some other type? Throw/panic here or continue
|
||||
}
|
||||
|
||||
switch v := i.(type) { // type switch on the struct field being validated
|
||||
case CustomByteArray:
|
||||
for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
|
||||
if e != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}))
|
||||
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||
switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
|
||||
case StructWithCustomByteArray:
|
||||
return len(v.ID) >= v.CustomMinLength
|
||||
}
|
||||
return false
|
||||
}))
|
||||
```
|
||||
|
||||
#### Notes
|
||||
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
|
||||
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
|
||||
|
@ -280,6 +390,7 @@ Full information about code coverage is also available here: [govalidator on goc
|
|||
If you do have a contribution for the package feel free to put up a Pull Request or open Issue.
|
||||
|
||||
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
|
||||
* [Daniel Lohse](https://github.com/annismckenzie)
|
||||
* [Attila Oláh](https://github.com/attilaolah)
|
||||
* [Daniel Korner](https://github.com/Dadie)
|
||||
* [Steven Wilkin](https://github.com/stevenwilkin)
|
||||
|
@ -288,5 +399,3 @@ If you do have a contribution for the package feel free to put up a Pull Request
|
|||
* [Nathan Davies](https://github.com/nathj07)
|
||||
* [Matt Sanford](https://github.com/mzsanford)
|
||||
* [Simon ccl1115](https://github.com/ccl1115)
|
||||
|
||||
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/asaskevich/govalidator/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
|
|
@ -18,7 +18,7 @@ func Each(array []interface{}, iterator Iterator) {
|
|||
|
||||
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
||||
func Map(array []interface{}, iterator ResultIterator) []interface{} {
|
||||
var result []interface{} = make([]interface{}, len(array))
|
||||
var result = make([]interface{}, len(array))
|
||||
for index, data := range array {
|
||||
result[index] = iterator(data, index)
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func Find(array []interface{}, iterator ConditionIterator) interface{} {
|
|||
|
||||
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
|
||||
var result []interface{} = make([]interface{}, 0)
|
||||
var result = make([]interface{}, 0)
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
result = append(result, data)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package govalidator
|
||||
|
||||
// Errors is an array of multiple errors and conforms to the error interface.
|
||||
type Errors []error
|
||||
|
||||
// Errors returns itself.
|
||||
func (es Errors) Errors() []error {
|
||||
return es
|
||||
}
|
||||
|
@ -14,6 +16,7 @@ func (es Errors) Error() string {
|
|||
return err
|
||||
}
|
||||
|
||||
// Error encapsulates a name, an error and whether there's a custom error message or not.
|
||||
type Error struct {
|
||||
Name string
|
||||
Err error
|
||||
|
@ -23,7 +26,6 @@ type Error struct {
|
|||
func (e Error) Error() string {
|
||||
if e.CustomErrorMessageExists {
|
||||
return e.Err.Error()
|
||||
} else {
|
||||
return e.Name + ": " + e.Err.Error()
|
||||
}
|
||||
return e.Name + ": " + e.Err.Error()
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ const (
|
|||
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||
DNSName string = `^([a-zA-Z0-9]{1}[a-zA-Z0-9_-]{1,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9_-]{1,62})*$`
|
||||
URL string = `^((ftp|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`
|
||||
URL string = `^((ftp|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]([a-zA-Z0-9-]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`
|
||||
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
|
||||
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
||||
UnixPath string = `^((?:\/[a-zA-Z0-9\.\:]+(?:_[a-zA-Z0-9\:\.]+)*(?:\-[\:a-zA-Z0-9\.]+)*)+\/?)$`
|
||||
|
|
|
@ -3,17 +3,19 @@ package govalidator
|
|||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
||||
type Validator func(str string) bool
|
||||
|
||||
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
||||
type CustomTypeValidator func(i interface{}) bool
|
||||
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
|
||||
type CustomTypeValidator func(i interface{}, o interface{}) bool
|
||||
|
||||
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
|
||||
type ParamValidator func(str string, params ...string) bool
|
||||
type tagOptions []string
|
||||
type tagOptionsMap map[string]string
|
||||
|
||||
// UnsupportedTypeError is a wrapper for reflect.Type
|
||||
type UnsupportedTypeError struct {
|
||||
|
@ -31,16 +33,36 @@ var ParamTagMap = map[string]ParamValidator{
|
|||
"matches": StringMatches,
|
||||
}
|
||||
|
||||
// ParamTagRegexMap maps param tags to their respective regexes.
|
||||
var ParamTagRegexMap = map[string]*regexp.Regexp{
|
||||
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"matches": regexp.MustCompile(`matches\(([^)]+)\)`),
|
||||
}
|
||||
|
||||
type customTypeTagMap struct {
|
||||
validators map[string]CustomTypeValidator
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
|
||||
tm.RLock()
|
||||
defer tm.RUnlock()
|
||||
v, ok := tm.validators[name]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
|
||||
tm.Lock()
|
||||
defer tm.Unlock()
|
||||
tm.validators[name] = ctv
|
||||
}
|
||||
|
||||
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
||||
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
||||
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
||||
var CustomTypeTagMap = map[string]CustomTypeValidator{}
|
||||
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
|
||||
|
||||
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
||||
var TagMap = map[string]Validator{
|
||||
|
|
|
@ -189,7 +189,7 @@ func NormalizeEmail(str string) (string, error) {
|
|||
return strings.Join(parts, "@"), nil
|
||||
}
|
||||
|
||||
// Will truncate a string closest length without breaking words.
|
||||
// Truncate a string to the closest length without breaking words.
|
||||
func Truncate(str string, length int, ending string) string {
|
||||
var aftstr, befstr string
|
||||
if len(str) > length {
|
||||
|
@ -203,9 +203,8 @@ func Truncate(str string, length int, ending string) string {
|
|||
if present > length && i != 0 {
|
||||
if (length - before) < (present - length) {
|
||||
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
|
||||
} else {
|
||||
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
||||
}
|
||||
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -529,7 +529,8 @@ func IsLongitude(str string) bool {
|
|||
return rxLongitude.MatchString(str)
|
||||
}
|
||||
|
||||
// ValidateStruct use tags for fields
|
||||
// ValidateStruct use tags for fields.
|
||||
// result will be equal to `false` if there are any errors.
|
||||
func ValidateStruct(s interface{}) (bool, error) {
|
||||
if s == nil {
|
||||
return true, nil
|
||||
|
@ -551,9 +552,9 @@ func ValidateStruct(s interface{}) (bool, error) {
|
|||
if typeField.PkgPath != "" {
|
||||
continue // Private field
|
||||
}
|
||||
resultField, err := typeCheck(valueField, typeField)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
resultField, err2 := typeCheck(valueField, typeField, val)
|
||||
if err2 != nil {
|
||||
errs = append(errs, err2)
|
||||
}
|
||||
result = result && resultField
|
||||
}
|
||||
|
@ -563,11 +564,22 @@ func ValidateStruct(s interface{}) (bool, error) {
|
|||
return result, err
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's tag into its
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) tagOptions {
|
||||
split := strings.SplitN(tag, ",", -1)
|
||||
return tagOptions(split)
|
||||
// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""}
|
||||
func parseTagIntoMap(tag string) tagOptionsMap {
|
||||
optionsMap := make(tagOptionsMap)
|
||||
options := strings.SplitN(tag, ",", -1)
|
||||
for _, option := range options {
|
||||
validationOptions := strings.Split(option, "~")
|
||||
if !isValidTag(validationOptions[0]) {
|
||||
continue
|
||||
}
|
||||
if len(validationOptions) == 2 {
|
||||
optionsMap[validationOptions[0]] = validationOptions[1]
|
||||
} else {
|
||||
optionsMap[validationOptions[0]] = ""
|
||||
}
|
||||
}
|
||||
return optionsMap
|
||||
}
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
|
@ -635,52 +647,20 @@ func StringLength(str string, params ...string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Contains returns whether checks that a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (opts tagOptions) contains(optionName string) bool {
|
||||
for i := range opts {
|
||||
tagOpt := opts[i]
|
||||
if tagOpt == optionName {
|
||||
return true
|
||||
func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) {
|
||||
if requiredOption, isRequired := options["required"]; isRequired {
|
||||
if len(requiredOption) > 0 {
|
||||
return false, Error{t.Name, fmt.Errorf(requiredOption), true}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func searchOption(limit int, predicate func(counter int) bool) int {
|
||||
for counter := 0; counter < limit; counter++ {
|
||||
if predicate(counter) {
|
||||
return counter
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func checkRequired(v reflect.Value, t reflect.StructField, options tagOptions) (bool, error) {
|
||||
var err error
|
||||
var customErrorMessageExists bool
|
||||
requiredIndex := searchOption(len(options), func(index int) bool { return strings.HasPrefix(options[index], "required") })
|
||||
optionalIndex := searchOption(len(options), func(index int) bool { return strings.HasPrefix(options[index], "optional") })
|
||||
if requiredIndex > -1 {
|
||||
validationOptions := strings.Split(options[requiredIndex], "~")
|
||||
if len(validationOptions) == 2 {
|
||||
err = fmt.Errorf(strings.Split(options[requiredIndex], "~")[1])
|
||||
customErrorMessageExists = true
|
||||
} else {
|
||||
err = fmt.Errorf("non zero value required")
|
||||
}
|
||||
return false, Error{t.Name, err, customErrorMessageExists}
|
||||
} else if fieldsRequiredByDefault && optionalIndex == -1 {
|
||||
err := fmt.Errorf("All fields are required to at least have one validation defined")
|
||||
return false, Error{t.Name, err, customErrorMessageExists}
|
||||
return false, Error{t.Name, fmt.Errorf("non zero value required"), false}
|
||||
} else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional {
|
||||
return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false}
|
||||
}
|
||||
// not required and empty is valid
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
||||
var customErrorMessageExists bool
|
||||
func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value) (bool, error) {
|
||||
if !v.IsValid() {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -693,30 +673,32 @@ func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
|||
if !fieldsRequiredByDefault {
|
||||
return true, nil
|
||||
}
|
||||
err := fmt.Errorf("All fields are required to at least have one validation defined")
|
||||
return false, Error{t.Name, err, customErrorMessageExists}
|
||||
return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false}
|
||||
case "-":
|
||||
return true, nil
|
||||
}
|
||||
|
||||
options := parseTag(tag)
|
||||
for i := range options {
|
||||
tagOpt := options[i]
|
||||
tagOptions := strings.Split(tagOpt, "~")
|
||||
if ok := isValidTag(tagOptions[0]); !ok {
|
||||
continue
|
||||
}
|
||||
if validatefunc, ok := CustomTypeTagMap[tagOptions[0]]; ok {
|
||||
options = append(options[:i], options[i+1:]...) // we found our custom validator, so remove it from the options
|
||||
if result := validatefunc(v.Interface()); !result {
|
||||
if len(tagOptions) == 2 {
|
||||
return false, Error{t.Name, fmt.Errorf(tagOptions[1]), true}
|
||||
options := parseTagIntoMap(tag)
|
||||
var customTypeErrors Errors
|
||||
var customTypeValidatorsExist bool
|
||||
for validatorName, customErrorMessage := range options {
|
||||
if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok {
|
||||
customTypeValidatorsExist = true
|
||||
if result := validatefunc(v.Interface(), o.Interface()); !result {
|
||||
if len(customErrorMessage) > 0 {
|
||||
customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf(customErrorMessage), CustomErrorMessageExists: true})
|
||||
continue
|
||||
}
|
||||
return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), tagOptions[0]), false}
|
||||
customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false})
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
if customTypeValidatorsExist {
|
||||
if len(customTypeErrors.Errors()) > 0 {
|
||||
return false, customTypeErrors
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if isEmptyValue(v) {
|
||||
// an empty value is not validated, check only required
|
||||
|
@ -730,25 +712,18 @@ func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
|||
reflect.Float32, reflect.Float64,
|
||||
reflect.String:
|
||||
// for each tag option check the map of validator functions
|
||||
for i := range options {
|
||||
tagOpt := options[i]
|
||||
tagOptions := strings.Split(tagOpt, "~")
|
||||
negate := false
|
||||
customMsgExists := (len(tagOptions) == 2)
|
||||
for validator, customErrorMessage := range options {
|
||||
var negate bool
|
||||
customMsgExists := (len(customErrorMessage) > 0)
|
||||
// Check wether the tag looks like '!something' or 'something'
|
||||
if len(tagOptions[0]) > 0 && tagOptions[0][0] == '!' {
|
||||
tagOpt = string(tagOptions[0][1:])
|
||||
tagOptions[0] = tagOpt
|
||||
if validator[0] == '!' {
|
||||
validator = string(validator[1:])
|
||||
negate = true
|
||||
}
|
||||
if ok := isValidTag(tagOptions[0]); !ok {
|
||||
err := fmt.Errorf("Unknown Validator %s", tagOptions[0])
|
||||
return false, Error{t.Name, err, false}
|
||||
}
|
||||
|
||||
// Check for param validators
|
||||
for key, value := range ParamTagRegexMap {
|
||||
ps := value.FindStringSubmatch(tagOptions[0])
|
||||
ps := value.FindStringSubmatch(validator)
|
||||
if len(ps) > 0 {
|
||||
if validatefunc, ok := ParamTagMap[key]; ok {
|
||||
switch v.Kind() {
|
||||
|
@ -758,30 +733,29 @@ func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
|||
var err error
|
||||
if !negate {
|
||||
if customMsgExists {
|
||||
err = fmt.Errorf(tagOptions[1])
|
||||
err = fmt.Errorf(customErrorMessage)
|
||||
} else {
|
||||
err = fmt.Errorf("%s does not validate as %s", field, tagOpt)
|
||||
err = fmt.Errorf("%s does not validate as %s", field, validator)
|
||||
}
|
||||
|
||||
} else {
|
||||
if customMsgExists {
|
||||
err = fmt.Errorf(tagOptions[1])
|
||||
err = fmt.Errorf(customErrorMessage)
|
||||
} else {
|
||||
err = fmt.Errorf("%s does validate as %s", field, tagOpt)
|
||||
err = fmt.Errorf("%s does validate as %s", field, validator)
|
||||
}
|
||||
}
|
||||
return false, Error{t.Name, err, customMsgExists}
|
||||
}
|
||||
default:
|
||||
//Not Yet Supported Types (Fail here!)
|
||||
err := fmt.Errorf("Validator %s doesn't support kind %s", tagOptions[0], v.Kind())
|
||||
return false, Error{t.Name, err, false}
|
||||
// type not yet supported, fail
|
||||
return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validatefunc, ok := TagMap[tagOptions[0]]; ok {
|
||||
if validatefunc, ok := TagMap[validator]; ok {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
field := fmt.Sprint(v) // make value into string, then validate with regex
|
||||
|
@ -790,22 +764,22 @@ func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
|||
|
||||
if !negate {
|
||||
if customMsgExists {
|
||||
err = fmt.Errorf(tagOptions[1])
|
||||
err = fmt.Errorf(customErrorMessage)
|
||||
} else {
|
||||
err = fmt.Errorf("%s does not validate as %s", field, tagOpt)
|
||||
err = fmt.Errorf("%s does not validate as %s", field, validator)
|
||||
}
|
||||
} else {
|
||||
if customMsgExists {
|
||||
err = fmt.Errorf(tagOptions[1])
|
||||
err = fmt.Errorf(customErrorMessage)
|
||||
} else {
|
||||
err = fmt.Errorf("%s does validate as %s", field, tagOpt)
|
||||
err = fmt.Errorf("%s does validate as %s", field, validator)
|
||||
}
|
||||
}
|
||||
return false, Error{t.Name, err, customMsgExists}
|
||||
}
|
||||
default:
|
||||
//Not Yet Supported Types (Fail here!)
|
||||
err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", tagOptions[0], v.Kind(), v)
|
||||
err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v)
|
||||
return false, Error{t.Name, err, false}
|
||||
}
|
||||
}
|
||||
|
@ -833,7 +807,7 @@ func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
|||
var resultItem bool
|
||||
var err error
|
||||
if v.Index(i).Kind() != reflect.Struct {
|
||||
resultItem, err = typeCheck(v.Index(i), t)
|
||||
resultItem, err = typeCheck(v.Index(i), t, o)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -852,7 +826,7 @@ func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
|||
var resultItem bool
|
||||
var err error
|
||||
if v.Index(i).Kind() != reflect.Struct {
|
||||
resultItem, err = typeCheck(v.Index(i), t)
|
||||
resultItem, err = typeCheck(v.Index(i), t, o)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -876,7 +850,7 @@ func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
|||
if v.IsNil() {
|
||||
return true, nil
|
||||
}
|
||||
return typeCheck(v.Elem(), t)
|
||||
return typeCheck(v.Elem(), t, o)
|
||||
case reflect.Struct:
|
||||
return ValidateStruct(v.Interface())
|
||||
default:
|
||||
|
@ -929,7 +903,10 @@ func ErrorsByField(e error) map[string]string {
|
|||
m[e.(Error).Name] = e.(Error).Err.Error()
|
||||
case Errors:
|
||||
for _, item := range e.(Errors).Errors() {
|
||||
m[item.(Error).Name] = item.(Error).Err.Error()
|
||||
n := ErrorsByField(item)
|
||||
for k, v := range n {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
"ignore": "test appengine",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "DypC2gHBVRhfPqBbsuGYgkrjvOw=",
|
||||
"checksumSHA1": "CDOphCxF7dup+hBBVRpFeQhM9Jw=",
|
||||
"path": "github.com/asaskevich/govalidator",
|
||||
"revision": "5b6e9375cbf581a9008064f7216e816b568d6daa",
|
||||
"revisionTime": "2016-04-23T17:31:43Z"
|
||||
"revision": "593d64559f7600f29581a3ee42177f5dbded27a9",
|
||||
"revisionTime": "2016-07-15T17:06:12Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "gzORxKc4oYt8ROSTT0UnuczfJB0=",
|
||||
|
|
Loading…
Reference in New Issue