Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 59 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,46 @@ It has the following **unique** features:
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
- Handles type interface by determining it's underlying type prior to validation.
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs

Installation
------------

Use go get.

go get gopkg.in/bluesuncorp/validator.v7
go get gopkg.in/bluesuncorp/validator.v8

or to update

go get -u gopkg.in/bluesuncorp/validator.v7
go get -u gopkg.in/bluesuncorp/validator.v8

Then import the validator package into your own code.

import "gopkg.in/bluesuncorp/validator.v7"
import "gopkg.in/bluesuncorp/validator.v8"

Error Return Value
-------

Validation functions return type error

They return type error to avoid the issue discussed in the following, where err is always != nil:

* http://stackoverflow.com/a/29138676/3158232
* https://github.com/bluesuncorp/validator/issues/134

validator only returns nil or ValidationErrors as type error; so in you code all you need to do
is check if the error returned is not nil, and if it's not type cast it to type ValidationErrors
like so:

```go
err := validate.Struct(mystruct)
validationErrors := err.(validator.ValidationErrors)
```

Usage and documentation
------

Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v7 for detailed usage docs.
Please see http://godoc.org/gopkg.in/bluesuncorp/validator.v8 for detailed usage docs.

##### Examples:

Expand All @@ -44,7 +64,7 @@ package main
import (
"fmt"

"gopkg.in/bluesuncorp/validator.v7"
"gopkg.in/bluesuncorp/validator.v8"
)

// User contains user information
Expand All @@ -69,10 +89,7 @@ var validate *validator.Validate

