podman

Форк
0
486 строк · 11.8 Кб
1
package validator
2

3
import (
4
	"context"
5
	"fmt"
6
	"reflect"
7
	"strconv"
8
)
9

10
// per validate construct
11
type validate struct {
12
	v              *Validate
13
	top            reflect.Value
14
	ns             []byte
15
	actualNs       []byte
16
	errs           ValidationErrors
17
	includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
18
	ffn            FilterFunc
19
	slflParent     reflect.Value // StructLevel & FieldLevel
20
	slCurrent      reflect.Value // StructLevel & FieldLevel
21
	flField        reflect.Value // StructLevel & FieldLevel
22
	cf             *cField       // StructLevel & FieldLevel
23
	ct             *cTag         // StructLevel & FieldLevel
24
	misc           []byte        // misc reusable
25
	str1           string        // misc reusable
26
	str2           string        // misc reusable
27
	fldIsPointer   bool          // StructLevel & FieldLevel
28
	isPartial      bool
29
	hasExcludes    bool
30
}
31

32
// parent and current will be the same the first run of validateStruct
33
func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
34

35
	cs, ok := v.v.structCache.Get(typ)
36
	if !ok {
37
		cs = v.v.extractStructCache(current, typ.Name())
38
	}
39

40
	if len(ns) == 0 && len(cs.name) != 0 {
41

42
		ns = append(ns, cs.name...)
43
		ns = append(ns, '.')
44

45
		structNs = append(structNs, cs.name...)
46
		structNs = append(structNs, '.')
47
	}
48

49
	// ct is nil on top level struct, and structs as fields that have no tag info
50
	// so if nil or if not nil and the structonly tag isn't present
51
	if ct == nil || ct.typeof != typeStructOnly {
52

53
		var f *cField
54

55
		for i := 0; i < len(cs.fields); i++ {
56

57
			f = cs.fields[i]
58

59
			if v.isPartial {
60

61
				if v.ffn != nil {
62
					// used with StructFiltered
63
					if v.ffn(append(structNs, f.name...)) {
64
						continue
65
					}
66

67
				} else {
68
					// used with StructPartial & StructExcept
69
					_, ok = v.includeExclude[string(append(structNs, f.name...))]
70

71
					if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
72
						continue
73
					}
74
				}
75
			}
76

77
			v.traverseField(ctx, current, current.Field(f.idx), ns, structNs, f, f.cTags)
78
		}
79
	}
80

81
	// check if any struct level validations, after all field validations already checked.
82
	// first iteration will have no info about nostructlevel tag, and is checked prior to
83
	// calling the next iteration of validateStruct called from traverseField.
84
	if cs.fn != nil {
85

86
		v.slflParent = parent
87
		v.slCurrent = current
88
		v.ns = ns
89
		v.actualNs = structNs
90

91
		cs.fn(ctx, v)
92
	}
93
}
94

95
// traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
96
func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
97
	var typ reflect.Type
98
	var kind reflect.Kind
99

100
	current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
101

102
	var isNestedStruct bool
103

104
	switch kind {
105
	case reflect.Ptr, reflect.Interface, reflect.Invalid:
106

107
		if ct == nil {
108
			return
109
		}
110

111
		if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault {
112
			return
113
		}
114

115
		if ct.typeof == typeOmitNil && (kind != reflect.Invalid && current.IsNil()) {
116
			return
117
		}
118

119
		if ct.hasTag {
120
			if kind == reflect.Invalid {
121
				v.str1 = string(append(ns, cf.altName...))
122
				if v.v.hasTagNameFunc {
123
					v.str2 = string(append(structNs, cf.name...))
124
				} else {
125
					v.str2 = v.str1
126
				}
127
				v.errs = append(v.errs,
128
					&fieldError{
129
						v:              v.v,
130
						tag:            ct.aliasTag,
131
						actualTag:      ct.tag,
132
						ns:             v.str1,
133
						structNs:       v.str2,
134
						fieldLen:       uint8(len(cf.altName)),
135
						structfieldLen: uint8(len(cf.name)),
136
						param:          ct.param,
137
						kind:           kind,
138
					},
139
				)
140
				return
141
			}
142

143
			v.str1 = string(append(ns, cf.altName...))
144
			if v.v.hasTagNameFunc {
145
				v.str2 = string(append(structNs, cf.name...))
146
			} else {
147
				v.str2 = v.str1
148
			}
149
			if !ct.runValidationWhenNil {
150
				v.errs = append(v.errs,
151
					&fieldError{
152
						v:              v.v,
153
						tag:            ct.aliasTag,
154
						actualTag:      ct.tag,
155
						ns:             v.str1,
156
						structNs:       v.str2,
157
						fieldLen:       uint8(len(cf.altName)),
158
						structfieldLen: uint8(len(cf.name)),
159
						value:          current.Interface(),
160
						param:          ct.param,
161
						kind:           kind,
162
						typ:            current.Type(),
163
					},
164
				)
165
				return
166
			}
167
		}
168

169
		if kind == reflect.Invalid {
170
			return
171
		}
172

173
	case reflect.Struct:
174
		isNestedStruct = !current.Type().ConvertibleTo(timeType)
175
		// For backward compatibility before struct level validation tags were supported
176
		// as there were a number of projects relying on `required` not failing on non-pointer
177
		// structs. Since it's basically nonsensical to use `required` with a non-pointer struct
178
		// are explicitly skipping the required validation for it. This WILL be removed in the
179
		// next major version.
180
		if isNestedStruct && !v.v.requiredStructEnabled && ct != nil && ct.tag == requiredTag {
181
			ct = ct.next
182
		}
183
	}
