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

go-playground / validator / 13663369444

04 Mar 2025 09:33PM UTC coverage: 74.823% (+0.5%) from 74.318%
13663369444

Pull #1353

github

nodivbyzero
Multiple consecutive dots
Pull Request #1353: Email regex optimization

14351 of 19180 relevant lines covered (74.82%)

76.55 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
        omitnil               = "omitnil"
26
        isdefault             = "isdefault"
27
        requiredWithoutAllTag = "required_without_all"
28
        requiredWithoutTag    = "required_without"
29
        requiredWithTag       = "required_with"
30
        requiredWithAllTag    = "required_with_all"
31
        requiredIfTag         = "required_if"
32
        requiredUnlessTag     = "required_unless"
33
        skipUnlessTag         = "skip_unless"
34
        excludedWithoutAllTag = "excluded_without_all"
35
        excludedWithoutTag    = "excluded_without"
36
        excludedWithTag       = "excluded_with"
37
        excludedWithAllTag    = "excluded_with_all"
38
        excludedIfTag         = "excluded_if"
39
        excludedUnlessTag     = "excluded_unless"
40
        skipValidationTag     = "-"
41
        diveTag               = "dive"
42
        keysTag               = "keys"
43
        endKeysTag            = "endkeys"
44
        requiredTag           = "required"
45
        namespaceSeparator    = "."
46
        leftBracket           = "["
47
        rightBracket          = "]"
48
        restrictedTagChars    = ".[],|=+()`~!@#$%^&*\\\"/?<>{}"
49
        restrictedAliasErr    = "Alias '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
50
        restrictedTagErr      = "Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation"
51
)
52

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

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

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

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

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

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

76
type internalValidationFuncWrapper struct {
77
        fn                 FuncCtx
78
        runValidationOnNil bool
79
}
80

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

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

268✔
107
        tc := new(tagCache)
268✔
108
        tc.m.Store(make(map[string]*cTag))
268✔
109

268✔
110
        sc := new(structCache)
268✔
111
        sc.m.Store(make(map[reflect.Type]*cStruct))
268✔
112

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

268✔
121
        // must copy alias validators for separate validations to be used in each validator instance
268✔
122
        for k, val := range bakedInAliases {
268✔
123
                v.RegisterAlias(k, val)
1,072✔
124
        }
804✔
125

804✔
126
        // must copy validators for separate validations to be used in each instance
127
        for k, val := range bakedInValidators {
128

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

141
        v.pool = &sync.Pool{
142
                New: func() interface{} {
268✔
143
                        return &validate{
1,044✔
144
                                v:        v,
776✔
145
                                ns:       make([]byte, 0, 64),
776✔
146
                                actualNs: make([]byte, 0, 64),
776✔
147
                                misc:     make([]byte, 32),
776✔
148
                        }
776✔
149
                },
776✔
150
        }
776✔
151

152
        for _, o := range options {
153
                o(v)
272✔
154
        }
4✔
155
        return v
4✔
156
}
268✔
157

158
// SetTagName allows for changing of the default tag name of 'validate'
159
func (v *Validate) SetTagName(name string) {
160
        v.tagName = name
1✔
161
}
1✔
162

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

194
// ValidateMap validates map data from a map of tags
195
func (v *Validate) ValidateMap(data map[string]interface{}, rules map[string]interface{}) map[string]interface{} {
196
        return v.ValidateMapCtx(context.Background(), data, rules)
×
197
}
×
198

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

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

16✔
225
// RegisterValidationCtx does the same as RegisterValidation on accepts a FuncCtx validation
226
// allowing context.Context validation support.
227
func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationEvenIfNull ...bool) error {
228
        var nilCheckable bool
18✔
229
        if len(callValidationEvenIfNull) > 0 {
18✔
230
                nilCheckable = callValidationEvenIfNull[0]
19✔
231
        }
1✔
232
        return v.registerValidation(tag, fn, false, nilCheckable)
1✔
233
}
18✔
234

235
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
236
        if len(tag) == 0 {
45,042✔
237
                return errors.New("function Key cannot be empty")
45,043✔
238
        }
1✔
239

1✔
240
        if fn == nil {
241
                return errors.New("function cannot be empty")
45,042✔
242
        }
1✔
243

1✔
244
        _, ok := restrictedTags[tag]
245
        if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
45,040✔
246
                panic(fmt.Sprintf(restrictedTagErr, tag))
45,041✔
247
        }
1✔
248
        v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidationOnNil: nilCheckable}
249
        return nil
45,039✔
250
}
45,039✔
251