func main() {

config := validator.Config{
TagName: "validate",
ValidationFuncs: validator.BakedInValidators,
}
config := &validator.Config{TagName: "validate"}

validate = validator.New(config)

Expand All @@ -98,13 +115,13 @@ func validateStruct() {
}

// returns nil or ValidationErrors ( map[string]*FieldError )
errs := validate.Struct(user)
err := validate.Struct(user)

if errs != nil {

fmt.Println(errs) // output: Key: "User.Age" Error:Field validation for "Age" failed on the "lte" tag
// Key: "User.Addresses[0].City" Error:Field validation for "City" failed on the "required" tag
err := errs["User.Addresses[0].City"]
err := errs.(validator.ValidationErrors)["User.Addresses[0].City"]
fmt.Println(err.Field) // output: City
fmt.Println(err.Tag) // output: required
fmt.Println(err.Kind) // output: string
Expand Down Expand Up @@ -143,7 +160,7 @@ import (
"fmt"
"reflect"

"gopkg.in/bluesuncorp/validator.v7"
"gopkg.in/bluesuncorp/validator.v8"
)

// DbBackedUser User struct
Expand All @@ -154,10 +171,7 @@ type DbBackedUser struct {

func main() {

config := validator.Config{
TagName: "validate",
ValidationFuncs: validator.BakedInValidators,
}
config := &validator.Config{TagName: "validate"}

validate := validator.New(config)

Expand All @@ -167,7 +181,7 @@ func main() {
x := DbBackedUser{Name: sql.NullString{String: "", Valid: true}, Age: sql.NullInt64{Int64: 0, Valid: false}}
errs := validate.Struct(x)

if len(errs) > 0 {
if len(errs.(validator.ValidationErrors)) > 0 {
fmt.Printf("Errs:\n%+v\n", errs)
}
}
Expand All @@ -191,32 +205,32 @@ Benchmarks
```go
$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4 5000000 285 ns/op 16 B/op 1 allocs/op
BenchmarkFieldFailure-4 5000000 284 ns/op 16 B/op 1 allocs/op
BenchmarkFieldDiveSuccess-4 500000 2501 ns/op 384 B/op 19 allocs/op
BenchmarkFieldDiveFailure-4 500000 3022 ns/op 752 B/op 23 allocs/op
BenchmarkFieldCustomTypeSuccess-4 3000000 445 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-4 2000000 788 ns/op 416 B/op 6 allocs/op
BenchmarkFieldOrTagSuccess-4 1000000 1377 ns/op 32 B/op 2 allocs/op
BenchmarkFieldOrTagFailure-4 1000000 1201 ns/op 400 B/op 6 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1257 ns/op 80 B/op 5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1776 ns/op 608 B/op 13 allocs/op
BenchmarkStructPartialSuccess-4 1000000 1354 ns/op 400 B/op 11 allocs/op
BenchmarkStructPartialFailure-4 1000000 1813 ns/op 784 B/op 16 allocs/op
BenchmarkStructExceptSuccess-4 2000000 916 ns/op 368 B/op 9 allocs/op
BenchmarkStructExceptFailure-4 1000000 1369 ns/op 400 B/op 11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1033 ns/op 128 B/op 6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1569 ns/op 528 B/op 11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1371 ns/op 160 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 1935 ns/op 560 B/op 13 allocs/op
BenchmarkFieldSuccess-4 5000000 296 ns/op 16 B/op 1 allocs/op
BenchmarkFieldFailure-4 5000000 294 ns/op 16 B/op 1 allocs/op
BenchmarkFieldDiveSuccess-4 500000 2529 ns/op 384 B/op 19 allocs/op
BenchmarkFieldDiveFailure-4 500000 3056 ns/op 768 B/op 23 allocs/op
BenchmarkFieldCustomTypeSuccess-4 3000000 443 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-4 2000000 753 ns/op 384 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-4 1000000 1334 ns/op 32 B/op 2 allocs/op
BenchmarkFieldOrTagFailure-4 1000000 1172 ns/op 416 B/op 6 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1206 ns/op 80 B/op 5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1737 ns/op 592 B/op 11 allocs/op
BenchmarkStructPartialSuccess-4 1000000 1367 ns/op 400 B/op 11 allocs/op
BenchmarkStructPartialFailure-4 1000000 1914 ns/op 800 B/op 16 allocs/op
BenchmarkStructExceptSuccess-4 2000000 909 ns/op 368 B/op 9 allocs/op
BenchmarkStructExceptFailure-4 1000000 1350 ns/op 400 B/op 11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1218 ns/op 128 B/op 6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1783 ns/op 544 B/op 11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1806 ns/op 160 B/op 8 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 2369 ns/op 576 B/op 13 allocs/op
BenchmarkStructSimpleSuccess-4 1000000 1161 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailure-4 1000000 1720 ns/op 560 B/op 11 allocs/op
BenchmarkStructSimpleSuccessParallel-4 5000000 329 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailureParallel-4 2000000 625 ns/op 560 B/op 11 allocs/op
BenchmarkStructComplexSuccess-4 200000 6636 ns/op 432 B/op 27 allocs/op
BenchmarkStructComplexFailure-4 200000 11327 ns/op 2919 B/op 69 allocs/op
BenchmarkStructComplexSuccessParallel-4 1000000 1991 ns/op 432 B/op 27 allocs/op
BenchmarkStructComplexFailureParallel-4 500000 3854 ns/op 2920 B/op 69 allocs/op
BenchmarkStructSimpleFailure-4 1000000 1813 ns/op 592 B/op 11 allocs/op
BenchmarkStructSimpleSuccessParallel-4 5000000 353 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailureParallel-4 2000000 656 ns/op 592 B/op 11 allocs/op
BenchmarkStructComplexSuccess-4 200000 7637 ns/op 432 B/op 27 allocs/op
BenchmarkStructComplexFailure-4 100000 12775 ns/op 3128 B/op 69 allocs/op
BenchmarkStructComplexSuccessParallel-4 1000000 2270 ns/op 432 B/op 27 allocs/op
BenchmarkStructComplexFailureParallel-4 300000 4328 ns/op 3128 B/op 69 allocs/op
```

How to Contribute
Expand All @@ -225,9 +239,9 @@ How to Contribute
There will always be a development branch for each version i.e. `v1-development`. In order to contribute,
please make your pull requests against those branches.

If the changes being proposed or requested are breaking changes, please create an issue, for discussion
or create a pull request against the highest development branch for example this package has a
v1 and v1-development branch however, there will also be a v2-development brach even though v2 doesn't exist yet.
If the changes being proposed or requested are breaking changes, please create an issue, for discussion
or create a pull request against the highest development branch for example this package has a
v1 and v1-development branch however, there will also be a v2-development branch even though v2 doesn't exist yet.

I strongly encourage everyone whom creates a custom validation function to contribute them and
help make this package even better.
Expand Down
60 changes: 34 additions & 26 deletions baked_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ import (
"unicode/utf8"
)

// BakedInAliasValidators is a default mapping of a single validationstag that
// defines a common or complex set of validation(s) to simplify
// adding validation to structs. i.e. set key "_ageok" and the tags
// are "gt=0,lte=130" or key "_preferredname" and tags "omitempty,gt=0,lte=60"
var bakedInAliasValidators = map[string]string{
"iscolor": "hexcolor|rgb|rgba|hsl|hsla",
}

// BakedInValidators is the default map of ValidationFunc
// you can add, remove or even replace items to suite your needs,
// or even disregard and use your own map if so desired.
var BakedInValidators = map[string]Func{
var bakedInValidators = map[string]Func{
"required": hasValue,
"len": hasLengthOf,
"min": hasMinOf,
Expand Down Expand Up @@ -107,15 +115,15 @@ func isSSN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Va
return false
}

return matchesRegex(sSNRegex, field.String())
return sSNRegex.MatchString(field.String())
}

func isLongitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(longitudeRegex, field.String())
return longitudeRegex.MatchString(field.String())
}

func isLatitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(latitudeRegex, field.String())
return latitudeRegex.MatchString(field.String())
}

func isDataURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
Expand All @@ -126,7 +134,7 @@ func isDataURI(v *Validate, topStruct reflect.Value, currentStructOrField reflec
return false
}

if !matchesRegex(dataURIRegex, uri[0]) {
if !dataURIRegex.MatchString(uri[0]) {
return false
}

Expand All @@ -141,31 +149,31 @@ func hasMultiByteCharacter(v *Validate, topStruct reflect.Value, currentStructOr
return true
}

return matchesRegex(multibyteRegex, field.String())
return multibyteRegex.MatchString(field.String())
}

func isPrintableASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(printableASCIIRegex, field.String())
return printableASCIIRegex.MatchString(field.String())
}

func isASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(aSCIIRegex, field.String())
return aSCIIRegex.MatchString(field.String())
}

func isUUID5(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUID5Regex, field.String())
return uUID5Regex.MatchString(field.String())
}

