Compare commits

...

16 Commits
master ... v9

Author SHA1 Message Date
Dean Karn 21c910fc6d update README 4 years ago
Dean Karn 98150497be Add test + docs for FieldLevel.GetTag 4 years ago
Dean Karn f1d020490a
Merge pull request #535 from LRichi/v9 4 years ago
Dean Karn 6720e73db2
Merge pull request #496 from shihanng/unique_struct_field 4 years ago
Dean Karn 5bbca668f0
Merge pull request #530 from skateinmars/chore/improve-doc 4 years ago
Dean Karn 17c1fa8f29
Merge pull request #529 from taybart/v9 4 years ago
Dean Karn bde478f946
Update README.md 5 years ago
Dean Karn a060cd9724
Update README.md 5 years ago
Dean Karn 6963d3cd8d
Merge pull request #544 from go-playground/backport-required-with-fixes 5 years ago
Dean Karn 38bfb46b5a fix required_* 5 years ago
richi b01869e6de Added the function GetTag. 5 years ago
Jean-Philippe Moal c370ccf1ba Clarify and complete tag names example 5 years ago
Jean-Philippe Moal 94aa4243a2 Rework the non standard validators documentation 5 years ago
Taylor aad1714b6a Add e.164 support 5 years ago
Shi Han NG 8efe088678
Update doc for unique=field 5 years ago
Shi Han NG d04a036fb5
Implement unique=FieldName 5 years ago
  1. 4
      README.md
  2. 4
      _examples/simple/main.go
  3. 27
      _examples/struct-level/main.go
  4. 50
      baked_in.go
  5. 46
      doc.go
  6. 46
      field_level.go
  7. 2
      regexes.go
  8. 5
      translations/en/en.go
  9. 7
      util.go
  10. 2
      validator.go
  11. 168
      validator_test.go

