validator.go (11326B)
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 switch kind { 103 case reflect.Ptr, reflect.Interface, reflect.Invalid: 104 105 if ct == nil { 106 return 107 } 108 109 if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault { 110 return 111 } 112 113 if ct.hasTag { 114 if kind == reflect.Invalid { 115 v.str1 = string(append(ns, cf.altName...)) 116 if v.v.hasTagNameFunc { 117 v.str2 = string(append(structNs, cf.name...)) 118 } else { 119 v.str2 = v.str1 120 } 121 v.errs = append(v.errs, 122 &fieldError{ 123 v: v.v, 124 tag: ct.aliasTag, 125 actualTag: ct.tag, 126 ns: v.str1, 127 structNs: v.str2, 128 fieldLen: uint8(len(cf.altName)), 129 structfieldLen: uint8(len(cf.name)), 130 param: ct.param, 131 kind: kind, 132 }, 133 ) 134 return 135 } 136 137 v.str1 = string(append(ns, cf.altName...)) 138 if v.v.hasTagNameFunc { 139 v.str2 = string(append(structNs, cf.name...)) 140 } else { 141 v.str2 = v.str1 142 } 143 if !ct.runValidationWhenNil { 144 v.errs = append(v.errs, 145 &fieldError{ 146 v: v.v, 147 tag: ct.aliasTag, 148 actualTag: ct.tag, 149 ns: v.str1, 150 structNs: v.str2, 151 fieldLen: uint8(len(cf.altName)), 152 structfieldLen: uint8(len(cf.name)), 153 value: current.Interface(), 154 param: ct.param, 155 kind: kind, 156 typ: current.Type(), 157 }, 158 ) 159 return 160 } 161 } 162 163 case reflect.Struct: 164 165 typ = current.Type() 166 167 if !typ.ConvertibleTo(timeType) { 168 169 if ct != nil { 170 171 if ct.typeof == typeStructOnly { 172 goto CONTINUE 173 } else if ct.typeof == typeIsDefault { 174 // set Field Level fields 175 v.slflParent = parent 176 v.flField = current 177 v.cf = cf 178 v.ct = ct 179 180 if !ct.fn(ctx, v) { 181 v.str1 = string(append(ns, cf.altName...)) 182 183 if v.v.hasTagNameFunc { 184 v.str2 = string(append(structNs, cf.name...)) 185 } else { 186 v.str2 = v.str1 187 } 188 189 v.errs = append(v.errs, 190 &fieldError{ 191 v: v.v, 192 tag: ct.aliasTag, 193 actualTag: ct.tag, 194 ns: v.str1, 195 structNs: v.str2, 196 fieldLen: uint8(len(cf.altName)), 197 structfieldLen: uint8(len(cf.name)), 198 value: current.Interface(), 199 param: ct.param, 200 kind: kind, 201 typ: typ, 202 }, 203 ) 204 return 205 } 206 } 207 208 ct = ct.next 209 } 210 211 if ct != nil && ct.typeof == typeNoStructLevel { 212 return 213 } 214 215 CONTINUE: 216 // if len == 0 then validating using 'Var' or 'VarWithValue' 217 // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm... 218 // VarWithField - this allows for validating against each field within the struct against a specific value 219 // pretty handy in certain situations 220 if len(cf.name) > 0 { 221 ns = append(append(ns, cf.altName...), '.') 222 structNs = append(append(structNs, cf.name...), '.') 223 } 224 225 v.validateStruct(ctx, parent, current, typ, ns, structNs, ct) 226 return 227 } 228 } 229 230 if ct == nil || !ct.hasTag { 231 return 232 } 233 234 typ = current.Type() 235 236 OUTER: 237 for { 238 if ct == nil { 239 return 240 } 241 242 switch ct.typeof { 243 244 case typeOmitEmpty: 245 246 // set Field Level fields 247 v.slflParent = parent 248 v.flField = current 249 v.cf = cf 250 v.ct = ct 251 252 if !hasValue(v) { 253 return 254 } 255 256 ct = ct.next 257 continue 258 259 case typeEndKeys: 260 return 261 262 case typeDive: 263 264 ct = ct.next 265 266 // traverse slice or map here 267 // or panic ;) 268 switch kind { 269 case reflect.Slice, reflect.Array: 270 271 var i64 int64 272 reusableCF := &cField{} 273 274 for i := 0; i < current.Len(); i++ { 275 276 i64 = int64(i) 277 278 v.misc = append(v.misc[0:0], cf.name...) 279 v.misc = append(v.misc, '[') 280 v.misc = strconv.AppendInt(v.misc, i64, 10) 281 v.misc = append(v.misc, ']') 282 283 reusableCF.name = string(v.misc) 284 285 if cf.namesEqual { 286 reusableCF.altName = reusableCF.name 287 } else { 288 289 v.misc = append(v.misc[0:0], cf.altName...) 290 v.misc = append(v.misc, '[') 291 v.misc = strconv.AppendInt(v.misc, i64, 10) 292 v.misc = append(v.misc, ']') 293 294 reusableCF.altName = string(v.misc) 295 } 296 v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct) 297 } 298 299 case reflect.Map: 300 301 var pv string 302 reusableCF := &cField{} 303 304 for _, key := range current.MapKeys() { 305 306 pv = fmt.Sprintf("%v", key.Interface()) 307 308 v.misc = append(v.misc[0:0], cf.name...) 309 v.misc = append(v.misc, '[') 310 v.misc = append(v.misc, pv...) 311 v.misc = append(v.misc, ']') 312 313 reusableCF.name = string(v.misc) 314 315 if cf.namesEqual { 316 reusableCF.altName = reusableCF.name 317 } else { 318 v.misc = append(v.misc[0:0], cf.altName...) 319 v.misc = append(v.misc, '[') 320 v.misc = append(v.misc, pv...) 321 v.misc = append(v.misc, ']') 322 323 reusableCF.altName = string(v.misc) 324 } 325 326 if ct != nil && ct.typeof == typeKeys && ct.keys != nil { 327 v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys) 328 // can be nil when just keys being validated 329 if ct.next != nil { 330 v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next) 331 } 332 } else { 333 v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct) 334 } 335 } 336 337 default: 338 // throw error, if not a slice or map then should not have gotten here 339 // bad dive tag 340 panic("dive error! can't dive on a non slice or map") 341 } 342 343 return 344 345 case typeOr: 346 347 v.misc = v.misc[0:0] 348 349 for { 350 351 // set Field Level fields 352 v.slflParent = parent 353 v.flField = current 354 v.cf = cf 355 v.ct = ct 356 357 if ct.fn(ctx, v) { 358 if ct.isBlockEnd { 359 ct = ct.next 360 continue OUTER 361 } 362 363 // drain rest of the 'or' values, then continue or leave 364 for { 365 366 ct = ct.next 367 368 if ct == nil { 369 return 370 } 371 372 if ct.typeof != typeOr { 373 continue OUTER 374 } 375 376 if ct.isBlockEnd { 377 ct = ct.next 378 continue OUTER 379 } 380 } 381 } 382 383 v.misc = append(v.misc, '|') 384 v.misc = append(v.misc, ct.tag...) 385 386 if ct.hasParam { 387 v.misc = append(v.misc, '=') 388 v.misc = append(v.misc, ct.param...) 389 } 390 391 if ct.isBlockEnd || ct.next == nil { 392 // if we get here, no valid 'or' value and no more tags 393 v.str1 = string(append(ns, cf.altName...)) 394 395 if v.v.hasTagNameFunc { 396 v.str2 = string(append(structNs, cf.name...)) 397 } else { 398 v.str2 = v.str1 399 } 400 401 if ct.hasAlias { 402 403 v.errs = append(v.errs, 404 &fieldError{ 405 v: v.v, 406 tag: ct.aliasTag, 407 actualTag: ct.actualAliasTag, 408 ns: v.str1, 409 structNs: v.str2, 410 fieldLen: uint8(len(cf.altName)), 411 structfieldLen: uint8(len(cf.name)), 412 value: current.Interface(), 413 param: ct.param, 414 kind: kind, 415 typ: typ, 416 }, 417 ) 418 419 } else { 420 421 tVal := string(v.misc)[1:] 422 423 v.errs = append(v.errs, 424 &fieldError{ 425 v: v.v, 426 tag: tVal, 427 actualTag: tVal, 428 ns: v.str1, 429 structNs: v.str2, 430 fieldLen: uint8(len(cf.altName)), 431 structfieldLen: uint8(len(cf.name)), 432 value: current.Interface(), 433 param: ct.param, 434 kind: kind, 435 typ: typ, 436 }, 437 ) 438 } 439 440 return 441 } 442 443 ct = ct.next 444 } 445 446 default: 447 448 // set Field Level fields 449 v.slflParent = parent 450 v.flField = current 451 v.cf = cf 452 v.ct = ct 453 454 if !ct.fn(ctx, v) { 455 v.str1 = string(append(ns, cf.altName...)) 456 457 if v.v.hasTagNameFunc { 458 v.str2 = string(append(structNs, cf.name...)) 459 } else { 460 v.str2 = v.str1 461 } 462 463 v.errs = append(v.errs, 464 &fieldError{ 465 v: v.v, 466 tag: ct.aliasTag, 467 actualTag: ct.tag, 468 ns: v.str1, 469 structNs: v.str2, 470 fieldLen: uint8(len(cf.altName)), 471 structfieldLen: uint8(len(cf.name)), 472 value: current.Interface(), 473 param: ct.param, 474 kind: kind, 475 typ: typ, 476 }, 477 ) 478 479 return 480 } 481 ct = ct.next 482 } 483 } 484 485 }