• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-playground / validator / 6774949602

06 Nov 2023 06:24PM UTC coverage: 73.926% (-0.05%) from 73.972%
6774949602

Pull #1115

github

yousifnimah
Fixes
Pull Request #1115: Efficient Regex Validation Integration

86 of 110 new or added lines in 18 files covered. (78.18%)

473 existing lines in 22 files now uncovered.

11636 of 15740 relevant lines covered (73.93%)

85.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.48
/validator_instance.go
1
package validator
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "reflect"
8
        "strings"
9
        "sync"
10
        "time"
11

12
        ut "github.com/go-playground/universal-translator"
13
)
14

15
const (
16
        defaultTagName        = "validate"
17
        utf8HexComma          = "0x2C"
18
        utf8Pipe              = "0x7C"
19
        tagSeparator          = ","
20
        orSeparator           = "|"
21
        tagKeySeparator       = "="
22
        structOnlyTag         = "structonly"
23
        noStructLevelTag      = "nostructlevel"
24
        omitempty             = "omitempty"
25
        isdefault             = "isdefault"
26
        requiredWithoutAllTag = "required_without_all"
27
        requiredWithoutTag    = "required_without"
28
        requiredWithTag       = "required_with"
29
        requiredWithAllTag    = "required_with_all"
30
        requiredIfTag         = "required_if"
31
        requiredUnlessTag     = "required_unless"
32
        skipUnlessTag         = "skip_unless"
33
        excludedWithoutAllTag = "excluded_without_all"
34
        excludedWithoutTag    = "excluded_without"
35
        excludedWithTag       = "excluded_with"
36
        excludedWithAllTag    = "excluded_with_all"
37
        excludedIfTag         = "excluded_if"
38
        excludedUnlessTag     = "excluded_unless"
39
        skipValidationTag     = "-"
40
        diveTag               = "dive"
41
        keysTag               = "keys"
42
        endKeysTag            = "endkeys"
43
        requiredTag           = "required"
44
        namespaceSeparator    = "."
45
        leftBracket           = "["
46
        rightBracket          = "]"
47
        restrictedTagChars    = ".[],|=+()`~!@#$%^&*\\\"/?<>{}"
48
        restrictedAliasErr    = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
49
        restrictedTagErr      = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
50
)
51

52
var (
53
        timeDurationType = reflect.TypeOf(time.Duration(0))
54
        timeType         = reflect.TypeOf(time.Time{})
55

56
        byteSliceType = reflect.TypeOf([]byte{})
57

58
        defaultCField = &cField{namesEqual: true}
59
)
60

61
// FilterFunc is the type used to filter fields using
62
// StructFiltered(...) function.
63
// returning true results in the field being filtered/skipped from
64
// validation
65
type FilterFunc func(ns []byte) bool
66

67
// CustomTypeFunc allows for overriding or adding custom field type handler functions
68
// field = field value of the type to return a value to be validated
69
// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
70
type CustomTypeFunc func(field reflect.Value) interface{}
71

72
// TagNameFunc allows for adding of a custom tag name parser
73
type TagNameFunc func(field reflect.StructField) string
74

75
type internalValidationFuncWrapper struct {
76
        fn                FuncCtx
77
        runValidatinOnNil bool
78
}
79

80
// Validate contains the validator settings and cache
81
type Validate struct {
82
        tagName          string
83
        pool             *sync.Pool
84
        hasCustomFuncs   bool
85
        hasTagNameFunc   bool
86
        tagNameFunc      TagNameFunc
87
        structLevelFuncs map[reflect.Type]StructLevelFuncCtx
88
        customFuncs      map[reflect.Type]CustomTypeFunc
89
        aliases          map[string]string
90
        validations      map[string]internalValidationFuncWrapper
91
        transTagFunc     map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
92
        rules            map[reflect.Type]map[string]string
93
        tagCache         *tagCache
94
        structCache      *structCache
95
}
96

