github.com/yandex/pandora@v0.5.32/lib/mp/map.go (about) 1 package mp 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 "strings" 8 ) 9 10 type ErrSegmentNotFound struct { 11 path string 12 segment string 13 } 14 15 func (e *ErrSegmentNotFound) Error() string { 16 return fmt.Sprintf("segment %s not found in path %s", e.segment, e.path) 17 } 18 19 func GetMapValue(current map[string]any, path string, iter Iterator) (any, error) { 20 if current == nil { 21 return nil, nil 22 } 23 var curSegment strings.Builder 24 segments := strings.Split(strings.TrimPrefix(path, "."), ".") 25 26 for i, segment := range segments { 27 segment = strings.TrimSpace(segment) 28 curSegment.WriteByte('.') 29 curSegment.WriteString(segment) 30 if strings.Contains(segment, "[") && strings.HasSuffix(segment, "]") { 31 openBraceIdx := strings.Index(segment, "[") 32 indexStr := strings.ToLower(strings.TrimSpace(segment[openBraceIdx+1 : len(segment)-1])) 33 34 segment = segment[:openBraceIdx] 35 pathVal, ok := current[segment] 36 if !ok { 37 return nil, &ErrSegmentNotFound{path: path, segment: segment} 38 } 39 sliceElement, err := extractFromSlice(pathVal, indexStr, curSegment.String(), iter) 40 if err != nil { 41 return nil, fmt.Errorf("cant extract value path=`%s`,segment=`%s`,err=%w", segment, path, err) 42 } 43 current, ok = sliceElement.(map[string]any) 44 if !ok { 45 if i != len(segments)-1 { 46 return nil, fmt.Errorf("not last segment %s in path %s", segment, path) 47 } 48 return sliceElement, nil 49 } 50 } else { 51 pathVal, ok := current[segment] 52 if !ok { 53 return nil, &ErrSegmentNotFound{path: path, segment: segment} 54 } 55 current, ok = pathVal.(map[string]any) 56 if !ok { 57 if i != len(segments)-1 { 58 return nil, fmt.Errorf("not last segment %s in path %s", segment, path) 59 } 60 return pathVal, nil 61 } 62 } 63 } 64 65 return current, nil 66 } 67 68 func extractFromSlice(curValue any, indexStr string, curSegment string, iter Iterator) (result any, err error) { 69 validTypes := []reflect.Type{ 70 reflect.TypeOf([]map[string]string{}), 71 reflect.TypeOf([]map[string]any{}), 72 reflect.TypeOf([]any{}), 73 reflect.TypeOf([]string{}), 74 reflect.TypeOf([]int{}), 75 reflect.TypeOf([]int64{}), 76 reflect.TypeOf([]float64{}), 77 } 78 79 var valueLen int 80 var valueFound bool 81 for _, valueType := range validTypes { 82 if reflect.TypeOf(curValue) == valueType { 83 valueLen = reflect.ValueOf(curValue).Len() 84 valueFound = true 85 break 86 } 87 } 88 89 if !valueFound { 90 return nil, fmt.Errorf("invalid type of value `%+v`, %T", curValue, curValue) 91 } 92 93 index, err := calcIndex(indexStr, curSegment, valueLen, iter) 94 if err != nil { 95 return nil, fmt.Errorf("failed to calc index for %T; err: %w", curValue, err) 96 } 97 98 switch v := curValue.(type) { 99 case []map[string]string: 100 currentData := make(map[string]any, len(v[index])) 101 for k, val := range v[index] { 102 currentData[k] = val 103 } 104 return currentData, nil 105 case []map[string]any: 106 return v[index], nil 107 case []any: 108 return v[index], nil 109 case []string: 110 return v[index], nil 111 case []int: 112 return v[index], nil 113 case []int64: 114 return v[index], nil 115 case []float64: 116 return v[index], nil 117 } 118 119 // This line should never be reached, as we've covered all valid types above 120 return nil, fmt.Errorf("invalid type of value `%+v`, %T", curValue, curValue) 121 } 122 123 func calcIndex(indexStr string, segment string, length int, iter Iterator) (int, error) { 124 index, err := strconv.Atoi(indexStr) 125 if err != nil && indexStr != "next" && indexStr != "rand" && indexStr != "last" { 126 return 0, fmt.Errorf("index should be integer or one of [next, rand, last], but got `%s`", indexStr) 127 } 128 if indexStr != "next" && indexStr != "rand" && indexStr != "last" { 129 if index >= 0 && index < length { 130 return index, nil 131 } 132 index %= length 133 if index < 0 { 134 index += length 135 } 136 return index, nil 137 } 138 139 if indexStr == "last" { 140 return length - 1, nil 141 } 142 if indexStr == "rand" { 143 return iter.Rand(length), nil 144 } 145 index = iter.Next(segment) 146 if index >= length { 147 index %= length 148 } 149 return index, nil 150 }