...

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  	"unsafe"
     9  )
    10  
    11  // per validate construct
    12  type validate struct {
    13  	v              *Validate
    14  	top            reflect.Value
    15  	ns             []byte
    16  	actualNs       []byte
    17  	errs           ValidationErrors
    18  	includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise
    19  	ffn            FilterFunc
    20  	slflParent     reflect.Value // StructLevel & FieldLevel
    21  	slCurrent      reflect.Value // StructLevel & FieldLevel
    22  	flField        reflect.Value // StructLevel & FieldLevel
    23  	cf             *cField       // StructLevel & FieldLevel
    24  	ct             *cTag         // StructLevel & FieldLevel
    25  	misc           []byte        // misc reusable
    26  	str1           string        // misc reusable
    27  	str2           string        // misc reusable
    28  	fldIsPointer   bool          // StructLevel & FieldLevel
    29  	isPartial      bool
    30  	hasExcludes    bool
    31  }
    32  
    33  // parent and current will be the same the first run of validateStruct
    34  func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
    35  
    36  	cs, ok := v.v.structCache.Get(typ)
    37  	if !ok {
    38  		cs = v.v.extractStructCache(current, typ.Name())
    39  	}
    40  
    41  	if len(ns) == 0 && len(cs.name) != 0 {
    42  
    43  		ns = append(ns, cs.name...)
    44  		ns = append(ns, '.')
    45  
    46  		structNs = append(structNs, cs.name...)
    47  		structNs = append(structNs, '.')
    48  	}
    49  
    50  	// ct is nil on top level struct, and structs as fields that have no tag info
    51  	// so if nil or if not nil and the structonly tag isn't present
    52  	if ct == nil || ct.typeof != typeStructOnly {
    53  
    54  		var f *cField
    55  
    56  		for i := 0; i < len(cs.fields); i++ {
    57  
    58  			f = cs.fields[i]
    59  
    60  			if v.isPartial {
    61  
    62  				if v.ffn != nil {
    63  					// used with StructFiltered
    64  					if v.ffn(append(structNs, f.name...)) {
    65  						continue
    66  					}
    67  
    68  				} else {
    69  					// used with StructPartial & StructExcept
    70  					_, ok = v.includeExclude[string(append(structNs, f.name...))]
    71  
    72  					if (ok && v.hasExcludes) || (!ok && !v.hasExcludes) {
    73  						continue
    74  					}
    75  				}
    76  			}
    77  
    78  			v.traverseField(ctx, current, current.Field(f.idx), ns, structNs, f, f.cTags)
    79  		}
    80  	}
    81  
    82  	// check if any struct level validations, after all field validations already checked.
    83  	// first iteration will have no info about nostructlevel tag, and is checked prior to
    84  	// calling the next iteration of validateStruct called from traverseField.
    85  	if cs.fn != nil {
    86  
    87  		v.slflParent = parent
    88  		v.slCurrent = current
    89  		v.ns = ns
    90  		v.actualNs = structNs
    91  
    92  		cs.fn(ctx, v)
    93  	}
    94  }
    95  
    96  // 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
    97  func (v *validate) traverseField(ctx context.Context, parent reflect.Value, current reflect.Value, ns []byte, structNs []byte, cf *cField, ct *cTag) {
    98  	var typ reflect.Type
    99  	var kind reflect.Kind
   100  
   101  	current, kind, v.fldIsPointer = v.extractTypeInternal(current, false)
   102  
   103  	var isNestedStruct bool
   104  
   105  	switch kind {
   106  	case reflect.Ptr, reflect.Interface, reflect.Invalid:
   107  
   108  		if ct == nil {
   109  			return
   110  		}
   111  
   112  		if ct.typeof == typeOmitEmpty || ct.typeof == typeIsDefault {
   113  			return
   114  		}
   115  
   116  		if ct.typeof == typeOmitNil && (kind != reflect.Invalid && current.IsNil()) {
   117  			return
   118  		}
   119  
   120  		if ct.hasTag {
   121  			if kind == reflect.Invalid {
   122  				v.str1 = string(append(ns, cf.altName...))
   123  				if v.v.hasTagNameFunc {
   124  					v.str2 = string(append(structNs, cf.name...))
   125  				} else {
   126  					v.str2 = v.str1
   127  				}
   128  				v.errs = append(v.errs,
   129  					&fieldError{
   130  						v:              v.v,
   131  						tag:            ct.aliasTag,
   132  						actualTag:      ct.tag,
   133  						ns:             v.str1,
   134  						structNs:       v.str2,
   135  						fieldLen:       uint8(len(cf.altName)),
   136  						structfieldLen: uint8(len(cf.name)),
   137  						param:          ct.param,
   138  						kind:           kind,
   139  					},
   140  				)
   141  				return
   142  			}
   143  
   144  			v.str1 = string(append(ns, cf.altName...))
   145  			if v.v.hasTagNameFunc {
   146  				v.str2 = string(append(structNs, cf.name...))
   147  			} else {
   148  				v.str2 = v.str1
   149  			}
   150  			if !ct.runValidationWhenNil {
   151  				v.errs = append(v.errs,
   152  					&fieldError{
   153  						v:              v.v,
   154  						tag:            ct.aliasTag,
   155  						actualTag:      ct.tag,
   156  						ns:             v.str1,
   157  						structNs:       v.str2,
   158  						fieldLen:       uint8(len(cf.altName)),
   159  						structfieldLen: uint8(len(cf.name)),
   160  						value:          getValue(current),
   161  						param:          ct.param,
   162  						kind:           kind,
   163  						typ:            current.Type(),
   164  					},
   165  				)
   166  				return
   167  			}
   168  		}
   169  
   170  		if kind == reflect.Invalid {
   171  			return
   172  		}
   173  
   174  	case reflect.Struct:
   175  		isNestedStruct = !current.Type().ConvertibleTo(timeType)
   176  		// For backward compatibility before struct level validation tags were supported
   177  		// as there were a number of projects relying on `required` not failing on non-pointer
   178  		// structs. Since it's basically nonsensical to use `required` with a non-pointer struct
   179  		// are explicitly skipping the required validation for it. This WILL be removed in the
   180  		// next major version.
   181  		if isNestedStruct && !v.v.requiredStructEnabled && ct != nil && ct.tag == requiredTag {
   182  			ct = ct.next
   183  		}
   184  	}
   185  
   186  	typ = current.Type()
   187  
   188  OUTER:
   189  	for {
   190  		if ct == nil || !ct.hasTag || (isNestedStruct && len(cf.name) == 0) {
   191  			// isNestedStruct check here
   192  			if isNestedStruct {
   193  				// if len == 0 then validating using 'Var' or 'VarWithValue'
   194  				// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
   195  				// VarWithField - this allows for validating against each field within the struct against a specific value
   196  				//                pretty handy in certain situations
   197  				if len(cf.name) > 0 {
   198  					ns = append(append(ns, cf.altName...), '.')
   199  					structNs = append(append(structNs, cf.name...), '.')
   200  				}
   201  
   202  				v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
   203  			}
   204  			return
   205  		}
   206  
   207  		switch ct.typeof {
   208  		case typeNoStructLevel:
   209  			return
   210  
   211  		case typeStructOnly:
   212  			if isNestedStruct {
   213  				// if len == 0 then validating using 'Var' or 'VarWithValue'
   214  				// Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
   215  				// VarWithField - this allows for validating against each field within the struct against a specific value
   216  				//                pretty handy in certain situations
   217  				if len(cf.name) > 0 {
   218  					ns = append(append(ns, cf.altName...), '.')
   219  					structNs = append(append(structNs, cf.name...), '.')
   220  				}
   221  
   222  				v.validateStruct(ctx, parent, current, typ, ns, structNs, ct)
   223  			}
   224  			return
   225  
   226  		case typeOmitEmpty:
   227  
   228  			// set Field Level fields
   229  			v.slflParent = parent
   230  			v.flField = current
   231  			v.cf = cf
   232  			v.ct = ct
   233  
   234  			if !hasValue(v) {
   235  				return
   236  			}
   237  
   238  			ct = ct.next
   239  			continue
   240  
   241  		case typeOmitNil:
   242  			v.slflParent = parent
   243  			v.flField = current
   244  			v.cf = cf
   245  			v.ct = ct
   246  
   247  			switch field := v.Field(); field.Kind() {
   248  			case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
   249  				if field.IsNil() {
   250  					return
   251  				}
   252  			default:
   253  				if v.fldIsPointer && field.Interface() == nil {
   254  					return
   255  				}
   256  			}
   257  
   258  			ct = ct.next
   259  			continue
   260  
   261  		case typeEndKeys:
   262  			return
   263  
   264  		case typeDive:
   265  
   266  			ct = ct.next
   267  
   268  			// traverse slice or map here
   269  			// or panic ;)
   270  			switch kind {
   271  			case reflect.Slice, reflect.Array:
   272  
   273  				var i64 int64
   274  				reusableCF := &cField{}
   275  
   276  				for i := 0; i < current.Len(); i++ {
   277  
   278  					i64 = int64(i)
   279  
   280  					v.misc = append(v.misc[0:0], cf.name...)
   281  					v.misc = append(v.misc, '[')
   282  					v.misc = strconv.AppendInt(v.misc, i64, 10)
   283  					v.misc = append(v.misc, ']')
   284  
   285  					reusableCF.name = string(v.misc)
   286  
   287  					if cf.namesEqual {
   288  						reusableCF.altName = reusableCF.name
   289  					} else {
   290  
   291  						v.misc = append(v.misc[0:0], cf.altName...)
   292  						v.misc = append(v.misc, '[')
   293  						v.misc = strconv.AppendInt(v.misc, i64, 10)
   294  						v.misc = append(v.misc, ']')
   295  
   296  						reusableCF.altName = string(v.misc)
   297  					}
   298  					v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
   299  				}
   300  
   301  			case reflect.Map:
   302  
   303  				var pv string
   304  				reusableCF := &cField{}
   305  
   306  				for _, key := range current.MapKeys() {
   307  
   308  					pv = fmt.Sprintf("%v", key.Interface())
   309  
   310  					v.misc = append(v.misc[0:0], cf.name...)
   311  					v.misc = append(v.misc, '[')
   312  					v.misc = append(v.misc, pv...)
   313  					v.misc = append(v.misc, ']')
   314  
   315  					reusableCF.name = string(v.misc)
   316  
   317  					if cf.namesEqual {
   318  						reusableCF.altName = reusableCF.name
   319  					} else {
   320  						v.misc = append(v.misc[0:0], cf.altName...)
   321  						v.misc = append(v.misc, '[')
   322  						v.misc = append(v.misc, pv...)
   323  						v.misc = append(v.misc, ']')
   324  
   325  						reusableCF.altName = string(v.misc)
   326  					}
   327  
   328  					if ct != nil && ct.typeof == typeKeys && ct.keys != nil {
   329  						v.traverseField(ctx, parent, key, ns, structNs, reusableCF, ct.keys)
   330  						// can be nil when just keys being validated
   331  						if ct.next != nil {
   332  							v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
   333  						}
   334  					} else {
   335  						v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
   336  					}
   337  				}
   338  
   339  			default:
   340  				// throw error, if not a slice or map then should not have gotten here
   341  				// bad dive tag
   342  				panic("dive error! can't dive on a non slice or map")
   343  			}
   344  
   345  			return
   346  
   347  		case typeOr:
   348  
   349  			v.misc = v.misc[0:0]
   350  
   351  			for {
   352  
   353  				// set Field Level fields
   354  				v.slflParent = parent
   355  				v.flField = current
   356  				v.cf = cf
   357  				v.ct = ct
   358  
   359  				if ct.fn(ctx, v) {
   360  					if ct.isBlockEnd {
   361  						ct = ct.next
   362  						continue OUTER
   363  					}
   364  
   365  					// drain rest of the 'or' values, then continue or leave
   366  					for {
   367  
   368  						ct = ct.next
   369  
   370  						if ct == nil {
   371  							continue OUTER
   372  						}
   373  
   374  						if ct.typeof != typeOr {
   375  							continue OUTER
   376  						}
   377  
   378  						if ct.isBlockEnd {
   379  							ct = ct.next
   380  							continue OUTER
   381  						}
   382  					}
   383  				}
   384  
   385  				v.misc = append(v.misc, '|')
   386  				v.misc = append(v.misc, ct.tag...)
   387  
   388  				if ct.hasParam {
   389  					v.misc = append(v.misc, '=')
   390  					v.misc = append(v.misc, ct.param...)
   391  				}
   392  
   393  				if ct.isBlockEnd || ct.next == nil {
   394  					// if we get here, no valid 'or' value and no more tags
   395  					v.str1 = string(append(ns, cf.altName...))
   396  
   397  					if v.v.hasTagNameFunc {
   398  						v.str2 = string(append(structNs, cf.name...))
   399  					} else {
   400  						v.str2 = v.str1
   401  					}
   402  
   403  					if ct.hasAlias {
   404  
   405  						v.errs = append(v.errs,
   406  							&fieldError{
   407  								v:              v.v,
   408  								tag:            ct.aliasTag,
   409  								actualTag:      ct.actualAliasTag,
   410  								ns:             v.str1,
   411  								structNs:       v.str2,
   412  								fieldLen:       uint8(len(cf.altName)),
   413  								structfieldLen: uint8(len(cf.name)),
   414  								value:          getValue(current),
   415  								param:          ct.param,
   416  								kind:           kind,
   417  								typ:            typ,
   418  							},
   419  						)
   420  
   421  					} else {
   422  
   423  						tVal := string(v.misc)[1:]
   424  
   425  						v.errs = append(v.errs,
   426  							&fieldError{
   427  								v:              v.v,
   428  								tag:            tVal,
   429  								actualTag:      tVal,
   430  								ns:             v.str1,
   431  								structNs:       v.str2,
   432  								fieldLen:       uint8(len(cf.altName)),
   433  								structfieldLen: uint8(len(cf.name)),
   434  								value:          getValue(current),
   435  								param:          ct.param,
   436  								kind:           kind,
   437  								typ:            typ,
   438  							},
   439  						)
   440  					}
   441  
   442  					return
   443  				}
   444  
   445  				ct = ct.next
   446  			}
   447  
   448  		default:
   449  
   450  			// set Field Level fields
   451  			v.slflParent = parent
   452  			v.flField = current
   453  			v.cf = cf
   454  			v.ct = ct
   455  
   456  			if !ct.fn(ctx, v) {
   457  				v.str1 = string(append(ns, cf.altName...))
   458  
   459  				if v.v.hasTagNameFunc {
   460  					v.str2 = string(append(structNs, cf.name...))
   461  				} else {
   462  					v.str2 = v.str1
   463  				}
   464  
   465  				v.errs = append(v.errs,
   466  					&fieldError{
   467  						v:              v.v,
   468  						tag:            ct.aliasTag,
   469  						actualTag:      ct.tag,
   470  						ns:             v.str1,
   471  						structNs:       v.str2,
   472  						fieldLen:       uint8(len(cf.altName)),
   473  						structfieldLen: uint8(len(cf.name)),
   474  						value:          getValue(current),
   475  						param:          ct.param,
   476  						kind:           kind,
   477  						typ:            typ,
   478  					},
   479  				)
   480  
   481  				return
   482  			}
   483  			ct = ct.next
   484  		}
   485  	}
   486  
   487  }
   488  
   489  func getValue(val reflect.Value) interface{} {
   490  	if val.CanInterface() {
   491  		return val.Interface()
   492  	}
   493  
   494  	if val.CanAddr() {
   495  		return reflect.NewAt(val.Type(), unsafe.Pointer(val.UnsafeAddr())).Elem().Interface()
   496  	}
   497  
   498  	switch val.Kind() {
   499  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   500  		return val.Int()
   501  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   502  		return val.Uint()
   503  	case reflect.Complex64, reflect.Complex128:
   504  		return val.Complex()
   505  	case reflect.Float32, reflect.Float64:
   506  		return val.Float()
   507  	default:
   508  		return val.String()
   509  	}
   510  }
   511  

View as plain text