97
// New returns a new instance of 'validate' with sane defaults.
98
// Validate is designed to be thread-safe and used as a singleton instance.
99
// It caches information about your struct and validations,
100
// in essence only parsing your validation tags once per struct type.
101
// Using multiple instances neglects the benefit of caching.
102
func New() *Validate {
103

104
        tc := new(tagCache)
254✔
105
        tc.m.Store(make(map[string]*cTag))
254✔
106

254✔
107
        sc := new(structCache)
254✔
108
        sc.m.Store(make(map[reflect.Type]*cStruct))
254✔
109

254✔
110
        v := &Validate{
254✔
111
                tagName:     defaultTagName,
254✔
112
                aliases:     make(map[string]string, len(bakedInAliases)),
254✔
113
                validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)),
254✔
114
                tagCache:    tc,
254✔
115
                structCache: sc,
254✔
116
        }
254✔
117

254✔
118
        // must copy alias validators for separate validations to be used in each validator instance
254✔
119
        for k, val := range bakedInAliases {
254✔
120
                v.RegisterAlias(k, val)
254✔
121
        }
762✔
122

508✔
123
        // must copy validators for separate validations to be used in each instance
508✔
124
        for k, val := range bakedInValidators {
125

126
                switch k {
41,402✔
127
                // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
41,148✔
128
                case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
41,148✔
129
                        excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag,
130
                        skipUnlessTag:
131
                        _ = v.registerValidation(k, wrapFunc(val), true, true)
132
                default:
3,302✔
133
                        // no need to error check here, baked in will always be valid
3,302✔
134
                        _ = v.registerValidation(k, wrapFunc(val), true, false)
37,846✔
135
                }
37,846✔
136
        }
37,846✔
137

138
        v.pool = &sync.Pool{
139
                New: func() interface{} {
140
                        return &validate{
254✔
141
                                v:        v,
983✔
142
                                ns:       make([]byte, 0, 64),
729✔
143
                                actualNs: make([]byte, 0, 64),
729✔
144
                                misc:     make([]byte, 32),
729✔
145
                        }
729✔
146
                },
729✔
147
        }
729✔
148

729✔
149
        return v
150
}
151

257✔
152
// SetTagName allows for changing of the default tag name of 'validate'
3✔
153
func (v *Validate) SetTagName(name string) {
3✔
154
        v.tagName = name
254✔
155
}
156

157
// ValidateMapCtx validates a map using a map of validation rules and allows passing of contextual
158
// validation information via context.Context.
1✔
159
func (v Validate) ValidateMapCtx(ctx context.Context, data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
1✔
160
        errs := make(map[string]interface{})
1✔
161
        for field, rule := range rules {
162
                if ruleObj, ok := rule.(map[string]interface{}); ok {
163
                        if dataObj, ok := data[field].(map[string]interface{}); ok {
164
                                err := v.ValidateMapCtx(ctx, dataObj, ruleObj)
8✔
165
                                if len(err) > 0 {
8✔
166
                                        errs[field] = err
22✔
167
                                }
22✔
168
                        } else if dataObjs, ok := data[field].([]map[string]interface{}); ok {
12✔
169
                                for _, obj := range dataObjs {
4✔
170
                                        err := v.ValidateMapCtx(ctx, obj, ruleObj)
6✔
171
                                        if len(err) > 0 {
2✔
172
                                                errs[field] = err
2✔
173
                                        }
6✔
174
                                }
4✔
175
                        } else {
2✔
176
                                errs[field] = errors.New("The field: '" + field + "' is not a map to dive")
3✔
177
                        }
1✔
178
                } else if ruleStr, ok := rule.(string); ok {
1✔
179
                        err := v.VarCtx(ctx, data[field], ruleStr)
180
                        if err != nil {
2✔
181
                                errs[field] = err
2✔
182
                        }
2✔
183
                }
12✔
184
        }
6✔
185
        return errs
8✔
186
}
2✔
187

2✔
188
// ValidateMap validates map data from a map of tags
189
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
190
        return v.ValidateMapCtx(context.Background(), data, rules)
8✔
191
}
192

193
// RegisterTagNameFunc registers a function to get alternate names for StructFields.
UNCOV
194
//
×
UNCOV
195
// eg. to use the names which have been specified for JSON representations of structs, rather than normal Go field names:
×
UNCOV
196
//
×
197
//        validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
198
//            name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
199
//            // skip if tag key says it should be ignored
200
//            if name == "-" {
201
//                return ""
202
//            }
203
//            return name
204
//        })
205
func (v *Validate) RegisterTagNameFunc(fn TagNameFunc) {
206
        v.tagNameFunc = fn
207
        v.hasTagNameFunc = true
208
}
209

