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

go-playground / validator / 9357912492

03 Jun 2024 10:08PM UTC coverage: 74.275% (+0.1%) from 74.172%
9357912492

Pull #1224

github

leodido
tests: fixup urn RFC8141 validator name

Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
Pull Request #1224: feat: URN RFC 8141

10 of 11 new or added lines in 1 file covered. (90.91%)

77 existing lines in 3 files now uncovered.

12225 of 16459 relevant lines covered (74.28%)

85.89 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
        runValidatinOnNil 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
}
98

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

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

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

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

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

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

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

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

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

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

1✔
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{} {
165
        errs := make(map[string]interface{})
8✔
166
        for field, rule := range rules {
8✔
167
                if ruleObj, ok := rule.(map[string]interface{}); ok {
22✔
168
                        if dataObj, ok := data[field].(map[string]interface{}); ok {
22✔
169
                                err := v.ValidateMapCtx(ctx, dataObj, ruleObj)
12✔
170
                                if len(err) > 0 {
4✔
171
                                        errs[field] = err
6✔
172
                                }
2✔
173
                        } else if dataObjs, ok := data[field].([]map[string]interface{}); ok {
2✔
174
                                for _, obj := range dataObjs {
6✔
175
                                        err := v.ValidateMapCtx(ctx, obj, ruleObj)
4✔
176
                                        if len(err) > 0 {
2✔
177
                                                errs[field] = err
3✔
178
                                        }
1✔
179
                                }
1✔
180
                        } else {
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 {
2✔
184
                        err := v.VarCtx(ctx, data[field], ruleStr)
12✔
185
                        if err != nil {
6✔
186
                                errs[field] = err
8✔
187
                        }
2✔
188
                }
2✔
189
        }
190
        return errs
191
}
8✔
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
}
×
UNCOV
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) {
211
        v.tagNameFunc = fn
14✔
212
        v.hasTagNameFunc = true
14✔
213
}
14✔
214

14✔
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 {
221
        return v.RegisterValidationCtx(tag, wrapFunc(fn), callValidationEvenIfNull...)
16✔
222
}
16✔
223

16✔
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 {
227
        var nilCheckable bool
18✔
228
        if len(callValidationEvenIfNull) > 0 {
18✔
229
                nilCheckable = callValidationEvenIfNull[0]
19✔
230
        }
1✔
231
        return v.registerValidation(tag, fn, false, nilCheckable)
1✔
232
}
18✔
233

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

1✔
239
        if fn == nil {
240
                return errors.New("function cannot be empty")
43,772✔
241
        }
1✔
242

1✔
243
        _, ok := restrictedTags[tag]
244
        if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
43,770✔
245
                panic(fmt.Sprintf(restrictedTagErr, tag))
43,771✔
246
        }
1✔
247
        v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidatinOnNil: nilCheckable}
248
        return nil
43,769✔
249
}
43,769✔
250

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

791✔
258
        _, ok := restrictedTags[alias]
791✔
259

791✔
260
        if ok || strings.ContainsAny(alias, restrictedTagChars) {
791✔
261
                panic(fmt.Sprintf(restrictedAliasErr, alias))
792✔
262
        }
1✔
263

264
        v.aliases[alias] = tags
265
}
790✔
266

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2✔
357
        m[tag] = translationFn
358

2✔
359
        return
2✔
360
}
2✔
361

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

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

447✔
377
        val := reflect.ValueOf(s)
447✔
378
        top := val
447✔
379

447✔
380
        if val.Kind() == reflect.Ptr && !val.IsNil() {
447✔
381
                val = val.Elem()
698✔
382
        }
251✔
383

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

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

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

445✔
396
        if len(vd.errs) > 0 {
445✔
397
                err = vd.errs
665✔
398
                vd.errs = nil
220✔
399
        }
220✔
400

220✔
401
        v.pool.Put(vd)
402

435✔
403
        return
435✔
404
}
435✔
405

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

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

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

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

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

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

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

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

14✔
449
        return
14✔
450
}
14✔
451

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

21✔
539
        return
21✔
540
}
21✔
541

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

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

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

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

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

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

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

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

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

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

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

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

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

15✔
604
        return
15✔
605
}
15✔
606

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

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

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

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

1,564✔
651
        if len(vd.errs) > 0 {
1,564✔
652
                err = vd.errs
2,361✔
653
                vd.errs = nil
797✔
654
        }
797✔
655
        v.pool.Put(vd)
797✔
656
        return
1,519✔
657
}
1,519✔
658

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

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

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

© 2026 Coveralls, Inc