252
// RegisterAlias registers a mapping of a single validation tag that
253
// defines a common or complex set of validation(s) to simplify adding validation
254
// to structs.
255
//
256
// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
257
func (v *Validate) RegisterAlias(alias, tags string) {
258

809✔
259
        _, ok := restrictedTags[alias]
809✔
260

809✔
261
        if ok || strings.ContainsAny(alias, restrictedTagChars) {
809✔
262
                panic(fmt.Sprintf(restrictedAliasErr, alias))
810✔
263
        }
1✔
264

265
        v.aliases[alias] = tags
266
}
808✔
267

268
// RegisterStructValidation registers a StructLevelFunc against a number of types.
269
//
270
// NOTE:
271
// - this method is not thread-safe it is intended that these all be registered prior to any validation
272
func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) {
273
        v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...)
8✔
274
}
8✔
275

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

9✔
283
        if v.structLevelFuncs == nil {
9✔
284
                v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
18✔
285
        }
9✔
286

9✔
287
        for _, t := range types {
288
                tv := reflect.ValueOf(t)
18✔
289
                if tv.Kind() == reflect.Ptr {
9✔
290
                        t = reflect.Indirect(tv).Interface()
10✔
291
                }
1✔
292

1✔
293
                v.structLevelFuncs[reflect.TypeOf(t)] = fn
294
        }
9✔
295
}
296

297
// RegisterStructValidationMapRules registers validate map rules.
298
// Be aware that map validation rules supersede those defined on a/the struct if present.
299
//
300
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
301
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
302
        if v.rules == nil {
×
303
                v.rules = make(map[reflect.Type]map[string]string)
×
304
        }
×
305

×
306
        deepCopyRules := make(map[string]string)
307
        for i, rule := range rules {
×
308
                deepCopyRules[i] = rule
×
309
        }
×
310

×
311
        for _, t := range types {
312
                typ := reflect.TypeOf(t)
×
313

×
314
                if typ.Kind() == reflect.Ptr {
×
315
                        typ = typ.Elem()
×
316
                }
×
317

×
318
                if typ.Kind() != reflect.Struct {
319
                        continue
×
320
                }
×
321
                v.rules[typ] = deepCopyRules
322
        }
×
323
}
324

325
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
326
//
327
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
328
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
329

7✔
330
        if v.customFuncs == nil {
7✔
331
                v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
9✔
332
        }
2✔
333

2✔
334
        for _, t := range types {
335
                v.customFuncs[reflect.TypeOf(t)] = fn
18✔
336
        }
12✔
337

12✔
338
        v.hasCustomFuncs = true
339
}
6✔
340

341
// RegisterTranslation registers translations against the provided tag.
342
func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
343

3✔
344
        if v.transTagFunc == nil {
3✔
345
                v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
5✔
346
        }
2✔
347

2✔
348
        if err = registerFn(trans); err != nil {
349
                return
4✔
350
        }
1✔
351

1✔
352
        m, ok := v.transTagFunc[trans]
353
        if !ok {
2✔
354
                m = make(map[string]TranslationFunc)
4✔
355
                v.transTagFunc[trans] = m
2✔
356
        }
2✔
357

2✔
358
        m[tag] = translationFn
359

2✔
360
        return
2✔
361
}
2✔
362

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

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

462✔
378
        val := reflect.ValueOf(s)
462✔
379
        top := val
462✔
380

462✔
381
        if val.Kind() == reflect.Ptr && !val.IsNil() {
462✔
382
                val = val.Elem()
713✔
383
        }
251✔
384

251✔
385
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
386
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
464✔
387
        }
2✔
388

2✔
389
        // good to validate
390
        vd := v.pool.Get().(*validate)
391
        vd.top = top
460✔
392
        vd.isPartial = false
460✔
393
        // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
460✔
394

460✔
395
        vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
460✔
396

460✔
397
        if len(vd.errs) > 0 {
460✔
398
                err = vd.errs
684✔
399
                vd.errs = nil
224✔
400
        }
224✔
401

224✔
402
        v.pool.Put(vd)
403

450✔
404
        return
450✔
405
}
450✔
406

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

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

15✔
426
        if val.Kind() == reflect.Ptr && !val.IsNil() {
15✔
427
                val = val.Elem()
30✔
428
        }
15✔
429

15✔
430
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
431
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
16✔
432
        }
1✔
433

1✔
434
        // good to validate
435
        vd := v.pool.Get().(*validate)
436
        vd.top = top
14✔
437
        vd.isPartial = true
14✔
438
        vd.ffn = fn
14✔
439
        // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
14✔
440

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

14✔
443
        if len(vd.errs) > 0 {
14✔
444
                err = vd.errs
18✔
445
                vd.errs = nil
4✔
446
        }
4✔
447

4✔
448
        v.pool.Put(vd)
449

14✔
450
        return
14✔
451
}
14✔
452

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

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

23✔
474
        if val.Kind() == reflect.Ptr && !val.IsNil() {
23✔
475
                val = val.Elem()
40✔
476
        }
17✔
477

17✔
478
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
479
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
24✔
480
        }