210
// RegisterValidation adds a validation with the given tag
14✔
211
//
14✔
212
// NOTES:
14✔
213
// - if the key already exists, the previous validation function will be replaced.
14✔
214
// - this method is not thread-safe it is intended that these all be registered prior to any validation
215
func (v *Validate) RegisterValidation(tag string, fn Func, callValidationEvenIfNull ...bool) error {
216
        return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...)
217
}
218

219
// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
220
// allowing context.Context validation support.
16✔
221
func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error {
16✔
222
        var nilCheckable bool
16✔
223
        if len(callValidationEvenIfNull) > 0 {
224
                nilCheckable = callValidationEvenIfNull[0]
225
        }
226
        return v.registerValidation(tag, fn, false, nilCheckable)
18✔
227
}
18✔
228

19✔
229
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
1✔
230
        if len(tag) == 0 {
1✔
231
                return errors.New("function Key cannot be empty")
18✔
232
        }
233

234
        if fn == nil {
41,166✔
235
                return errors.New("function cannot be empty")
41,167✔
236
        }
1✔
237

1✔
238
        _, ok := restrictedTags[tag]
239
        if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
41,166✔
240
                panic(fmt.Sprintf(restrictedTagErr, tag))
1✔
241
        }
1✔
242
        v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable}
243
        return nil
41,164✔
244
}
41,165✔
245

1✔
246
// RegisterAlias registers a mapping of a single validation tag that
247
// defines a common or complex set of validation(s) to simplify adding validation
41,163✔
248
// to structs.
41,163✔
249
//
250
// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
251
func (v *Validate) RegisterAlias(alias, tags string) {
252

253
        _, ok := restrictedTags[alias]
254

255
        if ok || strings.ContainsAny(alias, restrictedTagChars) {
256
                panic(fmt.Sprintf(restrictedAliasErr, alias))
513✔
257
        }
513✔
258

513✔
259
        v.aliases[alias] = tags
513✔
260
}
514✔
261

1✔
262
// RegisterStructValidation registers a StructLevelFunc against a number of types.
263
//
264
// NOTE:
512✔
265
// - this method is not thread-safe it is intended that these all be registered prior to any validation
266
func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) {
267
        v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...)
268
}
269

270
// RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing
271
// of contextual validation information via context.Context.
8✔
272
//
8✔
273
// NOTE:
8✔
274
// - this method is not thread-safe it is intended that these all be registered prior to any validation
275
func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
276

277
        if v.structLevelFuncs == nil {
278
                v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
279
        }
280

9✔
281
        for _, t := range types {
9✔
282
                tv := reflect.ValueOf(t)
18✔
283
                if tv.Kind() == reflect.Ptr {
9✔
284
                        t = reflect.Indirect(tv).Interface()
9✔
285
                }
286

18✔
287
                v.structLevelFuncs[reflect.TypeOf(t)] = fn
9✔
288
        }
10✔
289
}
1✔
290

1✔
291
// RegisterStructValidationMapRules registers validate map rules.
292
// Be aware that map validation rules supersede those defined on a/the struct if present.
9✔
293
//
294
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
295
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
296
        if v.rules == nil {
297
                v.rules = make(map[reflect.Type]map[string]string)
298
        }
299

300
        deepCopyRules := make(map[string]string)
×
301
        for i, rule := range rules {
×
302
                deepCopyRules[i] = rule
×
303
        }
×
304

305
        for _, t := range types {
×
306
                typ := reflect.TypeOf(t)
×
307

×
308
                if typ.Kind() == reflect.Ptr {
×
309
                        typ = typ.Elem()
310
                }
×
UNCOV
311

×
312
                if typ.Kind() != reflect.Struct {
×
313
                        continue
×
UNCOV
314
                }
×
315
                v.rules[typ] = deepCopyRules
×
316
        }
UNCOV
317
}
×
UNCOV
318

×
319
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
UNCOV
320
//
×
321
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
322
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
323

