diff --git a/README.md b/README.md index e960c23..0734393 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Package validator ================ [![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) [![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) diff --git a/translations/en/en.go b/translations/en/en.go index bc6b3ac..1c8c2fe 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1247,6 +1247,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} must contain a valid MAC address", override: false, }, + { + tag: "iscolor", + translation: "{0} must be a valid color", + override: false, + }, } for _, t := range translations { diff --git a/translations/en/en_test.go b/translations/en/en_test.go index 05453fe..4203335 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -128,6 +128,7 @@ func TestTranslations(t *testing.T) { 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 MAC string `validate:"mac"` + IsColor string `validate:"iscolor"` } var test Test @@ -180,6 +181,10 @@ func TestTranslations(t *testing.T) { ns string expected string }{ + { + ns: "Test.IsColor", + expected: "IsColor must be a valid color", + }, { ns: "Test.MAC", expected: "MAC must contain a valid MAC address", diff --git a/util.go b/util.go index 7711428..a01d4b1 100644 --- a/util.go +++ b/util.go @@ -11,6 +11,7 @@ import ( // underlying value and it's kind. func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) { +BEGIN: switch current.Kind() { case reflect.Ptr: @@ -20,7 +21,8 @@ func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (re return current, reflect.Ptr, nullable } - return v.extractTypeInternal(current.Elem(), nullable) + current = current.Elem() + goto BEGIN case reflect.Interface: @@ -30,7 +32,8 @@ func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (re return current, reflect.Interface, nullable } - return v.extractTypeInternal(current.Elem(), nullable) + current = current.Elem() + goto BEGIN case reflect.Invalid: return current, reflect.Invalid, nullable @@ -40,7 +43,8 @@ func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (re if v.v.hasCustomFuncs { 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 // 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 { - return current, kind, false + return } if namespace == "" { - return current, kind, true + found = true + return } switch kind { case reflect.Ptr, reflect.Interface: - - return current, kind, false + return case reflect.Struct: @@ -95,9 +100,9 @@ func (v *validate) getStructFieldOKInternal(current reflect.Value, namespace str ns = namespace[bracketIdx:] } - current = current.FieldByName(fld) - - return v.getStructFieldOKInternal(current, ns) + val = current.FieldByName(fld) + namespace = ns + goto BEGIN } 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: idx := strings.Index(namespace, leftBracket) + 1 @@ -137,48 +144,76 @@ func (v *validate) getStructFieldOKInternal(current reflect.Value, namespace str switch current.Type().Key().Kind() { case reflect.Int: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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: 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 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 diff --git a/validator.go b/validator.go index 6622b98..d9f2f17 100644 --- a/validator.go +++ b/validator.go @@ -310,6 +310,11 @@ OUTER: v.misc = append(v.misc, '|') 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 we get here, no valid 'or' value and no more tags @@ -373,6 +378,10 @@ OUTER: v.flField = current v.flParam = ct.param + // // report error interface functions need these + // v.ns = ns + // v.actualNs = structNs + if !ct.fn(v) { v.str1 = string(append(ns, cf.altName...)) diff --git a/validator_test.go b/validator_test.go index daa166c..89f5ab8 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5504,7 +5504,7 @@ func TestOrTag(t *testing.T) { s = "this ain't right" errs = validate.Var(s, "rgb|rgba|len=10") NotEqual(t, errs, nil) - AssertError(t, errs, "", "", "", "", "rgb|rgba|len") + AssertError(t, errs, "", "", "", "", "rgb|rgba|len=10") s = "this is right" errs = validate.Var(s, "rgb|rgba|len=13")