From cc25246f0183e1ea9aa7e0d92090d73ad3095dd7 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 29 Sep 2019 13:39:27 -0700 Subject: [PATCH 1/4] fix required_without_* --- baked_in.go | 58 ++++++++++++----------------- cache.go | 37 +++++++++---------- field_level.go | 10 +++++ validator.go | 48 ++++++++++++------------ validator_instance.go | 85 +++++++++++++++++++++++++------------------ validator_test.go | 54 +++++++++++++++++++-------- 6 files changed, 164 insertions(+), 128 deletions(-) diff --git a/baked_in.go b/baked_in.go index 338cddd..a2660ef 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1301,26 +1301,35 @@ func isDefault(fl FieldLevel) bool { // HasValue is the validation function for validating if the current field's value is not the default static value. func hasValue(fl FieldLevel) bool { - return requireCheckFieldKind(fl, "") + field := fl.Field() + switch field.Kind() { + case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: + return !field.IsNil() + default: + if fl.(*validate).fldIsPointer && field.Interface() != nil { + return true + } + return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() + } } // requireCheckField is a func for check field kind func requireCheckFieldKind(fl FieldLevel, param string) bool { field := fl.Field() + var ok bool + kind := field.Kind() if len(param) > 0 { - if fl.Parent().Kind() == reflect.Ptr { - field = fl.Parent().Elem().FieldByName(param) - } else { - field = fl.Parent().FieldByName(param) + field, kind, ok = fl.GetStructFieldOKAdvanced(fl.Parent(), param) + if !ok { + return true } } - switch field.Kind() { + switch kind { + case reflect.Invalid: + return true case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: return !field.IsNil() default: - if fl.(*validate).fldIsPointer && field.Interface() != nil { - return true - } return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface() } } @@ -1328,76 +1337,55 @@ func requireCheckFieldKind(fl FieldLevel, param string) bool { // RequiredWith is the validation function // The field under validation must be present and not empty only if any of the other specified fields are present. func requiredWith(fl FieldLevel) bool { - params := parseOneOfParam2(fl.Param()) for _, param := range params { - if requireCheckFieldKind(fl, param) { return requireCheckFieldKind(fl, "") } } - return true } // RequiredWithAll is the validation function // The field under validation must be present and not empty only if all of the other specified fields are present. func requiredWithAll(fl FieldLevel) bool { - isValidateCurrentField := true params := parseOneOfParam2(fl.Param()) for _, param := range params { if !requireCheckFieldKind(fl, param) { isValidateCurrentField = false + break } } - if isValidateCurrentField { return requireCheckFieldKind(fl, "") } - return true } // 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 { - - isValidateCurrentField := false params := parseOneOfParam2(fl.Param()) for _, param := range params { - - if requireCheckFieldKind(fl, param) { - isValidateCurrentField = true + if !requireCheckFieldKind(fl, param) { + return hasValue(fl) } } - - if !isValidateCurrentField { - return requireCheckFieldKind(fl, "") - } - return true } // RequiredWithoutAll is the validation function // The field under validation must be present and not empty only when all of the other specified fields are not present. func requiredWithoutAll(fl FieldLevel) bool { - - isValidateCurrentField := true params := parseOneOfParam2(fl.Param()) for _, param := range params { - if requireCheckFieldKind(fl, param) { - isValidateCurrentField = false + return true } } - - if isValidateCurrentField { - return requireCheckFieldKind(fl, "") - } - - return true + return hasValue(fl) } // IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value. diff --git a/cache.go b/cache.go index a7a4202..8276504 100644 --- a/cache.go +++ b/cache.go @@ -87,18 +87,19 @@ type cField struct { } type cTag struct { - tag string - aliasTag string - actualAliasTag string - param string - keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation - next *cTag - fn FuncCtx - typeof tagType - hasTag bool - hasAlias bool - hasParam bool // true if parameter used eg. eq= where the equal sign has been set - isBlockEnd bool // indicates the current tag represents the last validation in the block + tag string + aliasTag string + actualAliasTag string + param string + keys *cTag // only populated when using tag's 'keys' and 'endkeys' for map key validation + next *cTag + fn FuncCtx + typeof tagType + hasTag bool + hasAlias bool + hasParam bool // true if parameter used eg. eq= where the equal sign has been set + isBlockEnd bool // indicates the current tag represents the last validation in the block + runValidationWhenNil bool } func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { @@ -141,9 +142,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr customName = fld.Name if v.hasTagNameFunc { - name := v.tagNameFunc(fld) - if len(name) > 0 { customName = name } @@ -168,16 +167,13 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr namesEqual: fld.Name == customName, }) } - v.structCache.Set(typ, cs) - return cs } func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { var t string - var ok bool noAlias := len(alias) == 0 tags := strings.Split(tag, tagSeparator) @@ -270,11 +266,9 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s continue default: - if t == isdefault { current.typeof = typeIsDefault } - // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" orVals := strings.Split(t, orSeparator) @@ -300,7 +294,10 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName))) } - if current.fn, ok = v.validations[current.tag]; !ok { + if wrapper, ok := v.validations[current.tag]; ok { + current.fn = wrapper.fn + current.runValidationWhenNil = wrapper.runValidatinOnNil + } else { panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, current.tag, fieldName))) } diff --git a/field_level.go b/field_level.go index cbfbc15..24bc134 100644 --- a/field_level.go +++ b/field_level.go @@ -38,6 +38,10 @@ 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. 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. + GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) } var _ FieldLevel = new(validate) @@ -67,3 +71,9 @@ func (v *validate) Param() string { func (v *validate) GetStructFieldOK() (reflect.Value, reflect.Kind, 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) GetStructFieldOKAdvanced(val reflect.Value, namespace string) (reflect.Value, reflect.Kind, bool) { + return v.getStructFieldOKInternal(val, namespace) +} diff --git a/validator.go b/validator.go index 67473f1..3abf5d3 100644 --- a/validator.go +++ b/validator.go @@ -94,7 +94,6 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) { - var typ reflect.Type var kind reflect.Kind @@ -112,16 +111,36 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr } if ct.hasTag { + if kind == reflect.Invalid { + v.str1 = string(append(ns, cf.altName...)) + if v.v.hasTagNameFunc { + v.str2 = string(append(structNs, cf.name...)) + } else { + v.str2 = v.str1 + } + v.errs = append(v.errs, + &fieldError{ + v: v.v, + tag: ct.aliasTag, + actualTag: ct.tag, + ns: v.str1, + structNs: v.str2, + fieldLen: uint8(len(cf.altName)), + structfieldLen: uint8(len(cf.name)), + param: ct.param, + kind: kind, + }, + ) + return + } v.str1 = string(append(ns, cf.altName...)) - if v.v.hasTagNameFunc { v.str2 = string(append(structNs, cf.name...)) } else { v.str2 = v.str1 } - - if kind == reflect.Invalid { + if !ct.runValidationWhenNil { v.errs = append(v.errs, &fieldError{ v: v.v, @@ -131,31 +150,14 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr structNs: v.str2, fieldLen: uint8(len(cf.altName)), structfieldLen: uint8(len(cf.name)), + value: current.Interface(), param: ct.param, kind: kind, + typ: current.Type(), }, ) - return } - - v.errs = append(v.errs, - &fieldError{ - v: v.v, - tag: ct.aliasTag, - actualTag: ct.tag, - ns: v.str1, - structNs: v.str2, - fieldLen: uint8(len(cf.altName)), - structfieldLen: uint8(len(cf.name)), - value: current.Interface(), - param: ct.param, - kind: kind, - typ: current.Type(), - }, - ) - - return } case reflect.Struct: diff --git a/validator_instance.go b/validator_instance.go index fc9db5a..4a89d40 100644 --- a/validator_instance.go +++ b/validator_instance.go @@ -13,27 +13,31 @@ import ( ) const ( - defaultTagName = "validate" - utf8HexComma = "0x2C" - utf8Pipe = "0x7C" - tagSeparator = "," - orSeparator = "|" - tagKeySeparator = "=" - structOnlyTag = "structonly" - noStructLevelTag = "nostructlevel" - omitempty = "omitempty" - isdefault = "isdefault" - skipValidationTag = "-" - diveTag = "dive" - keysTag = "keys" - endKeysTag = "endkeys" - requiredTag = "required" - namespaceSeparator = "." - leftBracket = "[" - rightBracket = "]" - restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" - restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" - restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" + defaultTagName = "validate" + utf8HexComma = "0x2C" + utf8Pipe = "0x7C" + tagSeparator = "," + orSeparator = "|" + tagKeySeparator = "=" + structOnlyTag = "structonly" + noStructLevelTag = "nostructlevel" + omitempty = "omitempty" + isdefault = "isdefault" + requiredWithoutAllTag = "required_without_all" + requiredWithoutTag = "required_without" + requiredWithTag = "required_with" + requiredWithAllTag = "required_with_all" + skipValidationTag = "-" + diveTag = "dive" + keysTag = "keys" + endKeysTag = "endkeys" + requiredTag = "required" + namespaceSeparator = "." + leftBracket = "[" + rightBracket = "]" + restrictedTagChars = ".[],|=+()`~!@#$%^&*\\\"/?<>{}" + restrictedAliasErr = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" + restrictedTagErr = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation" ) var ( @@ -55,6 +59,11 @@ type CustomTypeFunc func(field reflect.Value) interface{} // TagNameFunc allows for adding of a custom tag name parser type TagNameFunc func(field reflect.StructField) string +type internalValidationFuncWrapper struct { + fn FuncCtx + runValidatinOnNil bool +} + // Validate contains the validator settings and cache type Validate struct { tagName string @@ -65,7 +74,7 @@ type Validate struct { structLevelFuncs map[reflect.Type]StructLevelFuncCtx customFuncs map[reflect.Type]CustomTypeFunc aliases map[string]string - validations map[string]FuncCtx + validations map[string]internalValidationFuncWrapper transTagFunc map[ut.Translator]map[string]TranslationFunc // map[]map[]TranslationFunc tagCache *tagCache structCache *structCache @@ -83,7 +92,7 @@ func New() *Validate { v := &Validate{ tagName: defaultTagName, aliases: make(map[string]string, len(bakedInAliases)), - validations: make(map[string]FuncCtx, len(bakedInValidators)), + validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)), tagCache: tc, structCache: sc, } @@ -96,8 +105,14 @@ func New() *Validate { // must copy validators for separate validations to be used in each instance for k, val := range bakedInValidators { - // no need to error check here, baked in will always be valid - _ = v.registerValidation(k, wrapFunc(val), true) + switch k { + // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour + case requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag: + _ = v.registerValidation(k, wrapFunc(val), true, true) + default: + // no need to error check here, baked in will always be valid + _ = v.registerValidation(k, wrapFunc(val), true, false) + } } v.pool = &sync.Pool{ @@ -140,18 +155,21 @@ func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) { // NOTES: // - if the key already exists, the previous validation function will be replaced. // - this method is not thread-safe it is intended that these all be registered prior to any validation -func (v *Validate) RegisterValidation(tag string, fn Func) error { - return v.RegisterValidationCtx(tag, wrapFunc(fn)) +func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error { + return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...) } // RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation // allowing context.Context validation support. -func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx) error { - return v.registerValidation(tag, fn, false) +func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error { + var nilCheckable bool + if len(callValidationEvenIfNull) > 0 { + nilCheckable = callValidationEvenIfNull[0] + } + return v.registerValidation(tag, fn, false, nilCheckable) } -func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool) error { - +func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error { if len(tag) == 0 { return errors.New("Function Key cannot be empty") } @@ -161,13 +179,10 @@ func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool) erro } _, ok := restrictedTags[tag] - if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) { panic(fmt.Sprintf(restrictedTagErr, tag)) } - - v.validations[tag] = fn - + v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable} return nil } diff --git a/validator_test.go b/validator_test.go index a8ccfaa..4844537 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8677,14 +8677,20 @@ func TestRequiredWithAll(t *testing.T) { func TestRequiredWithout(t *testing.T) { + type Inner struct { + Field *string + } + fieldVal := "test" test := struct { + Inner *Inner Field1 string `validate:"omitempty" json:"field_1"` Field2 *string `validate:"required_without=Field1" json:"field_2"` Field3 map[string]string `validate:"required_without=Field2" json:"field_3"` Field4 interface{} `validate:"required_without=Field3" json:"field_4"` Field5 string `validate:"required_without=Field3" json:"field_5"` }{ + Inner: &Inner{Field: &fieldVal}, Field2: &fieldVal, Field3: map[string]string{"key": "val"}, Field4: "test", @@ -8694,29 +8700,35 @@ func TestRequiredWithout(t *testing.T) { validate := New() errs := validate.Struct(test) - - if errs != nil { - t.Fatalf("failed Error: %s", errs) - } + Equal(t, errs, nil) test2 := struct { - Field1 string `validate:"omitempty" json:"field_1"` + Inner *Inner + Inner2 *Inner + Field1 string `json:"field_1"` Field2 *string `validate:"required_without=Field1" json:"field_2"` Field3 map[string]string `validate:"required_without=Field2" json:"field_3"` Field4 interface{} `validate:"required_without=Field3" json:"field_4"` Field5 string `validate:"required_without=Field3" json:"field_5"` Field6 string `validate:"required_without=Field1" json:"field_6"` + Field7 string `validate:"required_without=Inner.Field" json:"field_7"` + Field8 string `validate:"required_without=Inner.Field" json:"field_8"` }{ + Inner: &Inner{}, Field3: map[string]string{"key": "val"}, Field4: "test", Field5: "test", } errs = validate.Struct(&test2) + NotEqual(t, errs, nil) - if errs == nil { - t.Fatalf("failed Error: %s", errs) - } + ve := errs.(ValidationErrors) + Equal(t, len(ve), 4) + AssertError(t, errs, "Field2", "Field2", "Field2", "Field2", "required_without") + AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_without") + AssertError(t, errs, "Field7", "Field7", "Field7", "Field7", "required_without") + AssertError(t, errs, "Field8", "Field8", "Field8", "Field8", "required_without") } func TestRequiredWithoutAll(t *testing.T) { @@ -8739,10 +8751,7 @@ func TestRequiredWithoutAll(t *testing.T) { validate := New() errs := validate.Struct(test) - - if errs != nil { - t.Fatalf("failed Error: %s", errs) - } + Equal(t, errs, nil) test2 := struct { Field1 string `validate:"omitempty" json:"field_1"` @@ -8750,7 +8759,7 @@ func TestRequiredWithoutAll(t *testing.T) { Field3 map[string]string `validate:"required_without_all=Field2" json:"field_3"` Field4 interface{} `validate:"required_without_all=Field3" json:"field_4"` Field5 string `validate:"required_without_all=Field3" json:"field_5"` - Field6 string `validate:"required_without_all=Field1" json:"field_6"` + Field6 string `validate:"required_without_all=Field1 Field3" json:"field_6"` }{ Field3: map[string]string{"key": "val"}, Field4: "test", @@ -8758,8 +8767,23 @@ func TestRequiredWithoutAll(t *testing.T) { } errs = validate.Struct(test2) + NotEqual(t, errs, nil) - if errs == nil { - t.Fatalf("failed Error: %s", errs) + ve := errs.(ValidationErrors) + Equal(t, len(ve), 1) + AssertError(t, errs, "Field2", "Field2", "Field2", "Field2", "required_without_all") +} + +func TestLookup(t *testing.T) { + type Lookup struct { + FieldA *string `json:"fieldA,omitempty" validate:"required_without=FieldB"` + FieldB *string `json:"fieldB,omitempty" validate:"required_without=FieldA"` + } + + fieldAValue := "1232" + lookup := Lookup{ + FieldA: &fieldAValue, + FieldB: nil, } + Equal(t, New().Struct(lookup), nil) } From 6484d9f2fbdaeea8397377e7abe94e894444d80a Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 29 Sep 2019 14:12:27 -0700 Subject: [PATCH 2/4] fix required_with_* --- baked_in.go | 26 +++++++----------- validator_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/baked_in.go b/baked_in.go index a2660ef..95d613c 100644 --- a/baked_in.go +++ b/baked_in.go @@ -1314,19 +1314,19 @@ func hasValue(fl FieldLevel) bool { } // requireCheckField is a func for check field kind -func requireCheckFieldKind(fl FieldLevel, param string) bool { +func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue bool) bool { field := fl.Field() var ok bool kind := field.Kind() if len(param) > 0 { field, kind, ok = fl.GetStructFieldOKAdvanced(fl.Parent(), param) if !ok { - return true + return defaultNotFoundValue } } switch kind { case reflect.Invalid: - return true + return defaultNotFoundValue case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func: return !field.IsNil() default: @@ -1339,8 +1339,8 @@ func requireCheckFieldKind(fl FieldLevel, param string) bool { func requiredWith(fl FieldLevel) bool { params := parseOneOfParam2(fl.Param()) for _, param := range params { - if requireCheckFieldKind(fl, param) { - return requireCheckFieldKind(fl, "") + if requireCheckFieldKind(fl, param, false) { + return hasValue(fl) } } return true @@ -1349,19 +1349,13 @@ func requiredWith(fl FieldLevel) bool { // RequiredWithAll is the validation function // The field under validation must be present and not empty only if all of the other specified fields are present. func requiredWithAll(fl FieldLevel) bool { - isValidateCurrentField := true params := parseOneOfParam2(fl.Param()) for _, param := range params { - - if !requireCheckFieldKind(fl, param) { - isValidateCurrentField = false - break + if !requireCheckFieldKind(fl, param, false) { + return true } } - if isValidateCurrentField { - return requireCheckFieldKind(fl, "") - } - return true + return hasValue(fl) } // RequiredWithout is the validation function @@ -1369,7 +1363,7 @@ func requiredWithAll(fl FieldLevel) bool { func requiredWithout(fl FieldLevel) bool { params := parseOneOfParam2(fl.Param()) for _, param := range params { - if !requireCheckFieldKind(fl, param) { + if !requireCheckFieldKind(fl, param, true) { return hasValue(fl) } } @@ -1381,7 +1375,7 @@ func requiredWithout(fl FieldLevel) bool { func requiredWithoutAll(fl FieldLevel) bool { params := parseOneOfParam2(fl.Param()) for _, param := range params { - if requireCheckFieldKind(fl, param) { + if requireCheckFieldKind(fl, param, true) { return true } } diff --git a/validator_test.go b/validator_test.go index 4844537..aab64ac 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8621,17 +8621,22 @@ func TestEndsWithValidation(t *testing.T) { } func TestRequiredWith(t *testing.T) { + type Inner struct { + Field *string + } + fieldVal := "test" test := struct { + Inner *Inner FieldE string `validate:"omitempty" json:"field_e"` FieldER string `validate:"required_with=FieldE" json:"field_er"` Field1 string `validate:"omitempty" json:"field_1"` Field2 *string `validate:"required_with=Field1" json:"field_2"` Field3 map[string]string `validate:"required_with=Field2" json:"field_3"` Field4 interface{} `validate:"required_with=Field3" json:"field_4"` - Field5 string `validate:"required_with=Field3" json:"field_5"` + Field5 string `validate:"required_with=Inner.Field" json:"field_5"` }{ - Field1: "test_field1", + Inner: &Inner{Field: &fieldVal}, Field2: &fieldVal, Field3: map[string]string{"key": "val"}, Field4: "test", @@ -8641,24 +8646,52 @@ func TestRequiredWith(t *testing.T) { validate := New() errs := validate.Struct(test) + Equal(t, errs, nil) - if errs != nil { - t.Fatalf("failed Error: %s", errs) + test2 := struct { + Inner *Inner + Inner2 *Inner + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"required_with=FieldE" json:"field_er"` + Field1 string `validate:"omitempty" json:"field_1"` + Field2 *string `validate:"required_with=Field1" json:"field_2"` + Field3 map[string]string `validate:"required_with=Field2" json:"field_3"` + Field4 interface{} `validate:"required_with=Field2" json:"field_4"` + Field5 string `validate:"required_with=Field3" json:"field_5"` + Field6 string `validate:"required_with=Inner.Field" json:"field_6"` + Field7 string `validate:"required_with=Inner2.Field" json:"field_7"` + }{ + Inner: &Inner{Field: &fieldVal}, + Field2: &fieldVal, } + + errs = validate.Struct(test2) + NotEqual(t, errs, nil) + + ve := errs.(ValidationErrors) + Equal(t, len(ve), 3) + AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_with") + AssertError(t, errs, "Field4", "Field4", "Field4", "Field4", "required_with") + AssertError(t, errs, "Field6", "Field6", "Field6", "Field6", "required_with") } func TestRequiredWithAll(t *testing.T) { + type Inner struct { + Field *string + } fieldVal := "test" test := struct { + Inner *Inner FieldE string `validate:"omitempty" json:"field_e"` FieldER string `validate:"required_with_all=FieldE" json:"field_er"` Field1 string `validate:"omitempty" json:"field_1"` Field2 *string `validate:"required_with_all=Field1" json:"field_2"` Field3 map[string]string `validate:"required_with_all=Field2" json:"field_3"` Field4 interface{} `validate:"required_with_all=Field3" json:"field_4"` - Field5 string `validate:"required_with_all=Field3" json:"field_5"` + Field5 string `validate:"required_with_all=Inner.Field" json:"field_5"` }{ + Inner: &Inner{Field: &fieldVal}, Field1: "test_field1", Field2: &fieldVal, Field3: map[string]string{"key": "val"}, @@ -8669,10 +8702,31 @@ func TestRequiredWithAll(t *testing.T) { validate := New() errs := validate.Struct(test) + Equal(t, errs, nil) - if errs != nil { - t.Fatalf("failed Error: %s", errs) + test2 := struct { + Inner *Inner + Inner2 *Inner + FieldE string `validate:"omitempty" json:"field_e"` + FieldER string `validate:"required_with_all=FieldE" json:"field_er"` + Field1 string `validate:"omitempty" json:"field_1"` + Field2 *string `validate:"required_with_all=Field1" json:"field_2"` + Field3 map[string]string `validate:"required_with_all=Field2" json:"field_3"` + Field4 interface{} `validate:"required_with_all=Field1 FieldE" json:"field_4"` + Field5 string `validate:"required_with_all=Inner.Field Field2" json:"field_5"` + Field6 string `validate:"required_with_all=Inner2.Field Field2" json:"field_6"` + }{ + Inner: &Inner{Field: &fieldVal}, + Field2: &fieldVal, } + + errs = validate.Struct(test2) + NotEqual(t, errs, nil) + + ve := errs.(ValidationErrors) + Equal(t, len(ve), 2) + AssertError(t, errs, "Field3", "Field3", "Field3", "Field3", "required_with_all") + AssertError(t, errs, "Field5", "Field5", "Field5", "Field5", "required_with_all") } func TestRequiredWithout(t *testing.T) { From 06f92248de55bcde25539c27323406f542474bf5 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 29 Sep 2019 14:55:11 -0700 Subject: [PATCH 3/4] linting cleanup --- Makefile | 8 +- benchmarks_test.go | 235 ++++++++----------------------- cache.go | 14 +- translations/ja/ja.go | 2 - translations/zh/zh.go | 19 +-- translations/zh/zh_test.go | 4 +- translations/zh_tw/zh_tw.go | 21 ++- translations/zh_tw/zh_tw_test.go | 4 +- validator_test.go | 134 +++++++++--------- 9 files changed, 157 insertions(+), 284 deletions(-) diff --git a/Makefile b/Makefile index aeeee9d..b912cae 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,13 @@ GOCMD=go linters-install: - $(GOCMD) get -u github.com/alecthomas/gometalinter - gometalinter --install + @golangci-lint --version >/dev/null 2>&1 || { \ + echo "installing linting tools..."; \ + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.19.1; \ + } lint: linters-install - gometalinter --vendor --disable-all --enable=vet --enable=vetshadow --enable=golint --enable=maligned --enable=megacheck --enable=ineffassign --enable=misspell --enable=errcheck --enable=goconst ./... + golangci-lint run test: $(GOCMD) test -cover -race ./... diff --git a/benchmarks_test.go b/benchmarks_test.go index 5ac871f..ee70f95 100644 --- a/benchmarks_test.go +++ b/benchmarks_test.go @@ -8,236 +8,200 @@ import ( ) func BenchmarkFieldSuccess(b *testing.B) { - validate := New() - s := "1" b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(&s, "len=1") + _ = validate.Var(&s, "len=1") } } func BenchmarkFieldSuccessParallel(b *testing.B) { - validate := New() - s := "1" b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(&s, "len=1") + _ = validate.Var(&s, "len=1") } }) } func BenchmarkFieldFailure(b *testing.B) { - validate := New() - s := "12" b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(&s, "len=1") + _ = validate.Var(&s, "len=1") } } func BenchmarkFieldFailureParallel(b *testing.B) { - validate := New() - s := "12" b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(&s, "len=1") + _ = validate.Var(&s, "len=1") } }) } func BenchmarkFieldArrayDiveSuccess(b *testing.B) { - validate := New() - m := []string{"val1", "val2", "val3"} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } } func BenchmarkFieldArrayDiveSuccessParallel(b *testing.B) { - validate := New() - m := []string{"val1", "val2", "val3"} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } }) } func BenchmarkFieldArrayDiveFailure(b *testing.B) { - validate := New() - m := []string{"val1", "", "val3"} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } } func BenchmarkFieldArrayDiveFailureParallel(b *testing.B) { - validate := New() - m := []string{"val1", "", "val3"} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } }) } func BenchmarkFieldMapDiveSuccess(b *testing.B) { - validate := New() - m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } } func BenchmarkFieldMapDiveSuccessParallel(b *testing.B) { - validate := New() - m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } }) } func BenchmarkFieldMapDiveFailure(b *testing.B) { - validate := New() - m := map[string]string{"": "", "val3": "val3"} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } } func BenchmarkFieldMapDiveFailureParallel(b *testing.B) { - validate := New() - m := map[string]string{"": "", "val3": "val3"} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(m, "required,dive,required") + _ = validate.Var(m, "required,dive,required") } }) } func BenchmarkFieldMapDiveWithKeysSuccess(b *testing.B) { - validate := New() - m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(m, "required,dive,keys,required,endkeys,required") + _ = validate.Var(m, "required,dive,keys,required,endkeys,required") } } func BenchmarkFieldMapDiveWithKeysSuccessParallel(b *testing.B) { - validate := New() - m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(m, "required,dive,keys,required,endkeys,required") + _ = validate.Var(m, "required,dive,keys,required,endkeys,required") } }) } func BenchmarkFieldMapDiveWithKeysFailure(b *testing.B) { - validate := New() - m := map[string]string{"": "", "val3": "val3"} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(m, "required,dive,keys,required,endkeys,required") + _ = validate.Var(m, "required,dive,keys,required,endkeys,required") } } func BenchmarkFieldMapDiveWithKeysFailureParallel(b *testing.B) { - validate := New() - m := map[string]string{"": "", "val3": "val3"} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(m, "required,dive,keys,required,endkeys,required") + _ = validate.Var(m, "required,dive,keys,required,endkeys,required") } }) } func BenchmarkFieldCustomTypeSuccess(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) - val := valuer{ Name: "1", } b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(val, "len=1") + _ = validate.Var(val, "len=1") } } func BenchmarkFieldCustomTypeSuccessParallel(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) - val := valuer{ Name: "1", } @@ -245,93 +209,80 @@ func BenchmarkFieldCustomTypeSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(val, "len=1") + _ = validate.Var(val, "len=1") } }) } func BenchmarkFieldCustomTypeFailure(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) - val := valuer{} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(val, "len=1") + _ = validate.Var(val, "len=1") } } func BenchmarkFieldCustomTypeFailureParallel(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) - val := valuer{} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(val, "len=1") + _ = validate.Var(val, "len=1") } }) } func BenchmarkFieldOrTagSuccess(b *testing.B) { - validate := New() - s := "rgba(0,0,0,1)" b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(s, "rgb|rgba") + _ = validate.Var(s, "rgb|rgba") } } func BenchmarkFieldOrTagSuccessParallel(b *testing.B) { - validate := New() - s := "rgba(0,0,0,1)" b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(s, "rgb|rgba") + _ = validate.Var(s, "rgb|rgba") } }) } func BenchmarkFieldOrTagFailure(b *testing.B) { - validate := New() - s := "#000" b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Var(s, "rgb|rgba") + _ = validate.Var(s, "rgb|rgba") } } func BenchmarkFieldOrTagFailureParallel(b *testing.B) { - validate := New() - s := "#000" b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Var(s, "rgb|rgba") + _ = validate.Var(s, "rgb|rgba") } }) } func BenchmarkStructLevelValidationSuccess(b *testing.B) { - validate := New() validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) @@ -341,12 +292,11 @@ func BenchmarkStructLevelValidationSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(tst) + _ = validate.Struct(tst) } } func BenchmarkStructLevelValidationSuccessParallel(b *testing.B) { - validate := New() validate.RegisterStructValidation(StructValidationTestStructSuccess, TestStruct{}) @@ -357,13 +307,12 @@ func BenchmarkStructLevelValidationSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(tst) + _ = validate.Struct(tst) } }) } func BenchmarkStructLevelValidationFailure(b *testing.B) { - validate := New() validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{}) @@ -373,12 +322,11 @@ func BenchmarkStructLevelValidationFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(tst) + _ = validate.Struct(tst) } } func BenchmarkStructLevelValidationFailureParallel(b *testing.B) { - validate := New() validate.RegisterStructValidation(StructValidationTestStruct, TestStruct{}) @@ -389,13 +337,12 @@ func BenchmarkStructLevelValidationFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(tst) + _ = validate.Struct(tst) } }) } func BenchmarkStructSimpleCustomTypeSuccess(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) @@ -412,15 +359,13 @@ func BenchmarkStructSimpleCustomTypeSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(validFoo) + _ = validate.Struct(validFoo) } } func BenchmarkStructSimpleCustomTypeSuccessParallel(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) - val := valuer{ Name: "1", } @@ -429,19 +374,17 @@ func BenchmarkStructSimpleCustomTypeSuccessParallel(b *testing.B) { Valuer valuer `validate:"len=1"` IntValue int `validate:"min=5,max=10"` } - validFoo := &Foo{Valuer: val, IntValue: 7} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(validFoo) + _ = validate.Struct(validFoo) } }) } func BenchmarkStructSimpleCustomTypeFailure(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) @@ -451,17 +394,15 @@ func BenchmarkStructSimpleCustomTypeFailure(b *testing.B) { Valuer valuer `validate:"len=1"` IntValue int `validate:"min=5,max=10"` } - validFoo := &Foo{Valuer: val, IntValue: 3} b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(validFoo) + _ = validate.Struct(validFoo) } } func BenchmarkStructSimpleCustomTypeFailureParallel(b *testing.B) { - validate := New() validate.RegisterCustomTypeFunc(ValidateValuerType, (*sql.Valuer)(nil), valuer{}) @@ -471,19 +412,17 @@ func BenchmarkStructSimpleCustomTypeFailureParallel(b *testing.B) { Valuer valuer `validate:"len=1"` IntValue int `validate:"min=5,max=10"` } - validFoo := &Foo{Valuer: val, IntValue: 3} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(validate.Struct(validFoo)) + _ = validate.Struct(validate.Struct(validFoo)) } }) } func BenchmarkStructFilteredSuccess(b *testing.B) { - validate := New() type Test struct { @@ -494,21 +433,18 @@ func BenchmarkStructFilteredSuccess(b *testing.B) { test := &Test{ Name: "Joey Bloggs", } - byts := []byte("Name") - fn := func(ns []byte) bool { return !bytes.HasSuffix(ns, byts) } b.ResetTimer() for n := 0; n < b.N; n++ { - validate.StructFiltered(test, fn) + _ = validate.StructFiltered(test, fn) } } func BenchmarkStructFilteredSuccessParallel(b *testing.B) { - validate := New() type Test struct { @@ -519,9 +455,7 @@ func BenchmarkStructFilteredSuccessParallel(b *testing.B) { test := &Test{ Name: "Joey Bloggs", } - byts := []byte("Name") - fn := func(ns []byte) bool { return !bytes.HasSuffix(ns, byts) } @@ -529,13 +463,12 @@ func BenchmarkStructFilteredSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.StructFiltered(test, fn) + _ = validate.StructFiltered(test, fn) } }) } func BenchmarkStructFilteredFailure(b *testing.B) { - validate := New() type Test struct { @@ -555,12 +488,11 @@ func BenchmarkStructFilteredFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.StructFiltered(test, fn) + _ = validate.StructFiltered(test, fn) } } func BenchmarkStructFilteredFailureParallel(b *testing.B) { - validate := New() type Test struct { @@ -571,9 +503,7 @@ func BenchmarkStructFilteredFailureParallel(b *testing.B) { test := &Test{ Name: "Joey Bloggs", } - byts := []byte("NickName") - fn := func(ns []byte) bool { return !bytes.HasSuffix(ns, byts) } @@ -581,13 +511,12 @@ func BenchmarkStructFilteredFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.StructFiltered(test, fn) + _ = validate.StructFiltered(test, fn) } }) } func BenchmarkStructPartialSuccess(b *testing.B) { - validate := New() type Test struct { @@ -601,12 +530,11 @@ func BenchmarkStructPartialSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.StructPartial(test, "Name") + _ = validate.StructPartial(test, "Name") } } func BenchmarkStructPartialSuccessParallel(b *testing.B) { - validate := New() type Test struct { @@ -621,13 +549,12 @@ func BenchmarkStructPartialSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.StructPartial(test, "Name") + _ = validate.StructPartial(test, "Name") } }) } func BenchmarkStructPartialFailure(b *testing.B) { - validate := New() type Test struct { @@ -641,12 +568,11 @@ func BenchmarkStructPartialFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.StructPartial(test, "NickName") + _ = validate.StructPartial(test, "NickName") } } func BenchmarkStructPartialFailureParallel(b *testing.B) { - validate := New() type Test struct { @@ -661,13 +587,12 @@ func BenchmarkStructPartialFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.StructPartial(test, "NickName") + _ = validate.StructPartial(test, "NickName") } }) } func BenchmarkStructExceptSuccess(b *testing.B) { - validate := New() type Test struct { @@ -681,12 +606,11 @@ func BenchmarkStructExceptSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.StructExcept(test, "Nickname") + _ = validate.StructExcept(test, "Nickname") } } func BenchmarkStructExceptSuccessParallel(b *testing.B) { - validate := New() type Test struct { @@ -701,13 +625,12 @@ func BenchmarkStructExceptSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.StructExcept(test, "NickName") + _ = validate.StructExcept(test, "NickName") } }) } func BenchmarkStructExceptFailure(b *testing.B) { - validate := New() type Test struct { @@ -721,12 +644,11 @@ func BenchmarkStructExceptFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.StructExcept(test, "Name") + _ = validate.StructExcept(test, "Name") } } func BenchmarkStructExceptFailureParallel(b *testing.B) { - validate := New() type Test struct { @@ -741,13 +663,12 @@ func BenchmarkStructExceptFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.StructExcept(test, "Name") + _ = validate.StructExcept(test, "Name") } }) } func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) { - validate := New() type Test struct { @@ -757,7 +678,6 @@ func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) { now := time.Now().UTC() then := now.Add(time.Hour * 5) - test := &Test{ Start: now, End: then, @@ -765,12 +685,11 @@ func BenchmarkStructSimpleCrossFieldSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(test) + _ = validate.Struct(test) } } func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) { - validate := New() type Test struct { @@ -780,7 +699,6 @@ func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) { now := time.Now().UTC() then := now.Add(time.Hour * 5) - test := &Test{ Start: now, End: then, @@ -789,13 +707,12 @@ func BenchmarkStructSimpleCrossFieldSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(test) + _ = validate.Struct(test) } }) } func BenchmarkStructSimpleCrossFieldFailure(b *testing.B) { - validate := New() type Test struct { @@ -813,12 +730,11 @@ func BenchmarkStructSimpleCrossFieldFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(test) + _ = validate.Struct(test) } } func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) { - validate := New() type Test struct { @@ -828,7 +744,6 @@ func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) { now := time.Now().UTC() then := now.Add(time.Hour * -5) - test := &Test{ Start: now, End: then, @@ -836,13 +751,12 @@ func BenchmarkStructSimpleCrossFieldFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(test) + _ = validate.Struct(test) } }) } func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) { - validate := New() type Inner struct { @@ -855,11 +769,9 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) { } now := time.Now().UTC() - inner := &Inner{ Start: now, } - outer := &Outer{ Inner: inner, CreatedAt: now, @@ -867,12 +779,11 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(outer) + _ = validate.Struct(outer) } } func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) { - validate := New() type Inner struct { @@ -885,11 +796,9 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) { } now := time.Now().UTC() - inner := &Inner{ Start: now, } - outer := &Outer{ Inner: inner, CreatedAt: now, @@ -898,15 +807,13 @@ func BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(outer) + _ = validate.Struct(outer) } }) } func BenchmarkStructSimpleCrossStructCrossFieldFailure(b *testing.B) { - validate := New() - type Inner struct { Start time.Time } @@ -930,12 +837,11 @@ func BenchmarkStructSimpleCrossStructCrossFieldFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(outer) + _ = validate.Struct(outer) } } func BenchmarkStructSimpleCrossStructCrossFieldFailureParallel(b *testing.B) { - validate := New() type Inner struct { @@ -962,15 +868,13 @@ func BenchmarkStructSimpleCrossStructCrossFieldFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(outer) + _ = validate.Struct(outer) } }) } func BenchmarkStructSimpleSuccess(b *testing.B) { - validate := New() - type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` @@ -980,33 +884,28 @@ func BenchmarkStructSimpleSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(validFoo) + _ = validate.Struct(validFoo) } } func BenchmarkStructSimpleSuccessParallel(b *testing.B) { - validate := New() - type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` } - validFoo := &Foo{StringValue: "Foobar", IntValue: 7} b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(validFoo) + _ = validate.Struct(validFoo) } }) } func BenchmarkStructSimpleFailure(b *testing.B) { - validate := New() - type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` @@ -1016,14 +915,12 @@ func BenchmarkStructSimpleFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(invalidFoo) + _ = validate.Struct(invalidFoo) } } func BenchmarkStructSimpleFailureParallel(b *testing.B) { - validate := New() - type Foo struct { StringValue string `validate:"min=5,max=10"` IntValue int `validate:"min=5,max=10"` @@ -1034,15 +931,13 @@ func BenchmarkStructSimpleFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(invalidFoo) + _ = validate.Struct(invalidFoo) } }) } func BenchmarkStructComplexSuccess(b *testing.B) { - validate := New() - tSuccess := &TestString{ Required: "Required", Len: "length==10", @@ -1072,14 +967,12 @@ func BenchmarkStructComplexSuccess(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(tSuccess) + _ = validate.Struct(tSuccess) } } func BenchmarkStructComplexSuccessParallel(b *testing.B) { - validate := New() - tSuccess := &TestString{ Required: "Required", Len: "length==10", @@ -1110,15 +1003,13 @@ func BenchmarkStructComplexSuccessParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(tSuccess) + _ = validate.Struct(tSuccess) } }) } func BenchmarkStructComplexFailure(b *testing.B) { - validate := New() - tFail := &TestString{ Required: "", Len: "", @@ -1145,14 +1036,12 @@ func BenchmarkStructComplexFailure(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - validate.Struct(tFail) + _ = validate.Struct(tFail) } } func BenchmarkStructComplexFailureParallel(b *testing.B) { - validate := New() - tFail := &TestString{ Required: "", Len: "", @@ -1180,7 +1069,7 @@ func BenchmarkStructComplexFailureParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - validate.Struct(tFail) + _ = validate.Struct(tFail) } }) } @@ -1193,7 +1082,7 @@ func BenchmarkOneof(b *testing.B) { w := &TestOneof{Color: "green"} val := New() for i := 0; i < b.N; i++ { - val.Struct(w) + _ = val.Struct(w) } } @@ -1204,7 +1093,7 @@ func BenchmarkOneofParallel(b *testing.B) { b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { - val.Struct(w) + _ = val.Struct(w) } }) } diff --git a/cache.go b/cache.go index 8276504..0d18d6e 100644 --- a/cache.go +++ b/cache.go @@ -39,9 +39,7 @@ func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) { } func (sc *structCache) Set(key reflect.Type, value *cStruct) { - m := sc.m.Load().(map[reflect.Type]*cStruct) - nm := make(map[reflect.Type]*cStruct, len(m)+1) for k, v := range m { nm[k] = v @@ -61,9 +59,7 @@ func (tc *tagCache) Get(key string) (c *cTag, found bool) { } func (tc *tagCache) Set(key string, value *cTag) { - m := tc.m.Load().(map[string]*cTag) - nm := make(map[string]*cTag, len(m)+1) for k, v := range m { nm[k] = v @@ -103,7 +99,6 @@ type cTag struct { } func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStruct { - v.structCache.lock.Lock() defer v.structCache.lock.Unlock() // leave as defer! because if inner panics, it will never get unlocked otherwise! @@ -172,15 +167,12 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr } func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias string, hasAlias bool) (firstCtag *cTag, current *cTag) { - var t string noAlias := len(alias) == 0 tags := strings.Split(tag, tagSeparator) for i := 0; i < len(tags); i++ { - t = tags[i] - if noAlias { alias = t } @@ -194,14 +186,13 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s current.next, current = next, curr } - continue } var prevTag tagType if i == 0 { - current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true} + current = &cTag{aliasTag: alias, hasAlias: hasAlias, hasTag: true, typeof: typeDefault} firstCtag = current } else { prevTag = current.typeof @@ -210,7 +201,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s } switch t { - case diveTag: current.typeof = typeDive continue @@ -273,9 +263,7 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s orVals := strings.Split(t, orSeparator) for j := 0; j < len(orVals); j++ { - vals := strings.SplitN(orVals[j], tagKeySeparator, 2) - if noAlias { alias = vals[0] current.aliasTag = alias diff --git a/translations/ja/ja.go b/translations/ja/ja.go index 205df34..6513117 100644 --- a/translations/ja/ja.go +++ b/translations/ja/ja.go @@ -374,9 +374,7 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er if err != nil { goto END } - t, err = ut.T("ne-items", fe.Field(), c) - break default: t, err = ut.T("ne", fe.Field(), fe.Param()) } diff --git a/translations/zh/zh.go b/translations/zh/zh.go index 6dc9017..09dc84a 100644 --- a/translations/zh/zh.go +++ b/translations/zh/zh.go @@ -9,7 +9,7 @@ import ( "time" "github.com/go-playground/locales" - "github.com/go-playground/universal-translator" + ut "github.com/go-playground/universal-translator" "gopkg.in/go-playground/validator.v9" ) @@ -430,10 +430,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) + } else { + t, err = ut.T("lt-datetime", fe.Field()) } - t, err = ut.T("lt-datetime", fe.Field()) - default: err = fn() if err != nil { @@ -549,10 +549,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) + } else { + t, err = ut.T("lte-datetime", fe.Field()) } - t, err = ut.T("lte-datetime", fe.Field()) - default: err = fn() if err != nil { @@ -668,9 +668,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) - } + } else { - t, err = ut.T("gt-datetime", fe.Field()) + t, err = ut.T("gt-datetime", fe.Field()) + } default: err = fn() @@ -787,10 +788,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用于struct类型.", fe.Tag()) + } else { + t, err = ut.T("gte-datetime", fe.Field()) } - t, err = ut.T("gte-datetime", fe.Field()) - default: err = fn() if err != nil { diff --git a/translations/zh/zh_test.go b/translations/zh/zh_test.go index 0a5713a..a7a4a01 100644 --- a/translations/zh/zh_test.go +++ b/translations/zh/zh_test.go @@ -5,7 +5,7 @@ import ( "time" zhongwen "github.com/go-playground/locales/zh" - "github.com/go-playground/universal-translator" + ut "github.com/go-playground/universal-translator" . "gopkg.in/go-playground/assert.v1" "gopkg.in/go-playground/validator.v9" ) @@ -14,7 +14,7 @@ func TestTranslations(t *testing.T) { zh := zhongwen.New() uni := ut.New(zh, zh) - trans, ok := uni.GetTranslator("zh") + trans, _ := uni.GetTranslator("zh") validate := validator.New() diff --git a/translations/zh_tw/zh_tw.go b/translations/zh_tw/zh_tw.go index 699de3b..05652fb 100644 --- a/translations/zh_tw/zh_tw.go +++ b/translations/zh_tw/zh_tw.go @@ -9,7 +9,7 @@ import ( "time" "github.com/go-playground/locales" - "github.com/go-playground/universal-translator" + ut "github.com/go-playground/universal-translator" "gopkg.in/go-playground/validator.v9" ) @@ -430,10 +430,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) + } else { + t, err = ut.T("lt-datetime", fe.Field()) } - t, err = ut.T("lt-datetime", fe.Field()) - default: err = fn() if err != nil { @@ -549,10 +549,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) + } else { + t, err = ut.T("lte-datetime", fe.Field()) } - t, err = ut.T("lte-datetime", fe.Field()) - default: err = fn() if err != nil { @@ -618,13 +618,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er var kind reflect.Kind fn := func() (err error) { - if idx := strings.Index(fe.Param(), "."); idx != -1 { digits = uint64(len(fe.Param()[idx+1:])) } - f64, err = strconv.ParseFloat(fe.Param(), 64) - return } @@ -668,10 +665,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) + } else { + t, err = ut.T("gt-datetime", fe.Field()) } - t, err = ut.T("gt-datetime", fe.Field()) - default: err = fn() if err != nil { @@ -787,10 +784,10 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er case reflect.Struct: if fe.Type() != reflect.TypeOf(time.Time{}) { err = fmt.Errorf("tag '%s'不能用於struct類型.", fe.Tag()) + } else { + t, err = ut.T("gte-datetime", fe.Field()) } - t, err = ut.T("gte-datetime", fe.Field()) - default: err = fn() if err != nil { diff --git a/translations/zh_tw/zh_tw_test.go b/translations/zh_tw/zh_tw_test.go index f631279..809653e 100644 --- a/translations/zh_tw/zh_tw_test.go +++ b/translations/zh_tw/zh_tw_test.go @@ -5,7 +5,7 @@ import ( "time" zhongwen "github.com/go-playground/locales/zh_Hant_TW" - "github.com/go-playground/universal-translator" + ut "github.com/go-playground/universal-translator" . "gopkg.in/go-playground/assert.v1" "gopkg.in/go-playground/validator.v9" ) @@ -14,7 +14,7 @@ func TestTranslations(t *testing.T) { zh := zhongwen.New() uni := ut.New(zh, zh) - trans, ok := uni.GetTranslator("zh") + trans, _ := uni.GetTranslator("zh") validate := validator.New() diff --git a/validator_test.go b/validator_test.go index aab64ac..48b0f84 100644 --- a/validator_test.go +++ b/validator_test.go @@ -462,6 +462,8 @@ func TestAnonymous(t *testing.T) { }, } + Equal(t, tst.anonymousC.c, "") + err := validate.Struct(tst) NotEqual(t, err, nil) @@ -715,16 +717,15 @@ func TestNilValidator(t *testing.T) { } PanicMatches(t, func() { val.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) }, "runtime error: invalid memory address or nil pointer dereference") - PanicMatches(t, func() { val.RegisterValidation("something", fn) }, "runtime error: invalid memory address or nil pointer dereference") - PanicMatches(t, func() { val.Var(ts.Test, "required") }, "runtime error: invalid memory address or nil pointer dereference") - PanicMatches(t, func() { val.VarWithValue("test", ts.Test, "required") }, "runtime error: invalid memory address or nil pointer dereference") - PanicMatches(t, func() { val.Struct(ts) }, "runtime error: invalid memory address or nil pointer dereference") - PanicMatches(t, func() { val.StructExcept(ts, "Test") }, "runtime error: invalid memory address or nil pointer dereference") - PanicMatches(t, func() { val.StructPartial(ts, "Test") }, "runtime error: invalid memory address or nil pointer dereference") + PanicMatches(t, func() { _ = val.RegisterValidation("something", fn) }, "runtime error: invalid memory address or nil pointer dereference") + PanicMatches(t, func() { _ = val.Var(ts.Test, "required") }, "runtime error: invalid memory address or nil pointer dereference") + PanicMatches(t, func() { _ = val.VarWithValue("test", ts.Test, "required") }, "runtime error: invalid memory address or nil pointer dereference") + PanicMatches(t, func() { _ = val.Struct(ts) }, "runtime error: invalid memory address or nil pointer dereference") + PanicMatches(t, func() { _ = val.StructExcept(ts, "Test") }, "runtime error: invalid memory address or nil pointer dereference") + PanicMatches(t, func() { _ = val.StructPartial(ts, "Test") }, "runtime error: invalid memory address or nil pointer dereference") } func TestStructPartial(t *testing.T) { - p1 := []string{ "NoTag", "Required", @@ -1850,7 +1851,7 @@ func TestSQLValue2Validation(t *testing.T) { val.Name = "errorme" - PanicMatches(t, func() { validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error") + PanicMatches(t, func() { _ = validate.Var(val, "required") }, "SQL Driver Valuer error: some kind of error") myVal := valuer{ Name: "", @@ -2675,7 +2676,7 @@ func TestBadKeyValidation(t *testing.T) { validate := New() - PanicMatches(t, func() { validate.Struct(tst) }, "Undefined validation function ' ' on field 'Name'") + PanicMatches(t, func() { _ = validate.Struct(tst) }, "Undefined validation function ' ' on field 'Name'") type Test2 struct { Name string `validate:"required,,len=2"` @@ -2685,7 +2686,7 @@ func TestBadKeyValidation(t *testing.T) { Name: "test", } - PanicMatches(t, func() { validate.Struct(tst2) }, "Invalid validation tag on field 'Name'") + PanicMatches(t, func() { _ = validate.Struct(tst2) }, "Invalid validation tag on field 'Name'") } func TestInterfaceErrValidation(t *testing.T) { @@ -3009,7 +3010,7 @@ func TestArrayDiveValidation(t *testing.T) { Name: "TEST", } - PanicMatches(t, func() { validate.Struct(bd) }, "dive error! can't dive on a non slice or map") + PanicMatches(t, func() { _ = validate.Struct(bd) }, "dive error! can't dive on a non slice or map") type Test struct { Errs []string `validate:"gt=0,dive,required"` @@ -3372,7 +3373,7 @@ func TestLongitudeValidation(t *testing.T) { } } - PanicMatches(t, func() { validate.Var(true, "longitude") }, "Bad field type bool") + PanicMatches(t, func() { _ = validate.Var(true, "longitude") }, "Bad field type bool") } func TestLatitudeValidation(t *testing.T) { @@ -3414,7 +3415,7 @@ func TestLatitudeValidation(t *testing.T) { } } - PanicMatches(t, func() { validate.Var(true, "latitude") }, "Bad field type bool") + PanicMatches(t, func() { _ = validate.Var(true, "latitude") }, "Bad field type bool") } func TestDataURIValidation(t *testing.T) { @@ -4317,7 +4318,7 @@ func TestIsNeValidation(t *testing.T) { NotEqual(t, errs, nil) AssertError(t, errs, "", "", "", "", "ne") - PanicMatches(t, func() { validate.Var(now, "ne=now") }, "Bad field type time.Time") + PanicMatches(t, func() { _ = validate.Var(now, "ne=now") }, "Bad field type time.Time") } func TestIsEqFieldValidation(t *testing.T) { @@ -4471,7 +4472,7 @@ func TestIsEqValidation(t *testing.T) { NotEqual(t, errs, nil) AssertError(t, errs, "", "", "", "", "eq") - PanicMatches(t, func() { validate.Var(now, "eq=now") }, "Bad field type time.Time") + PanicMatches(t, func() { _ = validate.Var(now, "eq=now") }, "Bad field type time.Time") } func TestOneOfValidation(t *testing.T) { @@ -4530,7 +4531,7 @@ func TestOneOfValidation(t *testing.T) { } PanicMatches(t, func() { - validate.Var(3.14, "oneof=red green") + _ = validate.Var(3.14, "oneof=red green") }, "Bad field type float64") } @@ -4634,7 +4635,7 @@ func TestFileValidation(t *testing.T) { } PanicMatches(t, func() { - validate.Var(6, "file") + _ = validate.Var(6, "file") }, "Bad field type int") } @@ -5724,7 +5725,6 @@ func TestGteField(t *testing.T) { } func TestValidateByTagAndValue(t *testing.T) { - validate := New() val := "test" @@ -5737,7 +5737,8 @@ func TestValidateByTagAndValue(t *testing.T) { return fl.Parent().String() == fl.Field().String() } - validate.RegisterValidation("isequaltestfunc", fn) + errs = validate.RegisterValidation("isequaltestfunc", fn) + Equal(t, errs, nil) errs = validate.VarWithValue(val, field, "isequaltestfunc") Equal(t, errs, nil) @@ -5768,7 +5769,7 @@ func TestAddFunctions(t *testing.T) { errs = validate.RegisterValidation("", fn) NotEqual(t, errs, nil) - validate.RegisterValidation("new", nil) + errs = validate.RegisterValidation("new", nil) NotEqual(t, errs, nil) errs = validate.RegisterValidation("new", fn) @@ -5777,7 +5778,7 @@ func TestAddFunctions(t *testing.T) { errs = validate.RegisterValidationCtx("new", fnCtx) Equal(t, errs, nil) - PanicMatches(t, func() { validate.RegisterValidation("dive", fn) }, "Tag 'dive' either contains restricted characters or is the same as a restricted tag needed for normal operation") + PanicMatches(t, func() { _ = validate.RegisterValidation("dive", fn) }, "Tag 'dive' either contains restricted characters or is the same as a restricted tag needed for normal operation") } func TestChangeTag(t *testing.T) { @@ -5803,7 +5804,6 @@ func TestChangeTag(t *testing.T) { } func TestUnexposedStruct(t *testing.T) { - validate := New() type Test struct { @@ -5816,41 +5816,36 @@ func TestUnexposedStruct(t *testing.T) { s := &Test{ Name: "TEST", } + Equal(t, s.unexposed.A, "") errs := validate.Struct(s) Equal(t, errs, nil) } func TestBadParams(t *testing.T) { - validate := New() - i := 1 errs := validate.Var(i, "-") Equal(t, errs, nil) - PanicMatches(t, func() { validate.Var(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") - PanicMatches(t, func() { validate.Var(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") + PanicMatches(t, func() { _ = validate.Var(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") + PanicMatches(t, func() { _ = validate.Var(i, "len=a") }, "strconv.ParseInt: parsing \"a\": invalid syntax") var ui uint = 1 - PanicMatches(t, func() { validate.Var(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") + PanicMatches(t, func() { _ = validate.Var(ui, "len=a") }, "strconv.ParseUint: parsing \"a\": invalid syntax") f := 1.23 - PanicMatches(t, func() { validate.Var(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") + PanicMatches(t, func() { _ = validate.Var(f, "len=a") }, "strconv.ParseFloat: parsing \"a\": invalid syntax") } func TestLength(t *testing.T) { - validate := New() - i := true - PanicMatches(t, func() { validate.Var(i, "len") }, "Bad field type bool") + PanicMatches(t, func() { _ = validate.Var(i, "len") }, "Bad field type bool") } func TestIsGt(t *testing.T) { - validate := New() - myMap := map[string]string{} errs := validate.Var(myMap, "gt=0") NotEqual(t, errs, nil) @@ -5866,7 +5861,7 @@ func TestIsGt(t *testing.T) { AssertError(t, errs, "", "", "", "", "gt") i := true - PanicMatches(t, func() { validate.Var(i, "gt") }, "Bad field type bool") + PanicMatches(t, func() { _ = validate.Var(i, "gt") }, "Bad field type bool") tm := time.Now().UTC() tm = tm.Add(time.Hour * 24) @@ -5900,11 +5895,9 @@ func TestIsGt(t *testing.T) { } func TestIsGte(t *testing.T) { - validate := New() - i := true - PanicMatches(t, func() { validate.Var(i, "gte") }, "Bad field type bool") + PanicMatches(t, func() { _ = validate.Var(i, "gte") }, "Bad field type bool") t1 := time.Now().UTC() t1 = t1.Add(time.Hour * 24) @@ -5938,9 +5931,7 @@ func TestIsGte(t *testing.T) { } func TestIsLt(t *testing.T) { - validate := New() - myMap := map[string]string{} errs := validate.Var(myMap, "lt=0") NotEqual(t, errs, nil) @@ -5957,7 +5948,7 @@ func TestIsLt(t *testing.T) { AssertError(t, errs, "", "", "", "", "lt") i := true - PanicMatches(t, func() { validate.Var(i, "lt") }, "Bad field type bool") + PanicMatches(t, func() { _ = validate.Var(i, "lt") }, "Bad field type bool") t1 := time.Now().UTC().Add(-time.Hour) @@ -5996,7 +5987,7 @@ func TestIsLte(t *testing.T) { validate := New() i := true - PanicMatches(t, func() { validate.Var(i, "lte") }, "Bad field type bool") + PanicMatches(t, func() { _ = validate.Var(i, "lte") }, "Bad field type bool") t1 := time.Now().UTC().Add(-time.Hour) @@ -6103,7 +6094,7 @@ func TestUrnRFC2141(t *testing.T) { } i := 1 - PanicMatches(t, func() { validate.Var(i, tag) }, "Bad field type int") + PanicMatches(t, func() { _ = validate.Var(i, tag) }, "Bad field type int") } func TestUrl(t *testing.T) { @@ -6171,7 +6162,7 @@ func TestUrl(t *testing.T) { } i := 1 - PanicMatches(t, func() { validate.Var(i, "url") }, "Bad field type int") + PanicMatches(t, func() { _ = validate.Var(i, "url") }, "Bad field type int") } func TestUri(t *testing.T) { @@ -6238,7 +6229,7 @@ func TestUri(t *testing.T) { } i := 1 - PanicMatches(t, func() { validate.Var(i, "uri") }, "Bad field type int") + PanicMatches(t, func() { _ = validate.Var(i, "uri") }, "Bad field type int") } func TestOrTag(t *testing.T) { @@ -6280,8 +6271,8 @@ func TestOrTag(t *testing.T) { s = "this is right, but a blank or isn't" - PanicMatches(t, func() { validate.Var(s, "rgb||len=13") }, "Invalid validation tag on field ''") - PanicMatches(t, func() { validate.Var(s, "rgb|rgbaa|len=13") }, "Undefined validation function 'rgbaa' on field ''") + PanicMatches(t, func() { _ = validate.Var(s, "rgb||len=13") }, "Invalid validation tag on field ''") + PanicMatches(t, func() { _ = validate.Var(s, "rgb|rgbaa|len=13") }, "Undefined validation function 'rgbaa' on field ''") v2 := New() v2.RegisterTagNameFunc(func(fld reflect.StructField) string { @@ -6351,7 +6342,7 @@ func TestHsla(t *testing.T) { AssertError(t, errs, "", "", "", "", "hsla") i := 1 - validate.Var(i, "hsla") + errs = validate.Var(i, "hsla") NotEqual(t, errs, nil) AssertError(t, errs, "", "", "", "", "hsla") } @@ -7053,7 +7044,7 @@ func TestInvalidValidatorFunction(t *testing.T) { Test: "1", } - PanicMatches(t, func() { validate.Var(s.Test, "zzxxBadFunction") }, "Undefined validation function 'zzxxBadFunction' on field ''") + PanicMatches(t, func() { _ = validate.Var(s.Test, "zzxxBadFunction") }, "Undefined validation function 'zzxxBadFunction' on field ''") } func TestCustomFieldName(t *testing.T) { @@ -7309,15 +7300,15 @@ func TestTranslations(t *testing.T) { } func TestTranslationErrors(t *testing.T) { - en := en.New() uni := ut.New(en, en, fr.New()) trans, _ := uni.GetTranslator("en") - trans.Add("required", "{0} is a required field", false) // using translator outside of validator also + err := trans.Add("required", "{0} is a required field", false) // using translator outside of validator also + Equal(t, err, nil) validate := New() - err := validate.RegisterTranslation("required", trans, + err = validate.RegisterTranslation("required", trans, func(ut ut.Translator) (err error) { // using this stype because multiple translation may have to be added for the full translation @@ -7766,31 +7757,40 @@ func TestFieldLevelName(t *testing.T) { return name }) - validate.RegisterValidation("custom1", func(fl FieldLevel) bool { + err := validate.RegisterValidation("custom1", func(fl FieldLevel) bool { res1 = fl.FieldName() alt1 = fl.StructFieldName() return true }) - validate.RegisterValidation("custom2", func(fl FieldLevel) bool { + Equal(t, err, nil) + + err = validate.RegisterValidation("custom2", func(fl FieldLevel) bool { res2 = fl.FieldName() alt2 = fl.StructFieldName() return true }) - validate.RegisterValidation("custom3", func(fl FieldLevel) bool { + Equal(t, err, nil) + + err = validate.RegisterValidation("custom3", func(fl FieldLevel) bool { res3 = fl.FieldName() alt3 = fl.StructFieldName() return true }) - validate.RegisterValidation("custom4", func(fl FieldLevel) bool { + Equal(t, err, nil) + + err = validate.RegisterValidation("custom4", func(fl FieldLevel) bool { res4 = fl.FieldName() alt4 = fl.StructFieldName() return true }) - validate.RegisterValidation("custom5", func(fl FieldLevel) bool { + Equal(t, err, nil) + + err = validate.RegisterValidation("custom5", func(fl FieldLevel) bool { res5 = fl.FieldName() alt5 = fl.StructFieldName() return true }) + Equal(t, err, nil) test := Test{ String: "test", @@ -7813,7 +7813,6 @@ func TestFieldLevelName(t *testing.T) { } func TestValidateStructRegisterCtx(t *testing.T) { - var ctxVal string fnCtx := func(ctx context.Context, fl FieldLevel) bool { @@ -7833,7 +7832,9 @@ func TestValidateStructRegisterCtx(t *testing.T) { var tst Test validate := New() - validate.RegisterValidationCtx("val", fnCtx) + err := validate.RegisterValidationCtx("val", fnCtx) + Equal(t, err, nil) + validate.RegisterStructValidationCtx(slFn, Test{}) ctx := context.WithValue(context.Background(), &ctxVal, "testval") @@ -8039,7 +8040,6 @@ func TestFQDNValidation(t *testing.T) { } func TestIsDefault(t *testing.T) { - validate := New() type Inner struct { @@ -8066,11 +8066,9 @@ func TestIsDefault(t *testing.T) { validate.RegisterTagNameFunc(func(fld reflect.StructField) string { name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] - if name == "-" { return "" } - return name }) @@ -8157,7 +8155,7 @@ func TestUniqueValidation(t *testing.T) { } } } - PanicMatches(t, func() { validate.Var(1.0, "unique") }, "Bad field type float64") + PanicMatches(t, func() { _ = validate.Var(1.0, "unique") }, "Bad field type float64") } func TestHTMLValidation(t *testing.T) { @@ -8362,8 +8360,8 @@ func TestKeys(t *testing.T) { // test bad tag definitions - PanicMatches(t, func() { validate.Var(map[string]string{"key": "val"}, "endkeys,dive,eq=val") }, "'endkeys' tag encountered without a corresponding 'keys' tag") - PanicMatches(t, func() { validate.Var(1, "keys,eq=1,endkeys") }, "'keys' tag must be immediately preceded by the 'dive' tag") + PanicMatches(t, func() { _ = validate.Var(map[string]string{"key": "val"}, "endkeys,dive,eq=val") }, "'endkeys' tag encountered without a corresponding 'keys' tag") + PanicMatches(t, func() { _ = validate.Var(1, "keys,eq=1,endkeys") }, "'keys' tag must be immediately preceded by the 'dive' tag") // test custom tag name validate = New() @@ -8391,7 +8389,6 @@ func TestKeys(t *testing.T) { // Thanks @adrian-sgn specific test for your specific scenario func TestKeysCustomValidation(t *testing.T) { - type LangCode string type Label map[LangCode]string @@ -8400,7 +8397,7 @@ func TestKeysCustomValidation(t *testing.T) { } validate := New() - validate.RegisterValidation("lang_code", func(fl FieldLevel) bool { + err := validate.RegisterValidation("lang_code", func(fl FieldLevel) bool { validLangCodes := map[LangCode]struct{}{ "en": {}, "es": {}, @@ -8410,6 +8407,7 @@ func TestKeysCustomValidation(t *testing.T) { _, ok := validLangCodes[fl.Field().Interface().(LangCode)] return ok }) + Equal(t, err, nil) label := Label{ "en": "Good morning!", @@ -8419,7 +8417,7 @@ func TestKeysCustomValidation(t *testing.T) { "xxx": "", } - err := validate.Struct(TestMapStructPtr{label}) + err = validate.Struct(TestMapStructPtr{label}) NotEqual(t, err, nil) errs := err.(ValidationErrors) @@ -8562,7 +8560,7 @@ func TestDirValidation(t *testing.T) { } PanicMatches(t, func() { - validate.Var(2, "dir") + _ = validate.Var(2, "dir") }, "Bad field type int") } From 9593a0f77e3b14639546a78f56dc0a0711a7ccf0 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sun, 29 Sep 2019 15:03:37 -0700 Subject: [PATCH 4/4] add test for new nil validation --- validator_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/validator_test.go b/validator_test.go index 48b0f84..6f42a91 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8839,3 +8839,28 @@ func TestLookup(t *testing.T) { } Equal(t, New().Struct(lookup), nil) } + +func TestAbilityToValidateNils(t *testing.T) { + + type TestStruct struct { + Test *string `validate:"nil"` + } + + ts := TestStruct{} + val := New() + fn := func(fl FieldLevel) bool { + return fl.Field().Kind() == reflect.Ptr && fl.Field().IsNil() + } + + err := val.RegisterValidation("nil", fn, true) + Equal(t, err, nil) + + errs := val.Struct(ts) + Equal(t, errs, nil) + + str := "string" + ts.Test = &str + + errs = val.Struct(ts) + NotEqual(t, errs, nil) +}