324
        if v.customFuncs == nil {
325
                v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
326
        }
327

7✔
328
        for _, t := range types {
7✔
329
                v.customFuncs[reflect.TypeOf(t)] = fn
9✔
330
        }
2✔
331

2✔
332
        v.hasCustomFuncs = true
333
}
18✔
334

12✔
335
// RegisterTranslation registers translations against the provided tag.
12✔
336
func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
337

6✔
338
        if v.transTagFunc == nil {
339
                v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
340
        }
341

3✔
342
        if err = registerFn(trans); err != nil {
3✔
343
                return
5✔
344
        }
2✔
345

2✔
346
        m, ok := v.transTagFunc[trans]
347
        if !ok {
4✔
348
                m = make(map[string]TranslationFunc)
1✔
349
                v.transTagFunc[trans] = m
1✔
350
        }
351

2✔
352
        m[tag] = translationFn
4✔
353

2✔
354
        return
2✔
355
}
2✔
356

357
// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
2✔
358
//
2✔
359
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
2✔
360
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
361
func (v *Validate) Struct(s interface{}) error {
362
        return v.StructCtx(context.Background(), s)
363
}
364

365
// StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified
366
// and also allows passing of context.Context for contextual validation information.
436✔
367
//
436✔
368
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
436✔
369
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
370
func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
371

372
        val := reflect.ValueOf(s)
373
        top := val
374

375
        if val.Kind() == reflect.Ptr && !val.IsNil() {
437✔
376
                val = val.Elem()
437✔
377
        }
437✔
378

437✔
379
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
437✔
380
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
683✔
381
        }
246✔
382

246✔
383
        // good to validate
384
        vd := v.pool.Get().(*validate)
439✔
385
        vd.top = top
2✔
386
        vd.isPartial = false
2✔
387
        // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
388

389
        vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
435✔
390

435✔
391
        if len(vd.errs) > 0 {
435✔
392
                err = vd.errs
435✔
393
                vd.errs = nil
435✔
394
        }
435✔
395

435✔
396
        v.pool.Put(vd)
645✔
397

210✔
398
        return
210✔
399
}
210✔
400

401
// StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates
425✔
402
// nested structs, unless otherwise specified.
425✔
403
//
425✔
404
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
405
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
406
func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error {
407
        return v.StructFilteredCtx(context.Background(), s, fn)
408
}
409

410
// StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates
411
// nested structs, unless otherwise specified and also allows passing of contextual validation information via
14✔
412
// context.Context
14✔
413
//
14✔
414
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
415
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
416
func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) {
417
        val := reflect.ValueOf(s)
418
        top := val
419

420
        if val.Kind() == reflect.Ptr && !val.IsNil() {
421
                val = val.Elem()
15✔
422
        }
15✔
423

15✔
424
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
15✔
425
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
30✔
426
        }
15✔
427

15✔
428
        // good to validate
429
        vd := v.pool.Get().(*validate)
16✔
430
        vd.top = top
1✔
431
        vd.isPartial = true
1✔
432
        vd.ffn = fn
433
        // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
434

14✔
435
        vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
14✔
436

14✔
437
        if len(vd.errs) > 0 {
14✔
438
                err = vd.errs
14✔
439
                vd.errs = nil
14✔
440
        }
14✔
441

14✔
442
        v.pool.Put(vd)
18✔
443

4✔
444
        return
4✔
445
}
4✔
446

447
// StructPartial validates the fields passed in only, ignoring all others.
14✔
448
// Fields may be provided in a namespaced fashion relative to the  struct provided
14✔
449
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
14✔
450
//
451
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
452
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
453
func (v *Validate) StructPartial(s interface{}, fields ...string) error {
454
        return v.StructPartialCtx(context.Background(), s, fields...)
455
}
456

457
// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
458
// validation information via context.Context
22✔
459
// Fields may be provided in a namespaced fashion relative to the  struct provided
22✔
460
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
22✔
461
//
462
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
463
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
464
func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
465
        val := reflect.ValueOf(s)
466
        top := val
467

468
        if val.Kind() == reflect.Ptr && !val.IsNil() {
469
                val = val.Elem()
23✔
470
        }
