From 49e73247eb694ad53520ba70ea842f87631df6b2 Mon Sep 17 00:00:00 2001 From: Neur0toxine Date: Thu, 13 Mar 2025 12:38:04 +0300 Subject: [PATCH] slice & array support for map extract --- core/util/testutil/map_extract.go | 29 +++++++++++++++++++++----- core/util/testutil/map_extract_test.go | 17 +++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/core/util/testutil/map_extract.go b/core/util/testutil/map_extract.go index 87c4fe6..3356454 100644 --- a/core/util/testutil/map_extract.go +++ b/core/util/testutil/map_extract.go @@ -8,11 +8,14 @@ import ( "testing" ) -// MapValue extracts nested map values using dot notation. +// MapValue extracts nested map values using dot notation. Keys are separated by dots, slices and arrays can be +// accessed by integer indexes. // Example: -// MapValue(m, "key1") -// MapValue(m, "key1.key2") -// MapValue(m, "key1.key2.key3") +// +// MapValue(m, "key1") // Access value with key "key1" +// MapValue(m, "key1.key2") // Access nested value with key "key2" +// MapValue(m, "key1.key2.key3") // Access nested value with key "key3" +// MapValue(m, "key1.key2.key3.0") // Access the first slice / array element in the nested map. func MapValue(data interface{}, path string) (interface{}, error) { if path == "" { return data, nil @@ -43,7 +46,23 @@ func MapValue(data interface{}, path string) (interface{}, error) { continue } - return nil, fmt.Errorf("value at path '%s' is not a map", + if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { + index, err := strconv.Atoi(part) + if err != nil { + return nil, fmt.Errorf("'%s' is not a valid slice / array index", part) + } + + if index < 0 || index >= v.Len() { + return nil, fmt.Errorf("index %d out of bounds for %s of length %d at path '%s'", + index, v.Kind().String(), v.Len(), strings.Join(parts[:i], ".")) + } + + current = v.Index(index).Interface() + + continue + } + + return nil, fmt.Errorf("value at path '%s' is not a map, slice or array", strings.Join(parts[:i], ".")) } diff --git a/core/util/testutil/map_extract_test.go b/core/util/testutil/map_extract_test.go index e8bc439..2a16fb8 100644 --- a/core/util/testutil/map_extract_test.go +++ b/core/util/testutil/map_extract_test.go @@ -56,9 +56,20 @@ func TestMapValue_Nested(t *testing.T) { "key3": "value", }, }, + "key4": []interface{}{ + "value5", + map[string]interface{}{ + "key6": "value7", + }, + }, } assert.Equal(t, "value", MustMapValue(m, "key1.key2.key3").(string)) assert.Equal(t, "value", MustMapValue(m, "key1.key2").(map[string]interface{})["key3"].(string)) + assert.Equal(t, "value5", MustMapValue(m, "key4.0")) + assert.Equal(t, "value7", MustMapValue(m, "key4.1.key6")) + assert.Equal(t, "value", MustMapValue([]map[string]string{ + {"key": "value"}, + }, "0.key")) } func TestMapValue_ErrorNotAMap(t *testing.T) { @@ -81,6 +92,12 @@ func TestMapValue_ErrorKeyNotFound(t *testing.T) { assert.Equal(t, "key 'key3' not found at path 'key'", err.Error()) } +func TestMapValue_ErrorOutOfBounds(t *testing.T) { + _, err := MapValue(map[string][]int{"key": {1}}, "key.1") + assert.Error(t, err) + assert.Equal(t, "index 1 out of bounds for slice of length 1 at path 'key'", err.Error()) +} + func TestMustMapValue_Panics(t *testing.T) { assert.Panics(t, func() { MustMapValue(map[string]int{"key": 1}, "key2")