diff --git a/README.md b/README.md index d64138a..8c40597 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Package validator ================ [![Join the chat at https://gitter.im/bluesuncorp/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bluesuncorp/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487374/badge.svg)](https://semaphoreci.com/joeybloggs/validator) +[![Build Status](https://semaphoreci.com/api/v1/projects/ec20115f-ef1b-4c7d-9393-cc76aba74eb4/487383/badge.svg)](https://semaphoreci.com/joeybloggs/validator) [![Coverage Status](https://coveralls.io/repos/bluesuncorp/validator/badge.svg?branch=v6)](https://coveralls.io/r/bluesuncorp/validator?branch=v6) [![GoDoc](https://godoc.org/gopkg.in/bluesuncorp/validator.v6?status.svg)](https://godoc.org/gopkg.in/bluesuncorp/validator.v6) @@ -138,84 +138,51 @@ Custom Field Type package main import ( - "errors" + "database/sql" + "database/sql/driver" "fmt" "reflect" - sql "database/sql/driver" - "gopkg.in/bluesuncorp/validator.v6" ) -var validate *validator.Validate - -type valuer struct { - Name string -} - -func (v valuer) Value() (sql.Value, error) { - - if v.Name == "errorme" { - return nil, errors.New("some kind of error") - } - - if v.Name == "blankme" { - return "", nil - } - - if len(v.Name) == 0 { - return nil, nil - } - - return v.Name, nil -} - -// ValidateValuerType implements validator.CustomTypeFunc -func ValidateValuerType(field reflect.Value) interface{} { - if valuer, ok := field.Interface().(sql.Valuer); ok { - val, err := valuer.Value() - if err != nil { - // handle the error how you want - return nil - } - - return val - } - - return nil +// DbBackedUser User struct +type DbBackedUser struct { + Name sql.NullString `validate:"required"` + Age sql.NullInt64 `validate:"required"` } func main() { - customTypes := map[reflect.Type]validator.CustomTypeFunc{} - customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType - customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType - config := validator.Config{ TagName: "validate", ValidationFuncs: validator.BakedInValidators, - CustomTypeFuncs: customTypes, } - validate = validator.New(config) + validate := validator.New(config) - validateCustomFieldType() -} + // register all sql.Null* types to use the ValidateValuer CustomTypeFunc + validate.RegisterCustomTypeFunc(ValidateValuer, sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) -func validateCustomFieldType() { - val := valuer{ - Name: "blankme", - } + x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}} + errs := validate.Struct(x) - errs := validate.Field(val, "required") - if errs != nil { - fmt.Println(errs) // output: Key: "" Error:Field validation for "" failed on the "required" tag - return + if len(errs) > 0 { + fmt.Printf("Errs:\n%+v\n", errs) } - - // all ok } +// ValidateValuer implements validator.CustomTypeFunc +func ValidateValuer(field reflect.Value) interface{} { + if valuer, ok := field.Interface().(driver.Valuer); ok { + val, err := valuer.Value() + if err == nil { + return val + } + // handle the error how you want + } + return nil +} ``` Benchmarks diff --git a/examples/custom.go b/examples/custom/custom.go similarity index 94% rename from examples/custom.go rename to examples/custom/custom.go index d7eb3b2..897b17f 100644 --- a/examples/custom.go +++ b/examples/custom/custom.go @@ -6,9 +6,10 @@ import ( "fmt" "reflect" - validator "gopkg.in/bluesuncorp/validator.v6" + "gopkg.in/bluesuncorp/validator.v6" ) +// DbBackedUser User struct type DbBackedUser struct { Name sql.NullString `validate:"required"` Age sql.NullInt64 `validate:"required"` diff --git a/examples/simple.go b/examples/simple/simple.go similarity index 100% rename from examples/simple.go rename to examples/simple/simple.go diff --git a/validator.go b/validator.go index 9fb4dcf..d8f35b6 100644 --- a/validator.go +++ b/validator.go @@ -157,14 +157,17 @@ func (v *Validate) RegisterValidation(key string, f Func) error { return nil } -// RegisterCustomTypeFunc registers types w/a custom type handler function. -func (v *Validate) RegisterCustomTypeFunc(f CustomTypeFunc, sampleTypeValues ...interface{}) { +// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types +func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) { + if v.config.CustomTypeFuncs == nil { v.config.CustomTypeFuncs = map[reflect.Type]CustomTypeFunc{} } - for _, sample := range sampleTypeValues { - v.config.CustomTypeFuncs[reflect.TypeOf(sample)] = f + + for _, t := range types { + v.config.CustomTypeFuncs[reflect.TypeOf(t)] = fn } + v.config.hasCustomFuncs = true } diff --git a/validator_test.go b/validator_test.go index 37b6dee..4b93f92 100644 --- a/validator_test.go +++ b/validator_test.go @@ -1,14 +1,14 @@ package validator import ( + "database/sql" + "database/sql/driver" "errors" "fmt" "reflect" "testing" "time" - sql "database/sql/driver" - . "gopkg.in/bluesuncorp/assert.v1" ) @@ -126,7 +126,7 @@ type valuer struct { Name string } -func (v valuer) Value() (sql.Value, error) { +func (v valuer) Value() (driver.Value, error) { if v.Name == "errorme" { return nil, errors.New("some kind of error") @@ -178,7 +178,7 @@ type CustomMadeUpStruct struct { } func ValidateValuerType(field reflect.Value) interface{} { - if valuer, ok := field.Interface().(sql.Valuer); ok { + if valuer, ok := field.Interface().(driver.Valuer); ok { val, err := valuer.Value() if err != nil { // handle the error how you want @@ -191,10 +191,68 @@ func ValidateValuerType(field reflect.Value) interface{} { return nil } +func TestSQLValue2Validation(t *testing.T) { + + config := Config{ + TagName: "validate", + ValidationFuncs: BakedInValidators, + } + + validate := New(config) + validate.RegisterCustomTypeFunc(ValidateValuerType, valuer{}, (*driver.Valuer)(nil), sql.NullString{}, sql.NullInt64{}, sql.NullBool{}, sql.NullFloat64{}) + validate.RegisterCustomTypeFunc(ValidateCustomType, MadeUpCustomType{}) + validate.RegisterCustomTypeFunc(OverrideIntTypeForSomeReason, 1) + + val := valuer{ + Name: "", + } + + errs := validate.Field(val, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + val.Name = "Valid Name" + errs = validate.Field(val, "required") + Equal(t, errs, nil) + + val.Name = "errorme" + + PanicMatches(t, func() { errs = validate.Field(val, "required") }, "SQL Driver Valuer error: some kind of error") + + type myValuer valuer + + myVal := valuer{ + Name: "", + } + + errs = validate.Field(myVal, "required") + NotEqual(t, errs, nil) + AssertError(t, errs, "", "", "required") + + cust := MadeUpCustomType{ + FirstName: "Joey", + LastName: "Bloggs", + } + + c := CustomMadeUpStruct{MadeUp: cust, OverriddenInt: 2} + + errs = validate.Struct(c) + Equal(t, errs, nil) + + c.MadeUp.FirstName = "" + c.OverriddenInt = 1 + + errs = validate.Struct(c) + NotEqual(t, errs, nil) + Equal(t, len(errs), 2) + AssertError(t, errs, "CustomMadeUpStruct.MadeUp", "MadeUp", "required") + AssertError(t, errs, "CustomMadeUpStruct.OverriddenInt", "OverriddenInt", "gt") +} + func TestSQLValueValidation(t *testing.T) { customTypes := map[reflect.Type]CustomTypeFunc{} - customTypes[reflect.TypeOf((*sql.Valuer)(nil))] = ValidateValuerType + customTypes[reflect.TypeOf((*driver.Valuer)(nil))] = ValidateValuerType customTypes[reflect.TypeOf(valuer{})] = ValidateValuerType customTypes[reflect.TypeOf(MadeUpCustomType{})] = ValidateCustomType customTypes[reflect.TypeOf(1)] = OverrideIntTypeForSomeReason