Merge branch 'field-level-validation2' into v9

pull/256/head
Dean Karn 8 years ago
commit 678f8cf9a3
  1. 2
      README.md
  2. 5
      translations/en/en.go
  3. 5
      translations/en/en_test.go
  4. 89
      util.go
  5. 9
      validator.go
  6. 2
      validator_test.go

@ -2,7 +2,7 @@ Package validator
================ ================
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png"> <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) [![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.1.0-green.svg) ![Project status](https://img.shields.io/badge/version-9.1.1-green.svg)
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![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) [![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) [![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)

@ -1247,6 +1247,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must contain a valid MAC address", translation: "{0} must contain a valid MAC address",
override: false, override: false,
}, },
{
tag: "iscolor",
translation: "{0} must be a valid color",
override: false,
},
} }
for _, t := range translations { for _, t := range translations {

@ -128,6 +128,7 @@ func TestTranslations(t *testing.T) {
IPAddrv6 string `validate:"ip6_addr"` IPAddrv6 string `validate:"ip6_addr"`
UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future UinxAddr string `validate:"unix_addr"` // can't fail from within Go's net package currently, but maybe in the future
MAC string `validate:"mac"` MAC string `validate:"mac"`
IsColor string `validate:"iscolor"`
} }
var test Test var test Test
@ -180,6 +181,10 @@ func TestTranslations(t *testing.T) {
ns string ns string
expected string expected string
}{ }{
{
ns: "Test.IsColor",
expected: "IsColor must be a valid color",
},
{ {
ns: "Test.MAC", ns: "Test.MAC",
expected: "MAC must contain a valid MAC address", expected: "MAC must contain a valid MAC address",

@ -11,6 +11,7 @@ import (
// underlying value and it's kind. // underlying value and it's kind.
func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) { func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) {
BEGIN:
switch current.Kind() { switch current.Kind() {
case reflect.Ptr: case reflect.Ptr:
@ -20,7 +21,8 @@ func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (re
return current, reflect.Ptr, nullable return current, reflect.Ptr, nullable
} }
return v.extractTypeInternal(current.Elem(), nullable) current = current.Elem()
goto BEGIN
case reflect.Interface: case reflect.Interface:
@ -30,7 +32,8 @@ func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (re
return current, reflect.Interface, nullable return current, reflect.Interface, nullable
} }
return v.extractTypeInternal(current.Elem(), nullable) current = current.Elem()
goto BEGIN
case reflect.Invalid: case reflect.Invalid:
return current, reflect.Invalid, nullable return current, reflect.Invalid, nullable
@ -40,7 +43,8 @@ func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (re
if v.v.hasCustomFuncs { if v.v.hasCustomFuncs {
if fn, ok := v.v.customFuncs[current.Type()]; ok { if fn, ok := v.v.customFuncs[current.Type()]; ok {
return v.extractTypeInternal(reflect.ValueOf(fn(current)), nullable) current = reflect.ValueOf(fn(current))
goto BEGIN
} }
} }
@ -53,23 +57,24 @@ func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (re
// //
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field // 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. // could not be retrieved because it didn't exist.
func (v *validate) getStructFieldOKInternal(current reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) { func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, found bool) {
current, kind, _ := v.ExtractType(current) BEGIN:
current, kind, _ = v.ExtractType(val)
if kind == reflect.Invalid { if kind == reflect.Invalid {
return current, kind, false return
} }
if namespace == "" { if namespace == "" {
return current, kind, true found = true
return
} }
switch kind { switch kind {
case reflect.Ptr, reflect.Interface: case reflect.Ptr, reflect.Interface:
return
return current, kind, false
case reflect.Struct: case reflect.Struct:
@ -95,9 +100,9 @@ func (v *validate) getStructFieldOKInternal(current reflect.Value, namespace str
ns = namespace[bracketIdx:] ns = namespace[bracketIdx:]
} }
current = current.FieldByName(fld) val = current.FieldByName(fld)
namespace = ns
return v.getStructFieldOKInternal(current, ns) goto BEGIN
} }
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
@ -118,7 +123,9 @@ func (v *validate) getStructFieldOKInternal(current reflect.Value, namespace str
} }
} }
return v.getStructFieldOKInternal(current.Index(arrIdx), namespace[startIdx:]) val = current.Index(arrIdx)
namespace = namespace[startIdx:]
goto BEGIN
case reflect.Map: case reflect.Map:
idx := strings.Index(namespace, leftBracket) + 1 idx := strings.Index(namespace, leftBracket) + 1
@ -137,48 +144,76 @@ func (v *validate) getStructFieldOKInternal(current reflect.Value, namespace str
switch current.Type().Key().Kind() { switch current.Type().Key().Kind() {
case reflect.Int: case reflect.Int:
i, _ := strconv.Atoi(key) i, _ := strconv.Atoi(key)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(i))
namespace = namespace[endIdx+1:]
case reflect.Int8: case reflect.Int8:
i, _ := strconv.ParseInt(key, 10, 8) i, _ := strconv.ParseInt(key, 10, 8)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(int8(i))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(int8(i)))
namespace = namespace[endIdx+1:]
case reflect.Int16: case reflect.Int16:
i, _ := strconv.ParseInt(key, 10, 16) i, _ := strconv.ParseInt(key, 10, 16)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(int16(i))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(int16(i)))
namespace = namespace[endIdx+1:]
case reflect.Int32: case reflect.Int32:
i, _ := strconv.ParseInt(key, 10, 32) i, _ := strconv.ParseInt(key, 10, 32)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(int32(i))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(int32(i)))
namespace = namespace[endIdx+1:]
case reflect.Int64: case reflect.Int64:
i, _ := strconv.ParseInt(key, 10, 64) i, _ := strconv.ParseInt(key, 10, 64)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(i))
namespace = namespace[endIdx+1:]
case reflect.Uint: case reflect.Uint:
i, _ := strconv.ParseUint(key, 10, 0) i, _ := strconv.ParseUint(key, 10, 0)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(uint(i))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(uint(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint8: case reflect.Uint8:
i, _ := strconv.ParseUint(key, 10, 8) i, _ := strconv.ParseUint(key, 10, 8)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(uint8(i))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(uint8(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint16: case reflect.Uint16:
i, _ := strconv.ParseUint(key, 10, 16) i, _ := strconv.ParseUint(key, 10, 16)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(uint16(i))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(uint16(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint32: case reflect.Uint32:
i, _ := strconv.ParseUint(key, 10, 32) i, _ := strconv.ParseUint(key, 10, 32)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(uint32(i))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(uint32(i)))
namespace = namespace[endIdx+1:]
case reflect.Uint64: case reflect.Uint64:
i, _ := strconv.ParseUint(key, 10, 64) i, _ := strconv.ParseUint(key, 10, 64)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(i)), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(i))
namespace = namespace[endIdx+1:]
case reflect.Float32: case reflect.Float32:
f, _ := strconv.ParseFloat(key, 32) f, _ := strconv.ParseFloat(key, 32)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(float32(f))), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(float32(f)))
namespace = namespace[endIdx+1:]
case reflect.Float64: case reflect.Float64:
f, _ := strconv.ParseFloat(key, 64) f, _ := strconv.ParseFloat(key, 64)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(f)), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(f))
namespace = namespace[endIdx+1:]
case reflect.Bool: case reflect.Bool:
b, _ := strconv.ParseBool(key) b, _ := strconv.ParseBool(key)
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(b)), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(b))
namespace = namespace[endIdx+1:]
// reflect.Type = string // reflect.Type = string
default: default:
return v.getStructFieldOKInternal(current.MapIndex(reflect.ValueOf(key)), namespace[endIdx+1:]) val = current.MapIndex(reflect.ValueOf(key))
namespace = namespace[endIdx+1:]
} }
goto BEGIN
} }
// if got here there was more namespace, cannot go any deeper // if got here there was more namespace, cannot go any deeper