23✔
471

23✔
472
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
23✔
473
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
40✔
474
        }
17✔
475

17✔
476
        // good to validate
477
        vd := v.pool.Get().(*validate)
24✔
478
        vd.top = top
1✔
479
        vd.isPartial = true
1✔
480
        vd.ffn = nil
481
        vd.hasExcludes = false
482
        vd.includeExclude = make(map[string]struct{})
22✔
483

22✔
484
        typ := val.Type()
22✔
485
        name := typ.Name()
22✔
486

22✔
487
        for _, k := range fields {
22✔
488

22✔
489
                flds := strings.Split(k, namespaceSeparator)
22✔
490
                if len(flds) > 0 {
22✔
491

22✔
492
                        vd.misc = append(vd.misc[0:0], name...)
71✔
493
                        // Don't append empty name for unnamed structs
49✔
494
                        if len(vd.misc) != 0 {
49✔
495
                                vd.misc = append(vd.misc, '.')
98✔
496
                        }
49✔
497

49✔
498
                        for _, s := range flds {
49✔
499

94✔
500
                                idx := strings.Index(s, leftBracket)
45✔
501

45✔
502
                                if idx != -1 {
503
                                        for idx != -1 {
115✔
504
                                                vd.misc = append(vd.misc, s[:idx]...)
66✔
505
                                                vd.includeExclude[string(vd.misc)] = struct{}{}
66✔
506

66✔
507
                                                idx2 := strings.Index(s, rightBracket)
73✔
508
                                                idx2++
14✔
509
                                                vd.misc = append(vd.misc, s[idx:idx2]...)
7✔
510
                                                vd.includeExclude[string(vd.misc)] = struct{}{}
7✔
511
                                                s = s[idx2:]
7✔
512
                                                idx = strings.Index(s, leftBracket)
7✔
513
                                        }
7✔
514
                                } else {
7✔
515

7✔
516
                                        vd.misc = append(vd.misc, s...)
7✔
517
                                        vd.includeExclude[string(vd.misc)] = struct{}{}
7✔
518
                                }
7✔
519

59✔
520
                                vd.misc = append(vd.misc, '.')
59✔
521
                        }
59✔
522
                }
59✔
523
        }
59✔
524

525
        vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
66✔
526

527
        if len(vd.errs) > 0 {
528
                err = vd.errs
529
                vd.errs = nil
530
        }
21✔
531

21✔
532
        v.pool.Put(vd)
28✔
533

7✔
534
        return
7✔
535
}
7✔
536

537
// StructExcept validates all fields except the ones passed in.
21✔
538
// Fields may be provided in a namespaced fashion relative to the  struct provided
21✔
539
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
21✔
540
//
541
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
542
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
543
func (v *Validate) StructExcept(s interface{}, fields ...string) error {
544
        return v.StructExceptCtx(context.Background(), s, fields...)
545
}
546

547
// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
548
// validation information via context.Context
16✔
549
// Fields may be provided in a namespaced fashion relative to the  struct provided
16✔
550
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
16✔
551
//
552
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
553
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
554
func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
555
        val := reflect.ValueOf(s)
556
        top := val
557

558
        if val.Kind() == reflect.Ptr && !val.IsNil() {
559
                val = val.Elem()
17✔
560
        }
17✔
561

17✔
562
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
17✔
563
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
31✔
564
        }
14✔
565

14✔
566
        // good to validate
567
        vd := v.pool.Get().(*validate)
18✔
568
        vd.top = top
1✔
569
        vd.isPartial = true
1✔
570
        vd.ffn = nil
571
        vd.hasExcludes = true
572
        vd.includeExclude = make(map[string]struct{})
16✔
573

16✔
574
        typ := val.Type()
16✔
575
        name := typ.Name()
16✔
576

16✔
577
        for _, key := range fields {
16✔
578

16✔
579
                vd.misc = vd.misc[0:0]
16✔
580

16✔
581
                if len(name) > 0 {
16✔
582
                        vd.misc = append(vd.misc, name...)
59✔
583
                        vd.misc = append(vd.misc, '.')
43✔
584
                }
43✔
585

43✔
586
                vd.misc = append(vd.misc, key...)
85✔
587
                vd.includeExclude[string(vd.misc)] = struct{}{}
42✔
588
        }
