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

go-playground / validator / 14493308745

16 Apr 2025 12:58PM UTC coverage: 73.613% (+0.006%) from 73.607%
14493308745

Pull #1416

github

thommeo
ci: fix linting issue
Pull Request #1416: feat: implement ValidatorValuer interface feature

22 of 24 new or added lines in 2 files covered. (91.67%)

14 existing lines in 1 file now uncovered.

14281 of 19400 relevant lines covered (73.61%)

77.57 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 {
294✔
107
        tc := new(tagCache)
294✔
108
        tc.m.Store(make(map[string]*cTag))
294✔
109

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

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

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

126
        // must copy validators for separate validations to be used in each instance
127
        for k, val := range bakedInValidators {
49,980✔
128
                switch k {
49,686✔
129
                // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
130
                case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
131
                        excludedIfTag, excludedUnlessTag, excludedWithTag, excludedWithAllTag, excludedWithoutTag, excludedWithoutAllTag,
132
                        skipUnlessTag:
3,822✔
133
                        _ = v.registerValidation(k, wrapFunc(val), true, true)
3,822✔
134
                default:
45,864✔
135
                        // no need to error check here, baked in will always be valid
45,864✔
136
                        _ = v.registerValidation(k, wrapFunc(val), true, false)
45,864✔
137
                }
138
        }
139

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

151
        for _, o := range options {
298✔
152
                o(v)
4✔
153
        }
4✔
154
        return v
294✔
155
}
156

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

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

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

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

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

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

234
// RegisterAlias registers a mapping of a single validation tag that
235
// defines a common or complex set of validation(s) to simplify adding validation
236
// to structs.
237
//
238
// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
239
func (v *Validate) RegisterAlias(alias, tags string) {
887✔
240
        _, ok := restrictedTags[alias]
887✔
241

887✔
242
        if ok || strings.ContainsAny(alias, restrictedTagChars) {
888✔
243
                panic(fmt.Sprintf(restrictedAliasErr, alias))
1✔
244
        }
245

246
        v.aliases[alias] = tags
886✔
247
}
248

249
// RegisterStructValidation registers a StructLevelFunc against a number of types.
250
//
251
// NOTE:
252
// - this method is not thread-safe it is intended that these all be registered prior to any validation
253
func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) {
8✔
254
        v.RegisterStructValidationCtx(wrapStructLevelFunc(fn), types...)
8✔
255
}
8✔
256

257
// RegisterStructValidationCtx registers a StructLevelFuncCtx against a number of types and allows passing
258
// of contextual validation information via context.Context.
259
//
260
// NOTE:
261
// - this method is not thread-safe it is intended that these all be registered prior to any validation
262
func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
9✔
263
        if v.structLevelFuncs == nil {
18✔
264
                v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
9✔
265
        }
9✔
266

267
        for _, t := range types {
18✔
268
                tv := reflect.ValueOf(t)
9✔
269
                if tv.Kind() == reflect.Ptr {
10✔
270
                        t = reflect.Indirect(tv).Interface()
1✔
271
                }
1✔
272

273
                v.structLevelFuncs[reflect.TypeOf(t)] = fn
9✔
274
        }
275
}
276

277
// RegisterStructValidationMapRules registers validate map rules.
278
// Be aware that map validation rules supersede those defined on a/the struct if present.
279
//
280
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
UNCOV
281
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
×
UNCOV
282
        if v.rules == nil {
×
UNCOV
283
                v.rules = make(map[reflect.Type]map[string]string)
×
UNCOV
284
        }
×
285

UNCOV
286
        deepCopyRules := make(map[string]string)
×
UNCOV
287
        for i, rule := range rules {
×
UNCOV
288
                deepCopyRules[i] = rule
×
UNCOV
289
        }
×
290

UNCOV
291
        for _, t := range types {
×
UNCOV
292
                typ := reflect.TypeOf(t)
×
UNCOV
293

×
UNCOV
294
                if typ.Kind() == reflect.Ptr {
×
UNCOV
295
                        typ = typ.Elem()
×
UNCOV
296
                }
×
297

298
                if typ.Kind() != reflect.Struct {
×
299
                        continue
×
300
                }
301
                v.rules[typ] = deepCopyRules
×
302
        }
303
}
304

305
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
306
//
307
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
308
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
7✔
309
        if v.customFuncs == nil {
9✔
310
                v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
2✔
311
        }
2✔
312