func isUUID4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUID4Regex, field.String())
return uUID4Regex.MatchString(field.String())
}

func isUUID3(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUID3Regex, field.String())
return uUID3Regex.MatchString(field.String())
}

func isUUID(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(uUIDRegex, field.String())
return uUIDRegex.MatchString(field.String())
}

func isISBN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
Expand All @@ -176,7 +184,7 @@ func isISBN13(v *Validate, topStruct reflect.Value, currentStructOrField reflect

s := strings.Replace(strings.Replace(field.String(), "-", "", 4), " ", "", 4)

if !matchesRegex(iSBN13Regex, s) {
if !iSBN13Regex.MatchString(s) {
return false
}

Expand All @@ -200,7 +208,7 @@ func isISBN10(v *Validate, topStruct reflect.Value, currentStructOrField reflect

s := strings.Replace(strings.Replace(field.String(), "-", "", 3), " ", "", 3)

if !matchesRegex(iSBN10Regex, s) {
if !iSBN10Regex.MatchString(s) {
return false
}

Expand Down Expand Up @@ -617,7 +625,7 @@ func isEq(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Val
}

func isBase64(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(base64Regex, field.String())
return base64Regex.MatchString(field.String())
}

func isURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
Expand Down Expand Up @@ -655,47 +663,47 @@ func isURL(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Va
}

func isEmail(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(emailRegex, field.String())
return emailRegex.MatchString(field.String())
}

func isHsla(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hslaRegex, field.String())
return hslaRegex.MatchString(field.String())
}

func isHsl(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hslRegex, field.String())
return hslRegex.MatchString(field.String())
}

func isRgba(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(rgbaRegex, field.String())
return rgbaRegex.MatchString(field.String())
}

func isRgb(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(rgbRegex, field.String())
return rgbRegex.MatchString(field.String())
}

func isHexcolor(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hexcolorRegex, field.String())
return hexcolorRegex.MatchString(field.String())
}

func isHexadecimal(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(hexadecimalRegex, field.String())
return hexadecimalRegex.MatchString(field.String())
}

func isNumber(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(numberRegex, field.String())
return numberRegex.MatchString(field.String())
}

func isNumeric(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(numericRegex, field.String())
return numericRegex.MatchString(field.String())
}

func isAlphanum(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(alphaNumericRegex, field.String())
return alphaNumericRegex.MatchString(field.String())
}

func isAlpha(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
return matchesRegex(alphaRegex, field.String())
return alphaRegex.MatchString(field.String())
}

func hasValue(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
Expand Down
Loading