@ -1,7 +1,9 @@
**NOTICE:** v9 has entered maintenance status as of 2019-12-24. Please make all new functionality PR's against master.
Package validator
================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
![Project status](https://img.shields.io/badge/version-9.30.0-green.svg)
![Project status](https://img.shields.io/badge/version-9.31.0-green.svg)
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)

@ -68,8 +68,8 @@ func validateStruct() {
fmt.Println(err.Namespace())
fmt.Println(err.Field())
fmt.Println(err.StructNamespace()) // can differ when a custom TagNameFunc is registered or
fmt.Println(err.StructField()) // by passing alt name to ReportError like below
fmt.Println(err.StructNamespace())
fmt.Println(err.StructField())
fmt.Println(err.Tag())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())

@ -2,6 +2,8 @@ package main
import (
"fmt"
"reflect"
"strings"
"gopkg.in/go-playground/validator.v9"
)
@ -11,7 +13,7 @@ type User struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age uint8 `validate:"gte=0,lte=130"`
Email string `validate:"required,email"`
Email string `json:"e-mail" validate:"required,email"`
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}
@ -31,6 +33,15 @@ func main() {
validate = validator.New()
// register function to get tag name from json tags.
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
}
return name
})
// register validation for 'User'
// NOTE: only have to register a non-pointer type for 'User', validator
// interanlly dereferences during it's type checks.
@ -48,7 +59,7 @@ func main() {
FirstName: "",
LastName: "",
Age: 45,
Email: "Badger.Smith@gmail.com",
Email: "Badger.Smith@gmail",
FavouriteColor: "#000",
Addresses: []*Address{address},
}
@ -67,10 +78,10 @@ func main() {
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.Namespace())
fmt.Println(err.Field())
fmt.Println(err.StructNamespace()) // can differ when a custom TagNameFunc is registered or
fmt.Println(err.StructField()) // by passing alt name to ReportError like below
fmt.Println(err.Namespace()) // can differ when a custom TagNameFunc is registered or
fmt.Println(err.Field()) // by passing alt name to ReportError like below
fmt.Println(err.StructNamespace())
fmt.Println(err.StructField())
fmt.Println(err.Tag())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())
@ -101,8 +112,8 @@ func UserStructLevelValidation(sl validator.StructLevel) {
user := sl.Current().Interface().(User)
if len(user.FirstName) == 0 && len(user.LastName) == 0 {
sl.ReportError(user.FirstName, "FirstName", "fname", "fnameorlname", "")
sl.ReportError(user.LastName, "LastName", "lname", "fnameorlname", "")
sl.ReportError(user.FirstName, "fname", "FirstName", "fnameorlname", "")
sl.ReportError(user.LastName, "lname", "LastName", "fnameorlname", "")
}
// plus can do more, even with different tag than "fnameorlname"

@ -103,6 +103,7 @@ var (
"rgba": isRGBA,
"hsl": isHSL,
"hsla": isHSLA,
"e164": isE164,
"email": isEmail,
"url": isURL,
"uri": isURI,
@ -224,14 +225,28 @@ func isOneOf(fl FieldLevel) bool {
func isUnique(fl FieldLevel) bool {
field := fl.Field()
param := fl.Param()
v := reflect.ValueOf(struct{}{})
switch field.Kind() {
case reflect.Slice, reflect.Array:
m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
if param == "" {
m := reflect.MakeMap(reflect.MapOf(field.Type().Elem(), v.Type()))
for i := 0; i < field.Len(); i++ {
m.SetMapIndex(field.Index(i), v)
}
return field.Len() == m.Len()
}
sf, ok := field.Type().Elem().FieldByName(param)
if !ok {
panic(fmt.Sprintf("Bad field name %s", param))
}
m := reflect.MakeMap(reflect.MapOf(sf.Type, v.Type()))
for i := 0; i < field.Len(); i++ {
m.SetMapIndex(field.Index(i), v)
m.SetMapIndex(field.Index(i).FieldByName(param), v)
}
return field.Len() == m.Len()
case reflect.Map:
@ -1219,6 +1234,11 @@ func isFile(fl FieldLevel) bool {
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
// IsE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
func isE164(fl FieldLevel) bool {
return e164Regex.MatchString(fl.Field().String())
}
// IsEmail is the validation function for validating if the current field's value is a valid email address.
func isEmail(fl FieldLevel) bool {
return emailRegex.MatchString(fl.Field().String())
@ -1316,11 +1336,11 @@ func hasValue(fl FieldLevel) bool {
// requireCheckField is a func for check field kind
func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool {
field := fl.Field()
var ok bool
kind := field.Kind()
var nullable, found bool
if len(param) > 0 {
field, kind, ok = fl.GetStructFieldOKAdvanced(fl.Parent(), param)
if !ok {
field, kind, nullable, found = fl.GetStructFieldOKAdvanced2(fl.Parent(), param)
if !found {
return defaultNotFoundValue
}
}
@ -1328,9 +1348,12 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
case reflect.Invalid:
return defaultNotFoundValue
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil()
return field.IsNil()
default:
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
if nullable && field.Interface() != nil {
return false
}
return field.IsValid() && field.Interface() == reflect.Zero(field.Type()).Interface()
}
}
@ -1339,7 +1362,7 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
func requiredWith(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
for _, param := range params {
if requireCheckFieldKind(fl, param, false) {
if !requireCheckFieldKind(fl, param, true) {
return hasValue(fl)
}
}
@ -1351,7 +1374,7 @@ func requiredWith(fl FieldLevel) bool {
func requiredWithAll(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
for _, param := range params {
if !requireCheckFieldKind(fl, param, false) {
if requireCheckFieldKind(fl, param, true) {
return true
}
}
@ -1361,11 +1384,8 @@ func requiredWithAll(fl FieldLevel) bool {
// RequiredWithout is the validation function
// The field under validation must be present and not empty only when any of the other specified fields are not present.
func requiredWithout(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
for _, param := range params {
if !requireCheckFieldKind(fl, param, true) {
return hasValue(fl)
}
if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
return hasValue(fl)
}
return true
}
@ -1375,7 +1395,7 @@ func requiredWithout(fl FieldLevel) bool {
func requiredWithoutAll(fl FieldLevel) bool {
params := parseOneOfParam2(fl.Param())
for _, param := range params {
if requireCheckFieldKind(fl, param, true) {
if !requireCheckFieldKind(fl, param, true) {
return true
}
}

@ -585,9 +585,15 @@ Unique
For arrays & slices, unique will ensure that there are no duplicates.
For maps, unique will ensure that there are no duplicate values.
For slices of struct, unique will ensure that there are no duplicate values
in a field of the struct specified via a parameter.
// For arrays, slices, and maps:
Usage: unique
// For slices of struct:
Usage: unique=field
Alpha Only
This validates that a string value contains ASCII alpha characters only
@ -1058,27 +1064,14 @@ Validator notes:
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
Non standard validators
A collection of validation rules that are frequently needed but are more
complex than the ones found in the baked in validators.
A non standard validator must be registered manually using any tag you like.
See below examples of registration and use.
A non standard validator must be registered manually like you would
with your own custom validation functions.
Example of registration and use:
type Test struct {
TestField string `validate:"yourtag"`
@ -1089,7 +1082,9 @@ See below examples of registration and use.
}
validate := validator.New()
validate.RegisterValidation("yourtag", validations.ValidatorName)
validate.RegisterValidation("yourtag", validators.NotBlank)
Here is a list of the current non standard validators:
NotBlank
This validates that the value is not blank or with length zero.
@ -1097,5 +1092,20 @@ See below examples of registration and use.
ensures they don't have zero length. For others, a non empty value is required.
Usage: notblank
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

@ -5,7 +5,6 @@ import "reflect"
// FieldLevel contains all the information and helper functions
// to validate a field
type FieldLevel interface {
// returns the top level struct, if any
Top() reflect.Value
@ -26,6 +25,9 @@ type FieldLevel interface {
// returns param for validation against current field
Param() string
// GetTag returns the current validations tag name
GetTag() string
// ExtractType gets the actual underlying type of field value.
// It will dive into pointers, customTypes and return you the
// underlying value and it's kind.
@ -37,11 +39,27 @@ type FieldLevel interface {
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
GetStructFieldOK() (reflect.Value, reflect.Kind, bool)
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool)
// traverses the parent struct to retrieve a specific field denoted by the provided namespace
// in the param and returns the field, field kind, if it's a nullable type and whether is was successful in retrieving
// the field at all.
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool)
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool)
}
var _ FieldLevel = new(validate)
@ -52,11 +70,16 @@ func (v *validate) Field() reflect.Value {
}
// FieldName returns the field's name with the tag
// name takeing precedence over the fields actual name.
// name taking precedence over the fields actual name.
func (v *validate) FieldName() string {
return v.cf.altName
}
// GetTag returns the current validations tag name
func (v *validate) GetTag() string {
return v.ct.tag
}
// StructFieldName returns the struct field's name
func (v *validate) StructFieldName() string {
return v.cf.name
@ -68,12 +91,29 @@ func (v *validate) Param() string {
}
// GetStructFieldOK returns Param returns param for validation against current field
//
// Deprecated: Use GetStructFieldOK2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, bool) {
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
current, kind, _, found := v.getStructFieldOKInternal(v.slflParent, v.ct.param)
return current, kind, found
}
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
//
// Deprecated: Use GetStructFieldOKAdvanced2() instead which also return if the value is nullable.
func (v *validate) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) {
current, kind, _, found := v.GetStructFieldOKAdvanced2(val, namespace)
return current, kind, found
}
// GetStructFieldOK returns Param returns param for validation against current field
func (v *validate) GetStructFieldOK2() (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(v.slflParent, v.ct.param)
}
// GetStructFieldOKAdvanced is the same as GetStructFieldOK except that it accepts the parent struct to start looking for
// the field and namespace allowing more extensibility for validators.
func (v *validate) GetStructFieldOKAdvanced2(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool, bool) {
return v.getStructFieldOKInternal(val, namespace)
}

@ -16,6 +16,7 @@ const (
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}])))\\.?$"
e164RegexString = "^\\+[1-9]?[0-9]{7,14}$"
base64RegexString = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
base64URLRegexString = "^(?:[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})$"
@ -61,6 +62,7 @@ var (
rgbaRegex = regexp.MustCompile(rgbaRegexString)
hslRegex = regexp.MustCompile(hslRegexString)
hslaRegex = regexp.MustCompile(hslaRegexString)
e164Regex = regexp.MustCompile(e164RegexString)
emailRegex = regexp.MustCompile(emailRegexString)
base64Regex = regexp.MustCompile(base64RegexString)
base64URLRegex = regexp.MustCompile(base64URLRegexString)

@ -1043,6 +1043,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid HSLA color",
override: false,
},
{
tag: "e164",
translation: "{0} must be a valid E.164 formatted phone number",
override: false,
},
{
tag: "email",
translation: "{0} must be a valid email address",

@ -57,11 +57,10 @@ BEGIN:
//
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, found bool) {
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
BEGIN:
current, kind, _ = v.ExtractType(val)
current, kind, nullable = v.ExtractType(val)
if kind == reflect.Invalid {
return
}
@ -112,7 +111,7 @@ BEGIN:
arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
if arrIdx >= current.Len() {
return current, kind, false
return
}
startIdx := idx2 + 1

@ -7,7 +7,7 @@ import (
"strconv"
)
// per validate contruct
// per validate construct
type validate struct {
v *Validate
top reflect.Value

@ -1640,125 +1640,125 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
v: vd,
}
current, kind, ok := v.getStructFieldOKInternal(val, "Inner.CreatedAt")
current, kind, _, ok := v.getStructFieldOKInternal(val, "Inner.CreatedAt")
Equal(t, ok, true)
Equal(t, kind, reflect.Struct)
tm, ok := current.Interface().(time.Time)
Equal(t, ok, true)
Equal(t, tm, now)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Slice[1]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[1]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField")
current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.CrazyNonExistantField")
Equal(t, ok, false)
current, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]")
current, _, _, ok = v.getStructFieldOKInternal(val, "Inner.Slice[101]")
Equal(t, ok, false)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.Map[key3]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val3")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapMap[key2][key2-1]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMap[key2][key2-1]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapStructs[key2].Name")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapStructs[key2].Name")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "name2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapMapStruct[key3][key3-1].Name")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapMapStruct[key3][key3-1].Name")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "name3")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceSlice[2][0]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSlice[2][0]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "7")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceSliceStruct[2][1].Name")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceSliceStruct[2][1].Name")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "name8")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceMap[1][key5]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceMap[1][key5]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val5")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapSlice[key3][2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapSlice[key3][2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "9")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt8[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt8[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt16[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt16[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt32[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt32[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapInt64[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapInt64[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint8[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint8[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint16[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint16[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint32[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint32[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapUint64[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapUint64[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat32[3.03]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat32[3.03]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val3")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat64[2.02]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapFloat64[2.02]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val2")
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.MapBool[true]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.MapBool[true]")
Equal(t, ok, true)
Equal(t, kind, reflect.String)
Equal(t, current.String(), "val1")
@ -1784,13 +1784,13 @@ func TestCrossNamespaceFieldValidation(t *testing.T) {
val = reflect.ValueOf(test)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2]")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2]")
Equal(t, ok, true)
Equal(t, kind, reflect.Ptr)
Equal(t, current.String(), "<*validator.SliceStruct Value>")
Equal(t, current.IsNil(), true)
current, kind, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2].Name")
current, kind, _, ok = v.getStructFieldOKInternal(val, "Inner.SliceStructs[2].Name")
Equal(t, ok, false)
Equal(t, kind, reflect.Ptr)
Equal(t, current.String(), "<*validator.SliceStruct Value>")
@ -4911,6 +4911,7 @@ func TestStructOnlyValidation(t *testing.T) {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age uint8 `validate:"gte=0,lte=130"`
Number string `validate:"required,e164"`
Email string `validate:"required,email"`
FavouriteColor string `validate:"hexcolor|rgb|rgba"`
Addresses []*Address `validate:"required"` // a person can have a home and cottage...
@ -4928,6 +4929,7 @@ func TestStructOnlyValidation(t *testing.T) {
FirstName: "",
LastName: "",
Age: 45,
Number: "+1123456789",
Email: "Badger.Smith@gmail.com",
FavouriteColor: "#000",
Addresses: []*Address{address},
@ -8158,6 +8160,49 @@ func TestUniqueValidation(t *testing.T) {
PanicMatches(t, func() { _ = validate.Var(1.0, "unique") }, "Bad field type float64")
}
func TestUniqueValidationStructSlice(t *testing.T) {
testStructs := []struct {
A string
B string
}{
{A: "one", B: "two"},
{A: "one", B: "three"},
}
tests := []struct {
target interface{}
param string
expected bool
}{
{testStructs, "unique", true},
{testStructs, "unique=A", false},
{testStructs, "unique=B", true},
}
validate := New()
for i, test := range tests {
errs := validate.Var(test.target, test.param)
if test.expected {
if !IsEqual(errs, nil) {
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
}
} else {
if IsEqual(errs, nil) {
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
} else {
val := getError(errs, "", "")
if val.Tag() != "unique" {
t.Fatalf("Index: %d unique failed Error: %v", i, errs)
}
}
}
}
PanicMatches(t, func() { validate.Var(testStructs, "unique=C") }, "Bad field name C")
}
func TestHTMLValidation(t *testing.T) {
tests := []struct {
param string
@ -8864,3 +8909,72 @@ func TestAbilityToValidateNils(t *testing.T) {
errs = val.Struct(ts)
NotEqual(t, errs, nil)
}
func TestRequiredWithoutPointers(t *testing.T) {
type Lookup struct {
FieldA *bool `json:"fieldA,omitempty" validate:"required_without=FieldB"`
FieldB *bool `json:"fieldB,omitempty" validate:"required_without=FieldA"`
}
b := true
lookup := Lookup{
FieldA: &b,
FieldB: nil,
}
val := New()
errs := val.Struct(lookup)
Equal(t, errs, nil)
b = false
lookup = Lookup{
FieldA: &b,
FieldB: nil,
}
errs = val.Struct(lookup)
Equal(t, errs, nil)
}
func TestRequiredWithoutAllPointers(t *testing.T) {
type Lookup struct {
FieldA *bool `json:"fieldA,omitempty" validate:"required_without_all=FieldB"`
FieldB *bool `json:"fieldB,omitempty" validate:"required_without_all=FieldA"`
}
b := true
lookup := Lookup{
FieldA: &b,
FieldB: nil,
}
val := New()
errs := val.Struct(lookup)
Equal(t, errs, nil)
b = false
lookup = Lookup{
FieldA: &b,
FieldB: nil,
}
errs = val.Struct(lookup)
Equal(t, errs, nil)
}
func TestGetTag(t *testing.T) {
var tag string
type Test struct {
String string `validate:"mytag"`
}
val := New()
val.RegisterValidation("mytag", func(fl FieldLevel) bool {
tag = fl.GetTag()
return true
})
var test Test
errs := val.Struct(test)
Equal(t, errs, nil)
Equal(t, tag, "mytag")
}

Loading…
Cancel
Save