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