42✔
589

42✔
590
        vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
591

43✔
592
        if len(vd.errs) > 0 {
43✔
593
                err = vd.errs
594
                vd.errs = nil
595
        }
15✔
596

15✔
597
        v.pool.Put(vd)
24✔
598

9✔
599
        return
9✔
600
}
9✔
601

602
// Var validates a single variable using tag style validation.
15✔
603
// eg.
15✔
604
// var i int
15✔
605
// validate.Var(i, "gt=1,lt=10")
606
//
607
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
608
// if you have a custom type and have registered a custom type handler, so must
609
// allow it; however unforeseen validations will occur if trying to validate a
610
// struct that is meant to be passed to 'validate.Struct'
611
//
612
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
613
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
614
// validate Array, Slice and maps fields which may contain more than one error
615
func (v *Validate) Var(field interface{}, tag string) error {
616
        return v.VarCtx(context.Background(), field, tag)
617
}
618

619
// VarCtx validates a single variable using tag style validation and allows passing of contextual
620
// validation information via context.Context.
1,468✔
621
// eg.
1,468✔
622
// var i int
1,468✔
623
// validate.Var(i, "gt=1,lt=10")
624
//
625
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
626
// if you have a custom type and have registered a custom type handler, so must
627
// allow it; however unforeseen validations will occur if trying to validate a
628
// struct that is meant to be passed to 'validate.Struct'
629
//
630
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
631
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
632
// validate Array, Slice and maps fields which may contain more than one error
633
func (v *Validate) VarCtx(ctx context.Context, field interface{}, tag string) (err error) {
634
        if len(tag) == 0 || tag == skipValidationTag {
635
                return nil
636
        }
637

638
        ctag := v.fetchCacheTag(tag)
1,475✔
639

1,477✔
640
        val := reflect.ValueOf(field)
2✔
641
        vd := v.pool.Get().(*validate)
2✔
642
        vd.top = val
643
        vd.isPartial = false
1,473✔
644
        vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
1,473✔
645

1,473✔
646
        if len(vd.errs) > 0 {
1,473✔
647
                err = vd.errs
1,473✔
648
                vd.errs = nil
1,473✔
649
        }
1,473✔
650
        v.pool.Put(vd)
1,473✔
651
        return
2,242✔
652
}
769✔
653

769✔
654
// VarWithValue validates a single variable, against another variable/field's value using tag style validation
769✔
655
// eg.
1,430✔
656
// s1 := "abcd"
1,430✔
657
// s2 := "abcd"
658
// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
659
//
660
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
661
// if you have a custom type and have registered a custom type handler, so must
662
// allow it; however unforeseen validations will occur if trying to validate a
663
// struct that is meant to be passed to 'validate.Struct'
664
//
665
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
666
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
667
// validate Array, Slice and maps fields which may contain more than one error
668
func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error {
669
        return v.VarWithValueCtx(context.Background(), field, other, tag)
670
}
671

672
// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
673
// allows passing of contextual validation validation information via context.Context.
160✔
674
// eg.
160✔
675
// s1 := "abcd"
160✔
676
// s2 := "abcd"
677
// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
678
//
679
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
680
// if you have a custom type and have registered a custom type handler, so must
681
// allow it; however unforeseen validations will occur if trying to validate a
682
// struct that is meant to be passed to 'validate.Struct'
683
//
684
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
685
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
686
// validate Array, Slice and maps fields which may contain more than one error
687
func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) {
688
        if len(tag) == 0 || tag == skipValidationTag {
689
                return nil
690
        }
691
        ctag := v.fetchCacheTag(tag)
692
        otherVal := reflect.ValueOf(other)
161✔
693
        vd := v.pool.Get().(*validate)
162✔
694
        vd.top = otherVal
1✔
695
        vd.isPartial = false
1✔
696
        vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
160✔
697

160✔
698
        if len(vd.errs) > 0 {
160✔
699
                err = vd.errs
160✔
700
                vd.errs = nil
160✔
701
        }
160✔
702
        v.pool.Put(vd)
160✔
703
        return
239✔
704
}
79✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc