Skip to content

Commit ecfab4f

Browse files
Dean KarnDean Karn
Dean Karn
authored and
Dean Karn
committed
Merge pull request #69 from bluesuncorp/v5-development
V5 development
2 parents c6a510f + ac10117 commit ecfab4f

File tree

3 files changed

+76
-7
lines changed

3 files changed

+76
-7
lines changed

doc.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ NOTE: Baked In Cross field validation only compares fields on the same struct,
143143
if cross field + cross struct validation is needed your own custom validator
144144
should be implemented.
145145
146+
NOTE2: comma is the default separator of validation tags, if you wish to have a comma
147+
included within the parameter i.e. excludesall=, you will need to use the UTF-8 hex
148+
representation 0x2C, which is replaced in the code as a comma, so the above will
149+
become excludesall=0x2C
150+
146151
Here is a list of the current built in validators:
147152
148153
-

validator.go

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
)
2121

2222
const (
23+
utf8HexComma = "0x2C"
2324
tagSeparator = ","
2425
orSeparator = "|"
2526
noValidationTag = "-"
@@ -30,6 +31,48 @@ const (
3031
structErrMsg = "Struct:%s\n"
3132
)
3233

34+
var structPool *pool
35+
36+
// Pool holds a channelStructErrors.
37+
type pool struct {
38+
pool chan *StructErrors
39+
}
40+
41+
// NewPool creates a new pool of Clients.
42+
func newPool(max int) *pool {
43+
return &pool{
44+
pool: make(chan *StructErrors, max),
45+
}
46+
}
47+
48+
// Borrow a StructErrors from the pool.
49+
func (p *pool) Borrow() *StructErrors {
50+
var c *StructErrors
51+
52+
select {
53+
case c = <-p.pool:
54+
default:
55+
c = &StructErrors{
56+
Errors: map[string]*FieldError{},
57+
StructErrors: map[string]*StructErrors{},
58+
}
59+
}
60+
61+
return c
62+
}
63+
64+
// Return returns a StructErrors to the pool.
65+
func (p *pool) Return(c *StructErrors) {
66+
67+
// c.Struct = ""
68+
69+
select {
70+
case p.pool <- c:
71+
default:
72+
// let it go, let it go...
73+
}
74+
}
75+
3376
type cachedTags struct {
3477
keyVals [][]string
3578
isOrVal bool
@@ -187,6 +230,9 @@ type Validate struct {
187230

188231
// New creates a new Validate instance for use.
189232
func New(tagName string, funcs map[string]Func) *Validate {
233+
234+
structPool = newPool(10)
235+
190236
return &Validate{
191237
tagName: tagName,
192238
validationFuncs: funcs,
@@ -200,6 +246,16 @@ func (v *Validate) SetTag(tagName string) {
200246
v.tagName = tagName
201247
}
202248

249+
// SetStructPoolMax sets the struct pools max size. this may be usefull for fine grained
250+
// performance tuning towards your application, however, the default should be fine for
251+
// nearly all cases. only increase if you have a deeply nested struct structure.
252+
// NOTE: this method is not thread-safe
253+
// NOTE: this is only here to keep compatibility with v5, in v6 the method will be removed
254+
// and the max pool size will be passed into the New function
255+
func (v *Validate) SetMaxStructPoolSize(max int) {
256+
structPool = newPool(max)
257+
}
258+
203259
// AddFunction adds a validation Func to a Validate's map of validators denoted by the key
204260
// NOTE: if the key already exists, it will get replaced.
205261
// NOTE: this method is not thread-safe
@@ -259,11 +315,8 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
259315
structCache.Set(structType, cs)
260316
}
261317

262-
validationErrors := &StructErrors{
263-
Struct: structName,
264-
Errors: map[string]*FieldError{},
265-
StructErrors: map[string]*StructErrors{},
266-
}
318+
validationErrors := structPool.Borrow()
319+
validationErrors.Struct = structName
267320

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

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

361414
if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
415+
structPool.Return(validationErrors)
362416
return nil
363417
}
364418

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

430484
for i, val := range orVals {
431-
vals := strings.Split(val, tagKeySeparator)
485+
vals := strings.SplitN(val, tagKeySeparator, 2)
432486

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

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

439493
param := ""
440494
if len(vals) > 1 {
441-
param = vals[1]
495+
param = strings.Replace(vals[1], utf8HexComma, ",", -1)
442496
}
443497

444498
cTag.keyVals[i] = []string{key, param}

validator_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,16 @@ func TestExcludesAllValidation(t *testing.T) {
281281

282282
err := validate.Field(username, "excludesall=@ ")
283283
NotEqual(t, err, nil)
284+
285+
excluded := ","
286+
287+
err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C?")
288+
NotEqual(t, err, nil)
289+
290+
excluded = "="
291+
292+
err = validate.Field(excluded, "excludesall=!@#$%^&*()_+.0x2C=?")
293+
NotEqual(t, err, nil)
284294
}
285295

286296
func TestExcludesValidation(t *testing.T) {

0 commit comments

Comments
 (0)