313
        for _, t := range types {
18✔
314
                v.customFuncs[reflect.TypeOf(t)] = fn
12✔
315
        }
12✔
316

317
        v.hasCustomFuncs = true
6✔
318
}
319

320
// RegisterTranslation registers translations against the provided tag.
321
func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
3✔
322
        if v.transTagFunc == nil {
5✔
323
                v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
2✔
324
        }
2✔
325

326
        if err = registerFn(trans); err != nil {
4✔
327
                return
1✔
328
        }
1✔
329

330
        m, ok := v.transTagFunc[trans]
2✔
331
        if !ok {
4✔
332
                m = make(map[string]TranslationFunc)
2✔
333
                v.transTagFunc[trans] = m
2✔
334
        }
2✔
335

336
        m[tag] = translationFn
2✔
337

2✔
338
        return
2✔
339
}
340

341
// Struct validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified.
342
//
343
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
344
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
345
func (v *Validate) Struct(s interface{}) error {
486✔
346
        return v.StructCtx(context.Background(), s)
486✔
347
}
486✔
348

349
// StructCtx validates a structs exposed fields, and automatically validates nested structs, unless otherwise specified
350
// and also allows passing of context.Context for contextual validation information.
351
//
352
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
353
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
354
func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
487✔
355
        val := reflect.ValueOf(s)
487✔
356
        top := val
487✔
357

487✔
358
        if val.Kind() == reflect.Ptr && !val.IsNil() {
751✔
359
                val = val.Elem()
264✔
360
        }
264✔
361

362
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
489✔
363
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
2✔
364
        }
2✔
365

366
        // good to validate
367
        vd := v.pool.Get().(*validate)
485✔
368
        vd.top = top
485✔
369
        vd.isPartial = false
485✔
370
        // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
485✔
371

485✔
372
        vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
485✔
373

485✔
374
        if len(vd.errs) > 0 {
726✔
375
                err = vd.errs
241✔
376
                vd.errs = nil
241✔
377
        }
241✔
378

379
        v.pool.Put(vd)
475✔
380

475✔
381
        return
475✔
382
}
383

384
// StructFiltered validates a structs exposed fields, that pass the FilterFunc check and automatically validates
385
// nested structs, unless otherwise specified.
386
//
387
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
388
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
389
func (v *Validate) StructFiltered(s interface{}, fn FilterFunc) error {
14✔
390
        return v.StructFilteredCtx(context.Background(), s, fn)
14✔
391
}
14✔
392

393
// StructFilteredCtx validates a structs exposed fields, that pass the FilterFunc check and automatically validates
394
// nested structs, unless otherwise specified and also allows passing of contextual validation information via
395
// context.Context
396
//
397
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
398
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
399
func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn FilterFunc) (err error) {
15✔
400
        val := reflect.ValueOf(s)
15✔
401
        top := val
15✔
402

15✔
403
        if val.Kind() == reflect.Ptr && !val.IsNil() {
30✔
404
                val = val.Elem()
15✔
405
        }
15✔
406

407
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
16✔
408
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
1✔
409
        }
1✔
410

411
        // good to validate
412
        vd := v.pool.Get().(*validate)
14✔
413
        vd.top = top
14✔
414
        vd.isPartial = true
14✔
415
        vd.ffn = fn
14✔
416
        // vd.hasExcludes = false // only need to reset in StructPartial and StructExcept
14✔
417

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

14✔
420
        if len(vd.errs) > 0 {
18✔
421
                err = vd.errs
4✔
422
                vd.errs = nil
4✔
423
        }
4✔
424

425
        v.pool.Put(vd)
14✔
426

14✔
427
        return
14✔
428
}
429

430
// StructPartial validates the fields passed in only, ignoring all others.
431
// Fields may be provided in a namespaced fashion relative to the  struct provided
432
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
433
//
434
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
435
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
436
func (v *Validate) StructPartial(s interface{}, fields ...string) error {
22✔
437
        return v.StructPartialCtx(context.Background(), s, fields...)
22✔
438
}
22✔
439

440
// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
441
// validation information via context.Context
442
// Fields may be provided in a namespaced fashion relative to the  struct provided
443
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
444
//
445
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
446
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
447
func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
23✔
448
        val := reflect.ValueOf(s)
23✔
449
        top := val
23✔
450

23✔
451
        if val.Kind() == reflect.Ptr && !val.IsNil() {
40✔
452
                val = val.Elem()
17✔
453
        }
17✔
454

