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
5 changes: 5 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ NOTE: Baked In Cross field validation only compares fields on the same struct,
if cross field + cross struct validation is needed your own custom validator
should be implemented.

NOTE2: comma is the default separator of validation tags, if you wish to have a comma
included within the parameter i.e. excludesall=, you will need to use the UTF-8 hex
representation 0x2C, which is replaced in the code as a comma, so the above will
become excludesall=0x2C

Here is a list of the current built in validators:

-
Expand Down
68 changes: 61 additions & 7 deletions validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
)

const (
utf8HexComma = "0x2C"
tagSeparator = ","
orSeparator = "|"
noValidationTag = "-"
Expand All @@ -30,6 +31,48 @@ const (
structErrMsg = "Struct:%s\n"
)

var structPool *pool

// Pool holds a channelStructErrors.
type pool struct {
pool chan *StructErrors
}

// NewPool creates a new pool of Clients.
func newPool(max int) *pool {
return &pool{
pool: make(chan *StructErrors, max),
}
}

// Borrow a StructErrors from the pool.
func (p *pool) Borrow() *StructErrors {
var c *StructErrors

select {
case c = <-p.pool:
default:
c = &StructErrors{
Errors: map[string]*FieldError{},
StructErrors: map[string]*StructErrors{},
}
}

return c
}

// Return returns a StructErrors to the pool.
func (p *pool) Return(c *StructErrors) {

// c.Struct = ""

select {
case p.pool <- c:
default:
// let it go, let it go...
}
}

type cachedTags struct {
keyVals [][]string
isOrVal bool
Expand Down Expand Up @@ -187,6 +230,9 @@ type Validate struct {

// New creates a new Validate instance for use.
func New(tagName string, funcs map[string]Func) *Validate {

structPool = newPool(10)

return &Validate{
tagName: tagName,
validationFuncs: funcs,
Expand All @@ -200,6 +246,16 @@ func (v *Validate) SetTag(tagName string) {
v.tagName = tagName
}

// SetStructPoolMax sets the struct pools max size. this may be usefull for fine grained
// performance tuning towards your application, however, the default should be fine for
// nearly all cases. only increase if you have a deeply nested struct structure.
// NOTE: this method is not thread-safe
// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed
// and the max pool size will be passed into the New function
func (v *Validate) SetMaxStructPoolSize(max int) {
structPool = newPool(max)
}

// AddFunction adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced.
// NOTE: this method is not thread-safe
Expand Down Expand Up @@ -259,11 +315,8 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
structCache.Set(structType, cs)
}

validationErrors := &StructErrors{
Struct: structName,
Errors: map[string]*FieldError{},
StructErrors: map[string]*StructErrors{},
}
validationErrors := structPool.Borrow()
validationErrors.Struct = structName

for i := 0; i < numFields; i++ {

Expand Down Expand Up @@ -359,6 +412,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
}

if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
structPool.Return(validationErrors)
return nil
}

Expand Down Expand Up @@ -428,7 +482,7 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
cField.tags = append(cField.tags, cTag)

for i, val := range orVals {
vals := strings.Split(val, tagKeySeparator)
vals := strings.SplitN(val, tagKeySeparator, 2)

key := strings.TrimSpace(vals[0])

Expand All @@ -438,7 +492,7 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f

param := ""
if len(vals) > 1 {
param = vals[1]
param = strings.Replace(vals[1], utf8HexComma, ",", -1)
}

cTag.keyVals[i] = []string{key, param}
Expand Down
10 changes: 10 additions & 0 deletions validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ func TestExcludesAllValidation(t *testing.T) {

err := validate.Field(username, "excludesall=@ ")
NotEqual(t, err, nil)

excluded := ","

err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?")
NotEqual(t, err, nil)

excluded = "="

err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?")
NotEqual(t, err, nil)
}

func TestExcludesValidation(t *testing.T) {
Expand Down