util.go raw

   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  // asFloat32 returns the parameter as a float32
 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(regexFn func() *regexp.Regexp, fl FieldLevel) bool {
 301  	regex := regexFn()
 302  	switch fl.Field().Kind() {
 303  	case reflect.String:
 304  		return regex.MatchString(fl.Field().String())
 305  	default:
 306  		if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
 307  			return regex.MatchString(stringer.String())
 308  		} else {
 309  			return regex.MatchString(fl.Field().String())
 310  		}
 311  	}
 312  }
 313