From 8009ad90547ccec937fb3fd5a366f6166113f55e Mon Sep 17 00:00:00 2001 From: BrandonRomano Date: Thu, 22 Jun 2017 16:22:26 -0400 Subject: [PATCH] Add documentation, update tests --- README.md | 59 ++++++++++++++++++++++++++++++++++++++++++----- validator_test.go | 42 ++++++++++++++++++++++----------- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 060aec3..3d3491c 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,12 @@ Let's check out the Value struct. ```go type Value struct { - Result interface{} - Name string - Input string - Rules []Rule + Result interface{} + Default string + Name string + Input string + Rules []Rule + TypeHandler TypeHandler } ``` @@ -52,9 +54,15 @@ type Value struct { Result must be a pointer to the variable you want to store the parsed input in. -Valid types for this are `*string`, `*float32`, `*float64`, `*bool`, `*int`, `*int8`, `*int16`, `*int32`, `*int64`, `*uint`, `*uint8`, `*uint16`, `*uint32`, `*uint64`. +By default, supported types for this are `*string`, `*float32`, `*float64`, `*bool`, `*int`, `*int8`, `*int16`, `*int32`, `*int64`, `*uint`, `*uint8`, `*uint16`, `*uint32`, `*uint64`. -It is expected that the value of the `Input` parameter can be parsed into the decided type using their respective [strconv](https://golang.org/pkg/strconv/) function, else an error will be thrown by [the Validate function](#the-validate-function) when it is called. +For the default supported types, it is expected that the value of the `Input` parameter can be parsed into the decided type using their respective [strconv](https://golang.org/pkg/strconv/) function, else an error will be thrown by [the Validate function](#the-validate-function) when it is called. + +If you need to use another type, `TypeHandler` must also be set to the Value struct. + +#### Default + +This is the optional default value of `Input` that will be set, if the value of `Input` ends up being an empty string. #### Name @@ -70,6 +78,12 @@ This is a slice of rules that you require a particular value to pass. This is optional, and can be not set if you don't have any rules for your value to pass. The value will still go through the type check if the Input is a non-empty string. +#### TypeHandler + +TypeHandler is a function that defines how the input string is parsed. + +For basic types, it's not necessary to implement your own TypeHandler, as they have already been implemented and will be attached to Values automatically. + ## Rules A Rule is a very simple type of function: @@ -113,6 +127,39 @@ Both of these strategies should feel very fluent in use: > You won't find any prebuilt rules in [go-carrot/validator](https://github.com/go-carrot/validator). If you're looking for those check out the [go-carrot/rules](https://github.com/go-carrot/rules) repository. +## TypeHandlers + +A TypeHandler is a function that follows the following definition: + +```go +type TypeHandler func(input string, value *Value) error +``` + +A TypeHandler is responsible for: + +- Validation of the non-null string input + - (TypeHandlers aren't called if the string is not set - you can mandate that a value is required via `Rules`) +- Converting string input to the desired type +- Passing the converted type into the `value.Result` + +TypeHandlers are best explained by example. This is a TypeHandler for a `*sql.NullInt64` + +```go +func NullInt64TypeHandler = func(input string, value *v.Value) error { + // Get int64 + res, err := strconv.ParseInt(input, 10, 64) + if err != nil { + return errors.New("Invalid parameter, must be an int64") + } + + // Update nullInt + nullInt := value.Result.(*sql.NullInt64) + (*nullInt).Int64 = int64(res) + (*nullInt).Valid = true + return nil +} +``` + ## The Validate Function The validate function is the function that will actually perform your input validation. This function will throw an error if any of your values fail validation. diff --git a/validator_test.go b/validator_test.go index 0e74ae4..034a70c 100644 --- a/validator_test.go +++ b/validator_test.go @@ -655,28 +655,44 @@ func TestUint64(t *testing.T) { // and use it as expected func TestCustomTypeHandler(t *testing.T) { // Create type handler - var nullStringTypeHandler = func(input string, value *v.Value) error { - nullString := value.Result.(*sql.NullString) - (*nullString).String = input - (*nullString).Valid = (input != "") + var nullInt64TypeHandler = func(input string, value *v.Value) error { + // Get int64 + res, err := strconv.ParseInt(input, 10, 64) + if err != nil { + return errors.New("Invalid parameter, must be an int64") + } + + // Update nullInt + nullInt := value.Result.(*sql.NullInt64) + (*nullInt).Int64 = int64(res) + (*nullInt).Valid = true return nil } // Test valid case - var slug sql.NullString + var id sql.NullInt64 err := v.Validate([]*v.Value{ - {Result: &slug, Name: "slug", Input: "wow", TypeHandler: nullStringTypeHandler}, + {Result: &id, Name: "id", Input: "42", TypeHandler: nullInt64TypeHandler}, }) assert.Nil(t, err) - assert.Equal(t, "wow", slug.String) - assert.Equal(t, true, slug.Valid) + assert.Equal(t, int64(42), id.Int64) + assert.Equal(t, true, id.Valid) - // Test invalid case - var emptySlug sql.NullString + // Test empty case + var emptyId sql.NullInt64 err = v.Validate([]*v.Value{ - {Result: &emptySlug, Name: "slug", Input: "", TypeHandler: nullStringTypeHandler}, + {Result: &emptyId, Name: "id", Input: "", TypeHandler: nullInt64TypeHandler}, }) assert.Nil(t, err) - assert.Equal(t, "", emptySlug.String) - assert.Equal(t, false, emptySlug.Valid) + assert.Equal(t, int64(0), emptyId.Int64) + assert.Equal(t, false, emptyId.Valid) + + // Test error case + var errorId sql.NullInt64 + err = v.Validate([]*v.Value{ + {Result: &errorId, Name: "id", Input: "abcd", TypeHandler: nullInt64TypeHandler}, + }) + assert.NotNil(t, err) + assert.Equal(t, int64(0), errorId.Int64) + assert.Equal(t, false, errorId.Valid) }