455
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
24✔
456
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
1✔
457
        }
1✔
458

459
        // good to validate
460
        vd := v.pool.Get().(*validate)
22✔
461
        vd.top = top
22✔
462
        vd.isPartial = true
22✔
463
        vd.ffn = nil
22✔
464
        vd.hasExcludes = false
22✔
465
        vd.includeExclude = make(map[string]struct{})
22✔
466

22✔
467
        typ := val.Type()
22✔
468
        name := typ.Name()
22✔
469

22✔
470
        for _, k := range fields {
71✔
471
                flds := strings.Split(k, namespaceSeparator)
49✔
472
                if len(flds) > 0 {
98✔
473
                        vd.misc = append(vd.misc[0:0], name...)
49✔
474
                        // Don't append empty name for unnamed structs
49✔
475
                        if len(vd.misc) != 0 {
94✔
476
                                vd.misc = append(vd.misc, '.')
45✔
477
                        }
45✔
478

479
                        for _, s := range flds {
115✔
480
                                idx := strings.Index(s, leftBracket)
66✔
481

66✔
482
                                if idx != -1 {
73✔
483
                                        for idx != -1 {
14✔
484
                                                vd.misc = append(vd.misc, s[:idx]...)
7✔
485
                                                vd.includeExclude[string(vd.misc)] = struct{}{}
7✔
486

7✔
487
                                                idx2 := strings.Index(s, rightBracket)
7✔
488
                                                idx2++
7✔
489
                                                vd.misc = append(vd.misc, s[idx:idx2]...)
7✔
490
                                                vd.includeExclude[string(vd.misc)] = struct{}{}
7✔
491
                                                s = s[idx2:]
7✔
492
                                                idx = strings.Index(s, leftBracket)
7✔
493
                                        }
7✔
494
                                } else {
59✔
495
                                        vd.misc = append(vd.misc, s...)
59✔
496
                                        vd.includeExclude[string(vd.misc)] = struct{}{}
59✔
497
                                }
59✔
498

499
                                vd.misc = append(vd.misc, '.')
66✔
500
                        }
501
                }
502
        }
503

504
        vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
21✔
505

21✔
506
        if len(vd.errs) > 0 {
28✔
507
                err = vd.errs
7✔
508
                vd.errs = nil
7✔
509
        }
7✔
510

511
        v.pool.Put(vd)
21✔
512

21✔
513
        return
21✔
514
}
515

516
// StructExcept validates all fields except the ones passed in.
517
// Fields may be provided in a namespaced fashion relative to the  struct provided
518
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
519
//
520
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
521
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
522
func (v *Validate) StructExcept(s interface{}, fields ...string) error {
16✔
523
        return v.StructExceptCtx(context.Background(), s, fields...)
16✔
524
}
16✔
525

526
// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
527
// validation information via context.Context
528
// Fields may be provided in a namespaced fashion relative to the  struct provided
529
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
530
//
531
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
532
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
533
func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ...string) (err error) {
17✔
534
        val := reflect.ValueOf(s)
17✔
535
        top := val
17✔
536

17✔
537
        if val.Kind() == reflect.Ptr && !val.IsNil() {
31✔
538
                val = val.Elem()
14✔
539
        }
14✔
540

541
        if val.Kind() != reflect.Struct || val.Type().ConvertibleTo(timeType) {
18✔
542
                return &InvalidValidationError{Type: reflect.TypeOf(s)}
1✔
543
        }
1✔
544

545
        // good to validate
546
        vd := v.pool.Get().(*validate)
16✔
547
        vd.top = top
16✔
548
        vd.isPartial = true
16✔
549
        vd.ffn = nil
16✔
550
        vd.hasExcludes = true
16✔
551
        vd.includeExclude = make(map[string]struct{})
16✔
552

16✔
553
        typ := val.Type()
16✔
554
        name := typ.Name()
16✔
555

16✔
556
        for _, key := range fields {
59✔
557
                vd.misc = vd.misc[0:0]
43✔
558

43✔
559
                if len(name) > 0 {
85✔
560
                        vd.misc = append(vd.misc, name...)
42✔
561
                        vd.misc = append(vd.misc, '.')
42✔
562
                }
42✔
563

564
                vd.misc = append(vd.misc, key...)
43✔
565
                vd.includeExclude[string(vd.misc)] = struct{}{}
43✔
566
        }
567

568
        vd.validateStruct(ctx, top, val, typ, vd.ns[0:0], vd.actualNs[0:0], nil)
15✔
569

15✔
570
        if len(vd.errs) > 0 {
24✔
571
                err = vd.errs
9✔
572
                vd.errs = nil
9✔
573
        }
9✔
574

575
        v.pool.Put(vd)
15✔
576

15✔
577
        return
15✔
578
}
579

