Evaluates a "key path", like "points[3].x.y" on a JSON-based map.
(root map[string]any, keyPath string)
| 354 | |
| 355 | // Evaluates a "key path", like "points[3].x.y" on a JSON-based map. |
| 356 | func evalKeyPath(root map[string]any, keyPath string) (reflect.Value, error) { |
| 357 | // Handle the first path component specially because we can access `root` without reflection: |
| 358 | var value reflect.Value |
| 359 | i := strings.IndexAny(keyPath, ".[") |
| 360 | if i < 0 { |
| 361 | i = len(keyPath) |
| 362 | } |
| 363 | key := keyPath[0:i] |
| 364 | keyPath = keyPath[i:] |
| 365 | firstVal := root[key] |
| 366 | if firstVal == nil { |
| 367 | return value, base.HTTPErrorf(http.StatusInternalServerError, "parameter %q is not declared in 'args'", key) |
| 368 | } |
| 369 | |
| 370 | value = reflect.ValueOf(firstVal) |
| 371 | if len(keyPath) == 0 { |
| 372 | return value, nil |
| 373 | } |
| 374 | |
| 375 | for len(keyPath) > 0 { |
| 376 | ch := keyPath[0] |
| 377 | keyPath = keyPath[1:] |
| 378 | if ch == '.' { |
| 379 | i = strings.IndexAny(keyPath, ".[") |
| 380 | if i < 0 { |
| 381 | i = len(keyPath) |
| 382 | } |
| 383 | key = keyPath[0:i] |
| 384 | keyPath = keyPath[i:] |
| 385 | |
| 386 | if value.Kind() != reflect.Map { |
| 387 | return value, base.HTTPErrorf(http.StatusBadRequest, "value is not a map") |
| 388 | } |
| 389 | value = value.MapIndex(reflect.ValueOf(key)) |
| 390 | } else if ch == '[' { |
| 391 | i = strings.IndexByte(keyPath, ']') |
| 392 | if i < 0 { |
| 393 | return value, base.HTTPErrorf(http.StatusInternalServerError, "missing ']") |
| 394 | } |
| 395 | key = keyPath[0:i] |
| 396 | keyPath = keyPath[i+1:] |
| 397 | |
| 398 | index, err := strconv.ParseUint(key, 10, 8) |
| 399 | if err != nil { |
| 400 | return value, err |
| 401 | } |
| 402 | if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { |
| 403 | return value, base.HTTPErrorf(http.StatusBadRequest, "value is a %v not an array", value.Type()) |
| 404 | } else if uint64(value.Len()) <= index { |
| 405 | return value, base.HTTPErrorf(http.StatusBadRequest, "array index out of range") |
| 406 | } |
| 407 | value = value.Index(int(index)) |
| 408 | } else { |
| 409 | return value, base.HTTPErrorf(http.StatusInternalServerError, "invalid character after a ']'") |
| 410 | } |
| 411 | for value.Kind() == reflect.Interface || value.Kind() == reflect.Pointer { |
| 412 | value = value.Elem() |
| 413 | } |