1✔
481

1✔
482
        // good to validate
483
        vd := v.pool.Get().(*validate)
484
        vd.top = top
22✔
485
        vd.isPartial = true
22✔
486
        vd.ffn = nil
22✔
487
        vd.hasExcludes = false
22✔
488
        vd.includeExclude = make(map[string]struct{})
22✔
489

22✔
490
        typ := val.Type()
22✔
491
        name := typ.Name()
22✔
492

22✔
493
        for _, k := range fields {
22✔
494

71✔
495
                flds := strings.Split(k, namespaceSeparator)
49✔
496
                if len(flds) > 0 {
49✔
497

98✔
498
                        vd.misc = append(vd.misc[0:0], name...)
49✔
499
                        // Don't append empty name for unnamed structs
49✔
500
                        if len(vd.misc) != 0 {
49✔
501
                                vd.misc = append(vd.misc, '.')
94✔
502
                        }
45✔
503

45✔
504
                        for _, s := range flds {
505

115✔
506
                                idx := strings.Index(s, leftBracket)
66✔
507

66✔
508
                                if idx != -1 {
66✔
509
                                        for idx != -1 {
73✔
510
                                                vd.misc = append(vd.misc, s[:idx]...)
14✔
511
                                                vd.includeExclude[string(vd.misc)] = struct{}{}
7✔
512

7✔
513
                                                idx2 := strings.Index(s, rightBracket)
7✔
514
                                                idx2++
7✔
515
                                                vd.misc = append(vd.misc, s[idx:idx2]...)
7✔
516
                                                vd.includeExclude[string(vd.misc)] = struct{}{}
7✔
517
                                                s = s[idx2:]
7✔
518
                                                idx = strings.Index(s, leftBracket)
7✔
519
                                        }
7✔
520
                                } else {
7✔
521

59✔
522
                                        vd.misc = append(vd.misc, s...)
59✔
523
                                        vd.includeExclude[string(vd.misc)] = struct{}{}
59✔
524
                                }
59✔
525

59✔
526
                                vd.misc = append(vd.misc, '.')
527
                        }
66✔
528
                }
529
        }
530

531
        vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
532

21✔
533
        if len(vd.errs) > 0 {
21✔
534
                err = vd.errs
28✔
535
                vd.errs = nil
7✔
536
        }
7✔
537

7✔
538
        v.pool.Put(vd)
539

21✔
540
        return
21✔
541
}
21✔
542

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

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

17✔
564
        if val.Kind() == reflect.Ptr && !val.IsNil() {
17✔
565
                val = val.Elem()
31✔
566
        }
14✔
567

14✔
568
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
569
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
18✔
570
        }
1✔
571

1✔
572
        // good to validate
573
        vd := v.pool.Get().(*validate)
574
        vd.top = top
16✔
575
        vd.isPartial = true
16✔
576
        vd.ffn = nil
16✔
577
        vd.hasExcludes = true
16✔
578
        vd.includeExclude = make(map[string]struct{})
16✔
579

16✔
580
        typ := val.Type()
16✔
581
        name := typ.Name()
16✔
582

16✔
583
        for _, key := range fields {
16✔
584

59✔
585
                vd.misc = vd.misc[0:0]
43✔
586

43✔
587
                if len(name) > 0 {
43✔
588
                        vd.misc = append(vd.misc, name...)
85✔
589
                        vd.misc = append(vd.misc, '.')
42✔
590
                }
42✔
591

42✔
592
                vd.misc = append(vd.misc, key...)
593
                vd.includeExclude[string(vd.misc)] = struct{}{}
43✔
594
        }
43✔
595

596
        vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
597

15✔
598
        if len(vd.errs) > 0 {
15✔
599
                err = vd.errs
24✔
600
                vd.errs = nil
9✔
601
        }
9✔
602

9✔
603
        v.pool.Put(vd)
604

15✔
605
        return
15✔
606
}
15✔
607

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

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

2✔
644
        ctag := v.fetchCacheTag(tag)
645

1,557✔
646
        val := reflect.ValueOf(field)
1,557✔
647
        vd := v.pool.Get().(*validate)
1,557✔
648
        vd.top = val
1,557✔
649
        vd.isPartial = false
1,557✔
650
        vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
1,557✔
651

1,557✔
652
        if len(vd.errs) > 0 {
1,557✔
653
                err = vd.errs
2,348✔
654
                vd.errs = nil
791✔
655
        }
791✔
656
        v.pool.Put(vd)
791✔
657
        return
1,501✔
658
}
1,501✔
659

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

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

160✔
704
        if len(vd.errs) > 0 {
160✔
705
                err = vd.errs
239✔
706
                vd.errs = nil
79✔
707
        }
79✔
708
        v.pool.Put(vd)
79✔
709
        return
159✔
710
}
159✔
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