...

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

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

     1  package validator
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  // extractTypeInternal gets the actual underlying type of field value.
    13  // It will dive into pointers, customTypes and return you the
    14  // underlying value and it's kind.
    15  func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) {
    16  
    17  BEGIN:
    18  	switch current.Kind() {
    19  	case reflect.Ptr:
    20  
    21  		nullable = true
    22  
    23  		if current.IsNil() {
    24  			return current, reflect.Ptr, nullable
    25  		}
    26  
    27  		current = current.Elem()
    28  		goto BEGIN
    29  
    30  	case reflect.Interface:
    31  
    32  		nullable = true
    33  
    34  		if current.IsNil() {
    35  			return current, reflect.Interface, nullable
    36  		}
    37  
    38  		current = current.Elem()
    39  		goto BEGIN
    40  
    41  	case reflect.Invalid:
    42  		return current, reflect.Invalid, nullable
    43  
    44  	default:
    45  
    46  		if v.v.hasCustomFuncs {
    47  
    48  			if fn, ok := v.v.customFuncs[current.Type()]; ok {
    49  				current = reflect.ValueOf(fn(current))
    50  				goto BEGIN
    51  			}
    52  		}
    53  
    54  		return current, current.Kind(), nullable
    55  	}
    56  }
    57  
    58  // getStructFieldOKInternal traverses a struct to retrieve a specific field denoted by the provided namespace and
    59  // returns the field, field kind and whether is was successful in retrieving the field at all.
    60  //
    61  // NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
    62  // could not be retrieved because it didn't exist.
    63  func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
    64  
    65  BEGIN:
    66  	current, kind, nullable = v.ExtractType(val)
    67  	if kind == reflect.Invalid {
    68  		return
    69  	}
    70  
    71  	if namespace == "" {
    72  		found = true
    73  		return
    74  	}
    75  
    76  	switch kind {
    77  
    78  	case reflect.Ptr, reflect.Interface:
    79  		return
    80  
    81  	case reflect.Struct:
    82  
    83  		typ := current.Type()
    84  		fld := namespace
    85  		var ns string
    86  
    87  		if !typ.ConvertibleTo(timeType) {
    88  
    89  			idx := strings.Index(namespace, namespaceSeparator)
    90  
    91  			if idx != -1 {
    92  				fld = namespace[:idx]
    93  				ns = namespace[idx+1:]
    94  			} else {
    95  				ns = ""
    96  			}
    97  
    98  			bracketIdx := strings.Index(fld, leftBracket)
    99  			if bracketIdx != -1 {
   100  				fld = fld[:bracketIdx]
   101  
   102  				ns = namespace[bracketIdx:]
   103  			}
   104  
   105  			val = current.FieldByName(fld)
   106  			namespace = ns
   107  			goto BEGIN
   108  		}
   109  
   110  	case reflect.Array, reflect.Slice:
   111  		idx := strings.Index(namespace, leftBracket)
   112  		idx2 := strings.Index(namespace, rightBracket)
   113  
   114  		arrIdx, _ := strconv.Atoi(namespace[idx+1 : idx2])
   115  
   116  		if arrIdx >= current.Len() {
   117  			return
   118  		}
   119  
   120  		startIdx := idx2 + 1
   121  
   122  		if startIdx < len(namespace) {
   123  			if namespace[startIdx:startIdx+1] == namespaceSeparator {
   124  				startIdx++
   125  			}
   126  		}
   127  
   128  		val = current.Index(arrIdx)
   129  		namespace = namespace[startIdx:]
   130  		goto BEGIN
   131  
   132  	case reflect.Map:
   133  		idx := strings.Index(namespace, leftBracket) + 1
   134  		idx2 := strings.Index(namespace, rightBracket)
   135  
   136  		endIdx := idx2
   137  
   138  		if endIdx+1 < len(namespace) {
   139  			if namespace[endIdx+1:endIdx+2] == namespaceSeparator {
   140  				endIdx++
   141  			}
   142  		}
   143  
   144  		key := namespace[idx:idx2]
   145  
   146  		switch current.Type().Key().Kind() {
   147  		case reflect.Int:
   148  			i, _ := strconv.Atoi(key)
   149  			val = current.MapIndex(reflect.ValueOf(i))
   150  			namespace = namespace[endIdx+1:]
   151  
   152  		case reflect.Int8:
   153  			i, _ := strconv.ParseInt(key, 10, 8)
   154  			val = current.MapIndex(reflect.ValueOf(int8(i)))
   155  			namespace = namespace[endIdx+1:]
   156  
   157  		case reflect.Int16:
   158  			i, _ := strconv.ParseInt(key, 10, 16)
   159  			val = current.MapIndex(reflect.ValueOf(int16(i)))
   160  			namespace = namespace[endIdx+1:]
   161  
   162  		case reflect.Int32:
   163  			i, _ := strconv.ParseInt(key, 10, 32)
   164  			val = current.MapIndex(reflect.ValueOf(int32(i)))
   165  			namespace = namespace[endIdx+1:]
   166  
   167  		case reflect.Int64:
   168  			i, _ := strconv.ParseInt(key, 10, 64)
   169  			val = current.MapIndex(reflect.ValueOf(i))
   170  			namespace = namespace[endIdx+1:]
   171  
   172  		case reflect.Uint:
   173  			i, _ := strconv.ParseUint(key, 10, 0)
   174  			val = current.MapIndex(reflect.ValueOf(uint(i)))
   175  			namespace = namespace[endIdx+1:]
   176  
   177  		case reflect.Uint8:
   178  			i, _ := strconv.ParseUint(key, 10, 8)
   179  			val = current.MapIndex(reflect.ValueOf(uint8(i)))
   180  			namespace = namespace[endIdx+1:]
   181  
   182  		case reflect.Uint16:
   183  			i, _ := strconv.ParseUint(key, 10, 16)
   184  			val = current.MapIndex(reflect.ValueOf(uint16(i)))
   185  			namespace = namespace[endIdx+1:]
   186  
   187  		case reflect.Uint32:
   188  			i, _ := strconv.ParseUint(key, 10, 32)
   189  			val = current.MapIndex(reflect.ValueOf(uint32(i)))
   190  			namespace = namespace[endIdx+1:]
   191  
   192  		case reflect.Uint64:
   193  			i, _ := strconv.ParseUint(key, 10, 64)
   194  			val = current.MapIndex(reflect.ValueOf(i))
   195  			namespace = namespace[endIdx+1:]
   196  
   197  		case reflect.Float32:
   198  			f, _ := strconv.ParseFloat(key, 32)
   199  			val = current.MapIndex(reflect.ValueOf(float32(f)))
   200  			namespace = namespace[endIdx+1:]
   201  
   202  		case reflect.Float64:
   203  			f, _ := strconv.ParseFloat(key, 64)
   204  			val = current.MapIndex(reflect.ValueOf(f))
   205  			namespace = namespace[endIdx+1:]
   206  
   207  		case reflect.Bool:
   208  			b, _ := strconv.ParseBool(key)
   209  			val = current.MapIndex(reflect.ValueOf(b))
   210  			namespace = namespace[endIdx+1:]
   211  
   212  		// reflect.Type = string
   213  		default:
   214  			val = current.MapIndex(reflect.ValueOf(key))
   215  			namespace = namespace[endIdx+1:]
   216  		}
   217  
   218  		goto BEGIN
   219  	}
   220  
   221  	// if got here there was more namespace, cannot go any deeper
   222  	panic("Invalid field namespace")
   223  }
   224  
   225  // asInt returns the parameter as a int64
   226  // or panics if it can't convert
   227  func asInt(param string) int64 {
   228  	i, err := strconv.ParseInt(param, 0, 64)
   229  	panicIf(err)
   230  
   231  	return i
   232  }
   233  
   234  // asIntFromTimeDuration parses param as time.Duration and returns it as int64
   235  // or panics on error.
   236  func asIntFromTimeDuration(param string) int64 {
   237  	d, err := time.ParseDuration(param)
   238  	if err != nil {
   239  		// attempt parsing as an integer assuming nanosecond precision
   240  		return asInt(param)
   241  	}
   242  	return int64(d)
   243  }
   244  
   245  // asIntFromType calls the proper function to parse param as int64,
   246  // given a field's Type t.
   247  func asIntFromType(t reflect.Type, param string) int64 {
   248  	switch t {
   249  	case timeDurationType:
   250  		return asIntFromTimeDuration(param)
   251  	default:
   252  		return asInt(param)
   253  	}
   254  }
   255  
   256  // asUint returns the parameter as a uint64
   257  // or panics if it can't convert
   258  func asUint(param string) uint64 {
   259  
   260  	i, err := strconv.ParseUint(param, 0, 64)
   261  	panicIf(err)
   262  
   263  	return i
   264  }
   265  
   266  // asFloat64 returns the parameter as a float64
   267  // or panics if it can't convert
   268  func asFloat64(param string) float64 {
   269  	i, err := strconv.ParseFloat(param, 64)
   270  	panicIf(err)
   271  	return i
   272  }
   273  
   274  // asFloat64 returns the parameter as a float64
   275  // or panics if it can't convert
   276  func asFloat32(param string) float64 {
   277  	i, err := strconv.ParseFloat(param, 32)
   278  	panicIf(err)
   279  	return i
   280  }
   281  
   282  // asBool returns the parameter as a bool
   283  // or panics if it can't convert
   284  func asBool(param string) bool {
   285  
   286  	i, err := strconv.ParseBool(param)
   287  	panicIf(err)
   288  
   289  	return i
   290  }
   291  
   292  func panicIf(err error) {
   293  	if err != nil {
   294  		panic(err.Error())
   295  	}
   296  }
   297  
   298  // Checks if field value matches regex. If fl.Field can be cast to Stringer, it uses the Stringer interfaces
   299  // String() return value. Otherwise, it uses fl.Field's String() value.
   300  func fieldMatchesRegexByStringerValOrString(regex *regexp.Regexp, fl FieldLevel) bool {
   301  	switch fl.Field().Kind() {
   302  	case reflect.String:
   303  		return regex.MatchString(fl.Field().String())
   304  	default:
   305  		if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
   306  			return regex.MatchString(stringer.String())
   307  		} else {
   308  			return regex.MatchString(fl.Field().String())
   309  		}
   310  	}
   311  }
   312  

View as plain text