...

Source file src/github.com/go-playground/validator/v10/validator.go

Documentation: github.com/go-playground/validator/v10

     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  

View as plain text