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  }