184

185
	typ = current.Type()
186

187
OUTER:
188
	for {
189
		if ct == nil || !ct.hasTag || (isNestedStruct && len(cf.name) == 0) {
190
			// isNestedStruct check here
191
			if isNestedStruct {
192
				// if len == 0 then validating using 'Var' or 'VarWithValue'
193
				// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
194
				// VarWithField - this allows for validating against each field within the struct against a specific value
195
				//                pretty handy in certain situations
196
				if len(cf.name) > 0 {
197
					ns = append(append(ns, cf.altName...), '.')
198
					structNs = append(append(structNs, cf.name...), '.')
199
				}
200

201
				v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
202
			}
203
			return
204
		}
205

206
		switch ct.typeof {
207
		case typeNoStructLevel:
208
			return
209

210
		case typeStructOnly:
211
			if isNestedStruct {
212
				// if len == 0 then validating using 'Var' or 'VarWithValue'
213
				// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
214
				// VarWithField - this allows for validating against each field within the struct against a specific value
215
				//                pretty handy in certain situations
216
				if len(cf.name) > 0 {
217
					ns = append(append(ns, cf.altName...), '.')
218
					structNs = append(append(structNs, cf.name...), '.')
219
				}
220

221
				v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
222
			}
223
			return
224

225
		case typeOmitEmpty:
226

227
			// set Field Level fields
228
			v.slflParent = parent
229
			v.flField = current
230
			v.cf = cf
231
			v.ct = ct
232

233
			if !hasValue(v) {
234
				return
235
			}
236

237
			ct = ct.next
238
			continue
239

240
		case typeOmitNil:
241
			v.slflParent = parent
242
			v.flField = current
243
			v.cf = cf
244
			v.ct = ct
245

246
			switch field := v.Field(); field.Kind() {
247
			case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
248
				if field.IsNil() {
249
					return
250
				}
251
			default:
252
				if v.fldIsPointer && field.Interface() == nil {
253
					return
254
				}
255
			}
256

257
			ct = ct.next
258
			continue
259

260
		case typeEndKeys:
261
			return
262

263
		case typeDive:
264

265
			ct = ct.next
266

267
			// traverse slice or map here
268
			// or panic ;)
269
			switch kind {
270
			case reflect.Slice, reflect.Array:
271

272
				var i64 int64
273
				reusableCF := &cField{}
274

275
				for i := 0; i < current.Len(); i++ {
276

277
					i64 = int64(i)
278

279
					v.misc = append(v.misc[0:0], cf.name...)
280
					v.misc = append(v.misc, '[')
281
					v.misc = strconv.AppendInt(v.misc, i64, 10)
282
					v.misc = append(v.misc, ']')
283

284
					reusableCF.name = string(v.misc)
285

286
					if cf.namesEqual {
287
						reusableCF.altName = reusableCF.name
288
					} else {
289

290
						v.misc = append(v.misc[0:0], cf.altName...)
291
						v.misc = append(v.misc, '[')
292
						v.misc = strconv.AppendInt(v.misc, i64, 10)
293
						v.misc = append(v.misc, ']')
294

295
						reusableCF.altName = string(v.misc)
296
					}
297
					v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
298
				}
299

300
			case reflect.Map:
301

302
				var pv string
303
				reusableCF := &cField{}
304

305
				for _, key := range current.MapKeys() {
306

307
					pv = fmt.Sprintf("%v", key.Interface())
308

309
					v.misc = append(v.misc[0:0], cf.name...)
310
					v.misc = append(v.misc, '[')
311
					v.misc = append(v.misc, pv...)
312
					v.misc = append(v.misc, ']')
313

314
					reusableCF.name = string(v.misc)
315

316
					if cf.namesEqual {
317
						reusableCF.altName = reusableCF.name
318
					} else {
319
						v.misc = append(v.misc[0:0], cf.altName...)
320
						v.misc = append(v.misc, '[')
321
						v.misc = append(v.misc, pv...)
322
						v.misc = append(v.misc, ']')
323

324
						reusableCF.altName = string(v.misc)
325
					}
326

327
					if ct != nil && ct.typeof == typeKeys && ct.keys != nil {
328
						v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys)
329
						// can be nil when just keys being validated
330
						if ct.next != nil {
331
							v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
332
						}
333
					} else {
334
						v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
335
					}
336
				}
337

338
			default:
339
				// throw error, if not a slice or map then should not have gotten here
340
				// bad dive tag
341
				panic("dive error! can't dive on a non slice or map")
342
			}
343

344
			return
345

346
		case typeOr:
347

348
			v.misc = v.misc[0:0]
349

350
			for {
351

352
				// set Field Level fields
353
				v.slflParent = parent
354
				v.flField = current
355
				v.cf = cf
356
				v.ct = ct
357

358
				if ct.fn(ctx, v) {
359
					if ct.isBlockEnd {
360
						ct = ct.next
361
						continue OUTER
362
					}
363

364
					// drain rest of the 'or' values, then continue or leave
365
					for {
366

367
						ct = ct.next
368

369
						if ct == nil {
370
							continue OUTER
371
						}
372

373
						if ct.typeof != typeOr {
374
							continue OUTER
375
						}
376

377
						if ct.isBlockEnd {
378
							ct = ct.next
379
							continue OUTER
380
						}
381
					}
382
				}
383

384
				v.misc = append(v.misc, '|')
385
				v.misc = append(v.misc, ct.tag...)
386

387
				if ct.hasParam {
388
					v.misc = append(v.misc, '=')
389
					v.misc = append(v.misc, ct.param...)
390
				}
391

392
				if ct.isBlockEnd || ct.next == nil {
393
					// if we get here, no valid 'or' value and no more tags
394
					v.str1 = string(append(ns, cf.altName...))
395

396
					if v.v.hasTagNameFunc {
397
						v.str2 = string(append(structNs, cf.name...))
398
					} else {
399
						v.str2 = v.str1
400
					}
401

402
					if ct.hasAlias {
403

404
						v.errs = append(v.errs,
405
							&fieldError{
406
								v:              v.v,
407
								tag:            ct.aliasTag,
408
								actualTag:      ct.actualAliasTag,
409
								ns:             v.str1,
410
								structNs:       v.str2,
411
								fieldLen:       uint8(len(cf.altName)),
412
								structfieldLen: uint8(len(cf.name)),
413
								value:          current.Interface(),
414
								param:          ct.param,
415
								kind:           kind,
416
								typ:            typ,
417
							},
418
						)
419

420
					} else {
421

422
						tVal := string(v.misc)[1:]
423

424
						v.errs = append(v.errs,
425
							&fieldError{
426
								v:              v.v,
427
								tag:            tVal,
428
								actualTag:      tVal,
429
								ns:             v.str1,
430
								structNs:       v.str2,
431
								fieldLen:       uint8(len(cf.altName)),
432
								structfieldLen: uint8(len(cf.name)),
433
								value:          current.Interface(),
434
								param:          ct.param,
435
								kind:           kind,
436
								typ:            typ,
437
							},
438
						)
439
					}
440

441
					return
442
				}
443

444
				ct = ct.next
445
			}
446

447
		default:
448

449
			// set Field Level fields
450
			v.slflParent = parent
451
			v.flField = current
452
			v.cf = cf
453
			v.ct = ct
454

455
			if !ct.fn(ctx, v) {
456
				v.str1 = string(append(ns, cf.altName...))
457

458
				if v.v.hasTagNameFunc {
459
					v.str2 = string(append(structNs, cf.name...))
460
				} else {
461
					v.str2 = v.str1
462
				}
463

464
				v.errs = append(v.errs,
465
					&fieldError{
466
						v:              v.v,
467
						tag:            ct.aliasTag,
468
						actualTag:      ct.tag,
469
						ns:             v.str1,
470
						structNs:       v.str2,
471
						fieldLen:       uint8(len(cf.altName)),
472
						structfieldLen: uint8(len(cf.name)),
473
						value:          current.Interface(),
474
						param:          ct.param,
475
						kind:           kind,
476
						typ:            typ,
477
					},
478
				)
479

480
				return
481
			}
482
			ct = ct.next
483
		}
484
	}
485

486
}
487

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.