@ -310,6 +310,11 @@ OUTER:
v.misc = append(v.misc, '|') v.misc = append(v.misc, '|')
v.misc = append(v.misc, ct.tag...) v.misc = append(v.misc, ct.tag...)
if len(ct.param) > 0 {
v.misc = append(v.misc, '=')
v.misc = append(v.misc, ct.param...)
}
if ct.next == nil || ct.next.typeof != typeOr { // ct.typeof != typeOr if ct.next == nil || ct.next.typeof != typeOr { // ct.typeof != typeOr
// if we get here, no valid 'or' value and no more tags // if we get here, no valid 'or' value and no more tags
@ -373,6 +378,10 @@ OUTER:
v.flField = current v.flField = current
v.flParam = ct.param v.flParam = ct.param
// // report error interface functions need these
// v.ns = ns
// v.actualNs = structNs
if !ct.fn(v) { if !ct.fn(v) {
v.str1 = string(append(ns, cf.altName...)) v.str1 = string(append(ns, cf.altName...))

@ -5504,7 +5504,7 @@ func TestOrTag(t *testing.T) {
s = "this ain't right" s = "this ain't right"
errs = validate.Var(s, "rgb|rgba|len=10") errs = validate.Var(s, "rgb|rgba|len=10")
NotEqual(t, errs, nil) NotEqual(t, errs, nil)
AssertError(t, errs, "", "", "", "", "rgb|rgba|len") AssertError(t, errs, "", "", "", "", "rgb|rgba|len=10")
s = "this is right" s = "this is right"
errs = validate.Var(s, "rgb|rgba|len=13") errs = validate.Var(s, "rgb|rgba|len=13")

Loading…
Cancel
Save