580
// Var validates a single variable using tag style validation.
581
// eg.
582
// var i int
583
// validate.Var(i, "gt=1,lt=10")
584
//
585
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
586
// if you have a custom type and have registered a custom type handler, so must
587
// allow it; however unforeseen validations will occur if trying to validate a
588
// struct that is meant to be passed to 'validate.Struct'
589
//
590
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
591
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
592
// validate Array, Slice and maps fields which may contain more than one error
593
func (v *Validate) Var(field interface{}, tag string) error {
1,561✔
594
        return v.VarCtx(context.Background(), field, tag)
1,561✔
595
}
1,561✔
596

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

616
        ctag := v.fetchCacheTag(tag)
1,566✔
617

1,566✔
618
        val := reflect.ValueOf(field)
1,566✔
619
        vd := v.pool.Get().(*validate)
1,566✔
620
        vd.top = val
1,566✔
621
        vd.isPartial = false
1,566✔
622
        vd.traverseField(ctx, val, val, vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
1,566✔
623

1,566✔
624
        if len(vd.errs) > 0 {
2,364✔
625
                err = vd.errs
798✔
626
                vd.errs = nil
798✔
627
        }
798✔
628
        v.pool.Put(vd)
1,510✔
629
        return
1,510✔
630
}
631

632
// VarWithValue validates a single variable, against another variable/field's value using tag style validation
633
// eg.
634
// s1 := "abcd"
635
// s2 := "abcd"
636
// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
637
//
638
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
639
// if you have a custom type and have registered a custom type handler, so must
640
// allow it; however unforeseen validations will occur if trying to validate a
641
// struct that is meant to be passed to 'validate.Struct'
642
//
643
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
644
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
645
// validate Array, Slice and maps fields which may contain more than one error
646
func (v *Validate) VarWithValue(field interface{}, other interface{}, tag string) error {
160✔
647
        return v.VarWithValueCtx(context.Background(), field, other, tag)
160✔
648
}
160✔
649

650
// VarWithValueCtx validates a single variable, against another variable/field's value using tag style validation and
651
// allows passing of contextual validation information via context.Context.
652
// eg.
653
// s1 := "abcd"
654
// s2 := "abcd"
655
// validate.VarWithValue(s1, s2, "eqcsfield") // returns true
656
//
657
// WARNING: a struct can be passed for validation eg. time.Time is a struct or
658
// if you have a custom type and have registered a custom type handler, so must
659
// allow it; however unforeseen validations will occur if trying to validate a
660
// struct that is meant to be passed to 'validate.Struct'
661
//
662
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
663
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
664
// validate Array, Slice and maps fields which may contain more than one error
665
func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other interface{}, tag string) (err error) {
161✔
666
        if len(tag) == 0 || tag == skipValidationTag {
162✔
667
                return nil
1✔
668
        }
1✔
669
        ctag := v.fetchCacheTag(tag)
160✔
670
        otherVal := reflect.ValueOf(other)
160✔
671
        vd := v.pool.Get().(*validate)
160✔
672
        vd.top = otherVal
160✔
673
        vd.isPartial = false
160✔
674
        vd.traverseField(ctx, otherVal, reflect.ValueOf(field), vd.ns[0:0], vd.actualNs[0:0], defaultCField, ctag)
160✔
675

160✔
676
        if len(vd.errs) > 0 {
239✔
677
                err = vd.errs
79✔
678
                vd.errs = nil
79✔
679
        }
79✔
680
        v.pool.Put(vd)
159✔
681
        return
159✔
682
}
683

684
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
49,704✔
685
        if len(tag) == 0 {
49,705✔
686
                return errors.New("function Key cannot be empty")
1✔
687
        }
1✔
688

689
        if fn == nil {
49,704✔
690
                return errors.New("function cannot be empty")
1✔
691
        }
1✔
692

693
        _, ok := restrictedTags[tag]
49,702✔
694
        if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
49,703✔
695
                panic(fmt.Sprintf(restrictedTagErr, tag))
1✔
696
        }
697
        v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidationOnNil: nilCheckable}
49,701✔
698
        return nil
49,701✔
699
}
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