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

go-playground / validator / 14503232764

16 Apr 2025 09:46PM UTC coverage: 73.653% (+0.05%) from 73.607%
14503232764

Pull #1394

github

nodivbyzero
add comment how to use it
Pull Request #1394: Add translation example

14310 of 19429 relevant lines covered (73.65%)

73.6 hits per line

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

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

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

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

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

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

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

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

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

82
// Validate contains the validator settings and cache
83
type Validate struct {
84
        tagName                string
85
        pool                   *sync.Pool
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
        hasCustomFuncs         bool
96
        hasTagNameFunc         bool
97
        requiredStructEnabled  bool
98
        privateFieldValidation bool
99
}
100

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

824✔
241
        if fn == nil {
824✔
242
                return errors.New("function cannot be empty")
825✔
243
        }
1✔
244

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

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

260
        _, ok := restrictedTags[alias]
261

262
        if ok || strings.ContainsAny(alias, restrictedTagChars) {
9✔
263
                panic(fmt.Sprintf(restrictedAliasErr, alias))
18✔
264
        }
9✔
265

9✔
266
        v.aliases[alias] = tags
267
}
18✔
268

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

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

×
284
        if v.structLevelFuncs == nil {
×
285
                v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
286
        }
×
287

×
288
        for _, t := range types {
×
289
                tv := reflect.ValueOf(t)
×
290
                if tv.Kind() == reflect.Ptr {
291
                        t = reflect.Indirect(tv).Interface()
×
292
                }
×
293

×
294
                v.structLevelFuncs[reflect.TypeOf(t)] = fn
×
295
        }
×
296
}
×
297

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

307
        deepCopyRules := make(map[string]string)
308
        for i, rule := range rules {
7✔
309
                deepCopyRules[i] = rule
9✔
310
        }
2✔
311

2✔
312
        for _, t := range types {
313
                typ := reflect.TypeOf(t)
18✔
314

12✔
315
                if typ.Kind() == reflect.Ptr {
12✔
316
                        typ = typ.Elem()
317
                }
6✔
318

319
                if typ.Kind() != reflect.Struct {
320
                        continue
321
                }
3✔
322
                v.rules[typ] = deepCopyRules
5✔
323
        }
2✔
324
}
2✔
325

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

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

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

2✔
339
        v.hasCustomFuncs = true
340
}
341

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

345
        if v.transTagFunc == nil {
471✔
346
                v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
471✔
347
        }
471✔
348

349
        if err = registerFn(trans); err != nil {
350
                return
351
        }
352

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

730✔
359
        m[tag] = translationFn
258✔
360

258✔
361
        return
362
}
474✔
363

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

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

379
        val := reflect.ValueOf(s)
460✔
380
        top := val
460✔
381

460✔
382
        if val.Kind() == reflect.Ptr && !val.IsNil() {
383
                val = val.Elem()
384
        }
385

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

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

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

398
        if len(vd.errs) > 0 {
399
                err = vd.errs
15✔
400
                vd.errs = nil
15✔
401
        }
15✔
402

15✔
403
        v.pool.Put(vd)
30✔
404

15✔
405
        return
15✔
406
}
407

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

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

14✔
427
        if val.Kind() == reflect.Ptr && !val.IsNil() {
14✔
428
                val = val.Elem()
429
        }
430

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

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

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

444
        if len(vd.errs) > 0 {
445
                err = vd.errs
446
                vd.errs = nil
447
        }
23✔
448

23✔
449
        v.pool.Put(vd)
23✔
450

23✔
451
        return
40✔
452
}
17✔
453

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

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

49✔
475
        if val.Kind() == reflect.Ptr && !val.IsNil() {
94✔
476
                val = val.Elem()
45✔
477
        }
45✔
478

479
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
115✔
480
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
66✔
481
        }
66✔
482

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

7✔
491
        typ := val.Type()
7✔
492
        name := typ.Name()
7✔
493

7✔
494
        for _, k := range fields {
59✔
495

59✔
496
                flds := strings.Split(k, namespaceSeparator)
59✔
497
                if len(flds) > 0 {
59✔
498

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

21✔
505
                        for _, s := range flds {
21✔
506

28✔
507
                                idx := strings.Index(s, leftBracket)
7✔
508

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

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

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

527
                                vd.misc = append(vd.misc, '.')
528
                        }
529
                }
530
        }
531

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

17✔
534
        if len(vd.errs) > 0 {
17✔
535
                err = vd.errs
17✔
536
                vd.errs = nil
17✔
537
        }
31✔
538

14✔
539
        v.pool.Put(vd)
14✔
540

541
        return
18✔
542
}
1✔
543

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

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

43✔
565
        if val.Kind() == reflect.Ptr && !val.IsNil() {
43✔
566
                val = val.Elem()
567
        }
568

15✔
569
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
15✔
570
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
24✔
571
        }
9✔
572

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

581
        typ := val.Type()
582
        name := typ.Name()
583

584
        for _, key := range fields {
585

586
                vd.misc = vd.misc[0:0]
587

588
                if len(name) > 0 {
589
                        vd.misc = append(vd.misc, name...)
590
                        vd.misc = append(vd.misc, '.')
591
                }
592

593
                vd.misc = append(vd.misc, key...)
1,561✔
594
                vd.includeExclude[string(vd.misc)] = struct{}{}
1,561✔
595
        }
1,561✔
596

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

599
        if len(vd.errs) > 0 {
600
                err = vd.errs
601
                vd.errs = nil
602
        }
603

604
        v.pool.Put(vd)
605

606
        return
607
}
608

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

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

645
        ctag := v.fetchCacheTag(tag)
646

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

653
        if len(vd.errs) > 0 {
654
                err = vd.errs
655
                vd.errs = nil
656
        }
657
        v.pool.Put(vd)
658
        return
659
}
660

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

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

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