From bfa6b9dcef7592e6dd8225aaa0d0ab5aef5b3f84 Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Sun, 9 Mar 2025 20:14:46 -0400 Subject: chore(refactoring): Refactor/modernizations (#2345) chore(refactoring): Refactor with modernization refactorings * range over int * replace interface{} with any * replace common map operations with maps.Copy/maps.Clone * simplifying loops with slices.Contains/ContainsFunc * modernize benchmarks with b.Loop * modernize tests with t.Context * use fmt.Appendf * range over strings.SplitSeq * use new stdlib crypto/pbkdf2 package --------- Signed-off-by: Dave Henderson --- coll/coll.go | 100 +++++++--------- coll/coll_test.go | 326 +++++++++++++++++++++++++------------------------- coll/index.go | 2 +- coll/index_test.go | 12 +- coll/jq.go | 18 +-- coll/jq_test.go | 56 ++++----- coll/jsonpath.go | 8 +- coll/jsonpath_test.go | 6 +- 8 files changed, 256 insertions(+), 272 deletions(-) (limited to 'coll') diff --git a/coll/coll.go b/coll/coll.go index cbc96023..310e7cf3 100644 --- a/coll/coll.go +++ b/coll/coll.go @@ -1,13 +1,15 @@ // Package coll contains functions to help manipulate and query collections of // data, like slices/arrays and maps. // -// For the functions that return an array, a []interface{} is returned, +// For the functions that return an array, a []any is returned, // regardless of whether or not the input was a different type. package coll import ( "fmt" + "maps" "reflect" + "slices" "sort" "github.com/hairyhenderson/gomplate/v4/conv" @@ -15,12 +17,12 @@ import ( ) // Slice creates a slice from a bunch of arguments -func Slice(args ...interface{}) []interface{} { +func Slice(args ...any) []any { return args } // Has determines whether or not a given object has a property with the given key -func Has(in interface{}, key interface{}) bool { +func Has(in any, key any) bool { av := reflect.ValueOf(in) switch av.Kind() { @@ -29,7 +31,7 @@ func Has(in interface{}, key interface{}) bool { return av.MapIndex(kv).IsValid() case reflect.Slice, reflect.Array: l := av.Len() - for i := 0; i < l; i++ { + for i := range l { v := av.Index(i).Interface() if reflect.DeepEqual(v, key) { return true @@ -45,8 +47,8 @@ func Has(in interface{}, key interface{}) bool { // is provided, the last is used as the key, and an empty string is // set as the value. // All keys are converted to strings, regardless of input type. -func Dict(v ...interface{}) (map[string]interface{}, error) { - dict := map[string]interface{}{} +func Dict(v ...any) (map[string]any, error) { + dict := map[string]any{} lenv := len(v) for i := 0; i < lenv; i += 2 { key := conv.ToString(v[i]) @@ -61,7 +63,7 @@ func Dict(v ...interface{}) (map[string]interface{}, error) { // Keys returns the list of keys in one or more maps. The returned list of keys // is ordered by map, each in sorted key order. -func Keys(in ...map[string]interface{}) ([]string, error) { +func Keys(in ...map[string]any) ([]string, error) { if len(in) == 0 { return nil, fmt.Errorf("need at least one argument") } @@ -73,9 +75,9 @@ func Keys(in ...map[string]interface{}) ([]string, error) { return keys, nil } -func splitMap(m map[string]interface{}) ([]string, []interface{}) { +func splitMap(m map[string]any) ([]string, []any) { keys := make([]string, len(m)) - values := make([]interface{}, len(m)) + values := make([]any, len(m)) i := 0 for k := range m { keys[i] = k @@ -91,11 +93,11 @@ func splitMap(m map[string]interface{}) ([]string, []interface{}) { // Values returns the list of values in one or more maps. The returned list of values // is ordered by map, each in sorted key order. If the Keys function is called with // the same arguments, the key/value mappings will be maintained. -func Values(in ...map[string]interface{}) ([]interface{}, error) { +func Values(in ...map[string]any) ([]any, error) { if len(in) == 0 { return nil, fmt.Errorf("need at least one argument") } - values := []interface{}{} + values := []any{} for _, m := range in { _, v := splitMap(m) values = append(values, v...) @@ -103,8 +105,8 @@ func Values(in ...map[string]interface{}) ([]interface{}, error) { return values, nil } -// Append v to the end of list. No matter what type of input slice or array list is, a new []interface{} is always returned. -func Append(v interface{}, list interface{}) ([]interface{}, error) { +// Append v to the end of list. No matter what type of input slice or array list is, a new []any is always returned. +func Append(v any, list any) ([]any, error) { l, err := iconv.InterfaceSlice(list) if err != nil { return nil, err @@ -113,24 +115,24 @@ func Append(v interface{}, list interface{}) ([]interface{}, error) { return append(l, v), nil } -// Prepend v to the beginning of list. No matter what type of input slice or array list is, a new []interface{} is always returned. -func Prepend(v interface{}, list interface{}) ([]interface{}, error) { +// Prepend v to the beginning of list. No matter what type of input slice or array list is, a new []any is always returned. +func Prepend(v any, list any) ([]any, error) { l, err := iconv.InterfaceSlice(list) if err != nil { return nil, err } - return append([]interface{}{v}, l...), nil + return append([]any{v}, l...), nil } -// Uniq finds the unique values within list. No matter what type of input slice or array list is, a new []interface{} is always returned. -func Uniq(list interface{}) ([]interface{}, error) { +// Uniq finds the unique values within list. No matter what type of input slice or array list is, a new []any is always returned. +func Uniq(list any) ([]any, error) { l, err := iconv.InterfaceSlice(list) if err != nil { return nil, err } - out := []interface{}{} + out := []any{} for _, v := range l { if !Has(out, v) { out = append(out, v) @@ -139,8 +141,8 @@ func Uniq(list interface{}) ([]interface{}, error) { return out, nil } -// Reverse the list. No matter what type of input slice or array list is, a new []interface{} is always returned. -func Reverse(list interface{}) ([]interface{}, error) { +// Reverse the list. No matter what type of input slice or array list is, a new []any is always returned. +func Reverse(list any) ([]any, error) { l, err := iconv.InterfaceSlice(list) if err != nil { return nil, err @@ -155,29 +157,19 @@ func Reverse(list interface{}) ([]interface{}, error) { // Merge source maps (srcs) into dst. Precedence is in left-to-right order, with // the left-most values taking precedence over the right-most. -func Merge(dst map[string]interface{}, srcs ...map[string]interface{}) (map[string]interface{}, error) { +func Merge(dst map[string]any, srcs ...map[string]any) (map[string]any, error) { for _, src := range srcs { dst = mergeValues(src, dst) } return dst, nil } -// returns whether or not a contains v -func contains(v string, a []string) bool { - for _, n := range a { - if n == v { - return true - } - } - return false -} - // Omit returns a new map without any entries that have the // given keys (inverse of Pick). -func Omit(in map[string]interface{}, keys ...string) map[string]interface{} { - out := map[string]interface{}{} +func Omit(in map[string]any, keys ...string) map[string]any { + out := map[string]any{} for k, v := range in { - if !contains(k, keys) { + if !slices.Contains(keys, k) { out[k] = v } } @@ -186,42 +178,34 @@ func Omit(in map[string]interface{}, keys ...string) map[string]interface{} { // Pick returns a new map with any entries that have the // given keys (inverse of Omit). -func Pick(in map[string]interface{}, keys ...string) map[string]interface{} { - out := map[string]interface{}{} +func Pick(in map[string]any, keys ...string) map[string]any { + out := map[string]any{} for k, v := range in { - if contains(k, keys) { + if slices.Contains(keys, k) { out[k] = v } } return out } -func copyMap(m map[string]interface{}) map[string]interface{} { - n := map[string]interface{}{} - for k, v := range m { - n[k] = v - } - return n -} - // Merges a default and override map -func mergeValues(d map[string]interface{}, o map[string]interface{}) map[string]interface{} { - def := copyMap(d) - over := copyMap(o) +func mergeValues(d map[string]any, o map[string]any) map[string]any { + def := maps.Clone(d) + over := maps.Clone(o) for k, v := range over { // If the key doesn't exist already, then just set the key to that value if _, exists := def[k]; !exists { def[k] = v continue } - nextMap, ok := v.(map[string]interface{}) + nextMap, ok := v.(map[string]any) // If it isn't another map, overwrite the value if !ok { def[k] = v continue } // Edge case: If the key exists in the default, but isn't a map - defMap, isMap := def[k].(map[string]interface{}) + defMap, isMap := def[k].(map[string]any) // If the override map has a map for this key, prefer it if !isMap { def[k] = v @@ -238,7 +222,7 @@ func mergeValues(d map[string]interface{}, o map[string]interface{}) map[string] // sort by the values of those entries. // // Does not modify the input list. -func Sort(key string, list interface{}) (out []interface{}, err error) { +func Sort(key string, list any) (out []any, err error) { if list == nil { return nil, nil } @@ -249,7 +233,7 @@ func Sort(key string, list interface{}) (out []interface{}, err error) { } // if the types are all the same, we can sort the slice if sameTypes(ia) { - s := make([]interface{}, len(ia)) + s := make([]any, len(ia)) // make a copy so the original is unmodified copy(s, ia) sort.SliceStable(s, func(i, j int) bool { @@ -261,8 +245,8 @@ func Sort(key string, list interface{}) (out []interface{}, err error) { } // lessThan - compare two values of the same type -func lessThan(key string) func(left, right interface{}) bool { - return func(left, right interface{}) bool { +func lessThan(key string) func(left, right any) bool { + return func(left, right any) bool { val := reflect.Indirect(reflect.ValueOf(left)) rval := reflect.Indirect(reflect.ValueOf(right)) switch val.Kind() { @@ -299,7 +283,7 @@ func lessThan(key string) func(left, right interface{}) bool { } } -func sameTypes(a []interface{}) bool { +func sameTypes(a []any) bool { var t reflect.Type for _, v := range a { if t == nil { @@ -315,7 +299,7 @@ func sameTypes(a []interface{}) bool { // Flatten a nested array or slice to at most 'depth' levels. Use depth of -1 // to completely flatten the input. // Returns a new slice without modifying the input. -func Flatten(list interface{}, depth int) ([]interface{}, error) { +func Flatten(list any, depth int) ([]any, error) { l, err := iconv.InterfaceSlice(list) if err != nil { return nil, err @@ -323,7 +307,7 @@ func Flatten(list interface{}, depth int) ([]interface{}, error) { if depth == 0 { return l, nil } - out := make([]interface{}, 0, len(l)*2) + out := make([]any, 0, len(l)*2) for _, v := range l { s := reflect.ValueOf(v) kind := s.Kind() diff --git a/coll/coll_test.go b/coll/coll_test.go index 4f7da49f..ab2241ee 100644 --- a/coll/coll_test.go +++ b/coll/coll_test.go @@ -16,24 +16,24 @@ func TestSlice(t *testing.T) { } func TestHas(t *testing.T) { - in := map[string]interface{}{ + in := map[string]any{ "foo": "bar", - "baz": map[string]interface{}{ + "baz": map[string]any{ "qux": "quux", }, } testdata := []struct { - in interface{} - key interface{} + in any + key any out bool }{ {in, "foo", true}, {in, "bar", false}, {in["baz"], "qux", true}, {[]string{"foo", "bar", "baz"}, "bar", true}, - {[]interface{}{"foo", "bar", "baz"}, "bar", true}, - {[]interface{}{"foo", "bar", "baz"}, 42, false}, + {[]any{"foo", "bar", "baz"}, "bar", true}, + {[]any{"foo", "bar", "baz"}, 42, false}, {[]int{1, 2, 42}, 42, true}, } @@ -44,16 +44,16 @@ func TestHas(t *testing.T) { func TestDict(t *testing.T) { testdata := []struct { - expected map[string]interface{} - args []interface{} + expected map[string]any + args []any }{ - {expected: map[string]interface{}{}}, - {args: []interface{}{}, expected: map[string]interface{}{}}, - {args: []interface{}{"foo"}, expected: map[string]interface{}{"foo": ""}}, - {args: []interface{}{42}, expected: map[string]interface{}{"42": ""}}, - {args: []interface{}{"foo", nil}, expected: map[string]interface{}{"foo": nil}}, - {args: []interface{}{"foo", "bar"}, expected: map[string]interface{}{"foo": "bar"}}, - {args: []interface{}{"foo", "bar", "baz", true}, expected: map[string]interface{}{ + {expected: map[string]any{}}, + {args: []any{}, expected: map[string]any{}}, + {args: []any{"foo"}, expected: map[string]any{"foo": ""}}, + {args: []any{42}, expected: map[string]any{"42": ""}}, + {args: []any{"foo", nil}, expected: map[string]any{"foo": nil}}, + {args: []any{"foo", "bar"}, expected: map[string]any{"foo": "bar"}}, + {args: []any{"foo", "bar", "baz", true}, expected: map[string]any{ "foo": "bar", "baz": true, }}, @@ -69,7 +69,7 @@ func TestKeys(t *testing.T) { _, err := Keys() require.Error(t, err) - in := map[string]interface{}{ + in := map[string]any{ "foo": 1, "bar": 2, } @@ -78,7 +78,7 @@ func TestKeys(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, expected, keys) - in2 := map[string]interface{}{ + in2 := map[string]any{ "baz": 3, "qux": 4, } @@ -87,7 +87,7 @@ func TestKeys(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, expected, keys) - in3 := map[string]interface{}{ + in3 := map[string]any{ "Foo": 5, "Bar": 6, "foo": 7, @@ -103,114 +103,114 @@ func TestValues(t *testing.T) { _, err := Values() require.Error(t, err) - in := map[string]interface{}{ + in := map[string]any{ "foo": 1, "bar": 2, } - expected := []interface{}{2, 1} + expected := []any{2, 1} values, err := Values(in) require.NoError(t, err) assert.EqualValues(t, expected, values) - in2 := map[string]interface{}{ + in2 := map[string]any{ "baz": 3, "qux": 4, } - expected = []interface{}{2, 1, 3, 4} + expected = []any{2, 1, 3, 4} values, err = Values(in, in2) require.NoError(t, err) assert.EqualValues(t, expected, values) - in3 := map[string]interface{}{ + in3 := map[string]any{ "Foo": 5, "Bar": 6, "foo": 7, "bar": 8, } - expected = []interface{}{2, 1, 3, 4, 6, 5, 8, 7} + expected = []any{2, 1, 3, 4, 6, 5, 8, 7} values, err = Values(in, in2, in3) require.NoError(t, err) assert.EqualValues(t, expected, values) } func TestAppend(t *testing.T) { - out, err := Append(42, []interface{}{}) + out, err := Append(42, []any{}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{42}, out) + assert.EqualValues(t, []any{42}, out) - out, err = Append(42, []interface{}{4.9, false, "foo"}) + out, err = Append(42, []any{4.9, false, "foo"}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{4.9, false, "foo", 42}, out) + assert.EqualValues(t, []any{4.9, false, "foo", 42}, out) - // a strange but valid use-cases, since we're converting to an []interface{} + // a strange but valid use-cases, since we're converting to an []any out, err = Append(42, []string{"foo"}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{"foo", 42}, out) + assert.EqualValues(t, []any{"foo", 42}, out) out, err = Append("baz", []string{"foo", "bar"}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{"foo", "bar", "baz"}, out) + assert.EqualValues(t, []any{"foo", "bar", "baz"}, out) } func TestPrepend(t *testing.T) { - out, err := Prepend(42, []interface{}{}) + out, err := Prepend(42, []any{}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{42}, out) + assert.EqualValues(t, []any{42}, out) - out, err = Prepend(42, []interface{}{4.9, false, "foo"}) + out, err = Prepend(42, []any{4.9, false, "foo"}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{42, 4.9, false, "foo"}, out) + assert.EqualValues(t, []any{42, 4.9, false, "foo"}, out) - // a strange but valid use-cases, since we're converting to an []interface{} + // a strange but valid use-cases, since we're converting to an []any out, err = Prepend(42, []string{"foo"}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{42, "foo"}, out) + assert.EqualValues(t, []any{42, "foo"}, out) out, err = Prepend("foo", []string{"bar", "baz"}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{"foo", "bar", "baz"}, out) + assert.EqualValues(t, []any{"foo", "bar", "baz"}, out) } func TestUniq(t *testing.T) { - out, err := Uniq([]interface{}{1, 2, 3, 1, true, false, true, "1", 2}) + out, err := Uniq([]any{1, 2, 3, 1, true, false, true, "1", 2}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{1, 2, 3, true, false, "1"}, out) + assert.EqualValues(t, []any{1, 2, 3, true, false, "1"}, out) out, err = Uniq([]string{"one", "two", "one", "three"}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{"one", "two", "three"}, out) + assert.EqualValues(t, []any{"one", "two", "three"}, out) } func TestReverse(t *testing.T) { - out, err := Reverse([]interface{}{}) + out, err := Reverse([]any{}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{}, out) + assert.EqualValues(t, []any{}, out) - out, err = Reverse([]interface{}{8}) + out, err = Reverse([]any{8}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{8}, out) + assert.EqualValues(t, []any{8}, out) - out, err = Reverse([]interface{}{1, 2, 3, 4}) + out, err = Reverse([]any{1, 2, 3, 4}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{4, 3, 2, 1}, out) + assert.EqualValues(t, []any{4, 3, 2, 1}, out) out, err = Reverse([]int{1, 2, 3, 4}) require.NoError(t, err) - assert.EqualValues(t, []interface{}{4, 3, 2, 1}, out) + assert.EqualValues(t, []any{4, 3, 2, 1}, out) } func TestMerge(t *testing.T) { - dst := map[string]interface{}{} - src := map[string]interface{}{} - expected := map[string]interface{}{} + dst := map[string]any{} + src := map[string]any{} + expected := map[string]any{} out, err := Merge(dst, src) require.NoError(t, err) assert.EqualValues(t, expected, out) - dst = map[string]interface{}{"a": 4, "c": 5} - src = map[string]interface{}{"a": 1, "b": 2, "c": 3} - expected = map[string]interface{}{ + dst = map[string]any{"a": 4, "c": 5} + src = map[string]any{"a": 1, "b": 2, "c": 3} + expected = map[string]any{ "a": dst["a"], "b": src["b"], "c": dst["c"], } @@ -218,10 +218,10 @@ func TestMerge(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, expected, out) - dst = map[string]interface{}{"a": 4, "c": 5} - src = map[string]interface{}{"a": 1, "b": 2, "c": 3} - src2 := map[string]interface{}{"a": 1, "b": 2, "c": 3, "d": 4} - expected = map[string]interface{}{ + dst = map[string]any{"a": 4, "c": 5} + src = map[string]any{"a": 1, "b": 2, "c": 3} + src2 := map[string]any{"a": 1, "b": 2, "c": 3, "d": 4} + expected = map[string]any{ "a": dst["a"], "b": src["b"], "c": dst["c"], "d": src2["d"], } @@ -229,10 +229,10 @@ func TestMerge(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, expected, out) - dst = map[string]interface{}{"a": false, "c": 5} - src = map[string]interface{}{"a": true, "b": 2, "c": 3} - src2 = map[string]interface{}{"a": true, "b": 2, "c": 3, "d": 4} - expected = map[string]interface{}{ + dst = map[string]any{"a": false, "c": 5} + src = map[string]any{"a": true, "b": 2, "c": 3} + src2 = map[string]any{"a": true, "b": 2, "c": 3, "d": 4} + expected = map[string]any{ "a": dst["a"], "b": src["b"], "c": dst["c"], "d": src2["d"], } @@ -240,15 +240,15 @@ func TestMerge(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, expected, out) - dst = map[string]interface{}{"a": true, "c": 5} - src = map[string]interface{}{ + dst = map[string]any{"a": true, "c": 5} + src = map[string]any{ "a": false, - "b": map[string]interface{}{ + "b": map[string]any{ "ca": "foo", }, } - src2 = map[string]interface{}{"a": false, "b": 2, "c": 3, "d": 4} - expected = map[string]interface{}{ + src2 = map[string]any{"a": false, "b": 2, "c": 3, "d": 4} + expected = map[string]any{ "a": dst["a"], "b": src["b"], "c": dst["c"], "d": src2["d"], } @@ -256,22 +256,22 @@ func TestMerge(t *testing.T) { require.NoError(t, err) assert.EqualValues(t, expected, out) - dst = map[string]interface{}{ + dst = map[string]any{ "a": true, - "b": map[string]interface{}{ + "b": map[string]any{ "ca": "foo", "cb": "bar", }, "c": 5, } - src = map[string]interface{}{ + src = map[string]any{ "a": false, - "b": map[string]interface{}{ + "b": map[string]any{ "ca": 8, }, } - expected = map[string]interface{}{ - "a": dst["a"], "b": map[string]interface{}{ + expected = map[string]any{ + "a": dst["a"], "b": map[string]any{ "ca": "foo", "cb": "bar", }, "c": dst["c"], @@ -288,19 +288,19 @@ type coords struct { func TestSameTypes(t *testing.T) { data := []struct { - in []interface{} + in []any out bool }{ - {[]interface{}{}, true}, - {[]interface{}{"a", "b"}, true}, - {[]interface{}{1.0, 3.14}, true}, - {[]interface{}{1, 3}, true}, - {[]interface{}{true, false}, true}, - {[]interface{}{1, 3.0}, false}, - {[]interface{}{"a", nil}, false}, - {[]interface{}{"a", true}, false}, - {[]interface{}{coords{2, 3}, coords{3, 4}}, true}, - {[]interface{}{coords{2, 3}, &coords{3, 4}}, false}, + {[]any{}, true}, + {[]any{"a", "b"}, true}, + {[]any{1.0, 3.14}, true}, + {[]any{1, 3}, true}, + {[]any{true, false}, true}, + {[]any{1, 3.0}, false}, + {[]any{"a", nil}, false}, + {[]any{"a", true}, false}, + {[]any{coords{2, 3}, coords{3, 4}}, true}, + {[]any{coords{2, 3}, &coords{3, 4}}, false}, } for _, d := range data { @@ -310,7 +310,7 @@ func TestSameTypes(t *testing.T) { func TestLessThan(t *testing.T) { data := []struct { - left, right interface{} + left, right any key string out bool }{ @@ -324,17 +324,17 @@ func TestLessThan(t *testing.T) { {left: uint(0xff), right: uint(0x32)}, {left: 1, right: 3, out: true}, {left: true, right: false, out: false}, - {left: map[string]interface{}{"foo": 1}, right: map[string]interface{}{"foo": 2}}, + {left: map[string]any{"foo": 1}, right: map[string]any{"foo": 2}}, { key: "foo", - left: map[string]interface{}{"foo": 1}, - right: map[string]interface{}{"foo": 2}, + left: map[string]any{"foo": 1}, + right: map[string]any{"foo": 2}, out: true, }, { key: "bar", - left: map[string]interface{}{"foo": 1}, - right: map[string]interface{}{"foo": 2}, + left: map[string]any{"foo": 1}, + right: map[string]any{"foo": 2}, }, {key: "X", left: coords{}, right: coords{-1, 2}}, {key: "Y", left: &coords{1, 1}, right: &coords{-1, 2}, out: true}, @@ -356,23 +356,23 @@ func TestSort(t *testing.T) { data := []struct { key string - in interface{} - out []interface{} + in any + out []any }{ { key: "", in: []string{"b", "c", "a", "d"}, - out: []interface{}{"a", "b", "c", "d"}, + out: []any{"a", "b", "c", "d"}, }, { key: "", - in: []interface{}{"b", "c", "a", "d"}, - out: []interface{}{"a", "b", "c", "d"}, + in: []any{"b", "c", "a", "d"}, + out: []any{"a", "b", "c", "d"}, }, { key: "", - in: []interface{}{"c", "a", "b", 3, 1, 2}, - out: []interface{}{"c", "a", "b", 3, 1, 2}, + in: []any{"c", "a", "b", 3, 1, 2}, + out: []any{"c", "a", "b", 3, 1, 2}, }, { key: "", @@ -382,41 +382,41 @@ func TestSort(t *testing.T) { { key: "", - in: []map[string]interface{}{ + in: []map[string]any{ {"name": "Bart", "age": 12}, {"age": 1, "name": "Maggie"}, {"name": "Lisa", "age": 6}, }, - out: []interface{}{ - map[string]interface{}{"name": "Bart", "age": 12}, - map[string]interface{}{"age": 1, "name": "Maggie"}, - map[string]interface{}{"name": "Lisa", "age": 6}, + out: []any{ + map[string]any{"name": "Bart", "age": 12}, + map[string]any{"age": 1, "name": "Maggie"}, + map[string]any{"name": "Lisa", "age": 6}, }, }, { key: "name", - in: []map[string]interface{}{ + in: []map[string]any{ {"name": "Bart", "age": 12}, {"age": 1, "name": "Maggie"}, {"name": "Lisa", "age": 6}, }, - out: []interface{}{ - map[string]interface{}{"name": "Bart", "age": 12}, - map[string]interface{}{"name": "Lisa", "age": 6}, - map[string]interface{}{"age": 1, "name": "Maggie"}, + out: []any{ + map[string]any{"name": "Bart", "age": 12}, + map[string]any{"name": "Lisa", "age": 6}, + map[string]any{"age": 1, "name": "Maggie"}, }, }, { key: "age", - in: []map[string]interface{}{ + in: []map[string]any{ {"name": "Bart", "age": 12}, {"age": 1, "name": "Maggie"}, {"name": "Lisa", "age": 6}, }, - out: []interface{}{ - map[string]interface{}{"age": 1, "name": "Maggie"}, - map[string]interface{}{"name": "Lisa", "age": 6}, - map[string]interface{}{"name": "Bart", "age": 12}, + out: []any{ + map[string]any{"age": 1, "name": "Maggie"}, + map[string]any{"name": "Lisa", "age": 6}, + map[string]any{"name": "Bart", "age": 12}, }, }, { @@ -426,7 +426,7 @@ func TestSort(t *testing.T) { {"x": 13, "y": -8}, {"x": 1, "y": 0}, }, - out: []interface{}{ + out: []any{ map[string]int{"x": 13, "y": -8}, map[string]int{"x": 1, "y": 0}, map[string]int{"x": 54, "y": 6}, @@ -439,7 +439,7 @@ func TestSort(t *testing.T) { {3, 3}, {1, 5}, }, - out: []interface{}{ + out: []any{ coords{1, 5}, coords{2, 4}, coords{3, 3}, @@ -452,7 +452,7 @@ func TestSort(t *testing.T) { {3, 3}, {1, 5}, }, - out: []interface{}{ + out: []any{ &coords{1, 5}, &coords{2, 4}, &coords{3, 3}, @@ -471,70 +471,70 @@ func TestSort(t *testing.T) { func TestFlatten(t *testing.T) { data := []struct { - in interface{} - expected []interface{} + in any + expected []any depth int }{ - {in: []int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {in: [3]int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {in: []interface{}{[]string{}, []int{1, 2}, 3}, expected: []interface{}{[]string{}, []int{1, 2}, 3}}, + {in: []int{1, 2, 3}, expected: []any{1, 2, 3}}, + {in: [3]int{1, 2, 3}, expected: []any{1, 2, 3}}, + {in: []any{[]string{}, []int{1, 2}, 3}, expected: []any{[]string{}, []int{1, 2}, 3}}, { - in: []interface{}{[]string{"one"}, [][]int{{1, 2}}, 3}, - expected: []interface{}{[]string{"one"}, [][]int{{1, 2}}, 3}, + in: []any{[]string{"one"}, [][]int{{1, 2}}, 3}, + expected: []any{[]string{"one"}, [][]int{{1, 2}}, 3}, }, - {depth: 1, in: []int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {depth: 1, in: [3]int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {depth: 1, in: []interface{}{[]string{}, []int{1, 2}, 3}, expected: []interface{}{1, 2, 3}}, + {depth: 1, in: []int{1, 2, 3}, expected: []any{1, 2, 3}}, + {depth: 1, in: [3]int{1, 2, 3}, expected: []any{1, 2, 3}}, + {depth: 1, in: []any{[]string{}, []int{1, 2}, 3}, expected: []any{1, 2, 3}}, { depth: 1, - in: []interface{}{[]string{"one"}, [][]int{{1, 2}}, 3}, - expected: []interface{}{"one", []int{1, 2}, 3}, + in: []any{[]string{"one"}, [][]int{{1, 2}}, 3}, + expected: []any{"one", []int{1, 2}, 3}, }, - {depth: 2, in: []int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {depth: 2, in: [3]int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {depth: 2, in: []interface{}{[]string{}, []int{1, 2}, 3}, expected: []interface{}{1, 2, 3}}, + {depth: 2, in: []int{1, 2, 3}, expected: []any{1, 2, 3}}, + {depth: 2, in: [3]int{1, 2, 3}, expected: []any{1, 2, 3}}, + {depth: 2, in: []any{[]string{}, []int{1, 2}, 3}, expected: []any{1, 2, 3}}, { depth: 2, - in: []interface{}{[]string{"one"}, [][]int{{1, 2}}, 3}, - expected: []interface{}{"one", 1, 2, 3}, + in: []any{[]string{"one"}, [][]int{{1, 2}}, 3}, + expected: []any{"one", 1, 2, 3}, }, { depth: 2, - in: []interface{}{ + in: []any{ []string{"one"}, - []interface{}{ - []interface{}{ + []any{ + []any{ []int{1}, - []interface{}{2, []int{3}}, + []any{2, []int{3}}, }, []int{4, 5}, }, 6, }, - expected: []interface{}{"one", []int{1}, []interface{}{2, []int{3}}, 4, 5, 6}, + expected: []any{"one", []int{1}, []any{2, []int{3}}, 4, 5, 6}, }, - {depth: -1, in: []int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {depth: -1, in: [3]int{1, 2, 3}, expected: []interface{}{1, 2, 3}}, - {depth: -1, in: []interface{}{[]string{}, []int{1, 2}, 3}, expected: []interface{}{1, 2, 3}}, + {depth: -1, in: []int{1, 2, 3}, expected: []any{1, 2, 3}}, + {depth: -1, in: [3]int{1, 2, 3}, expected: []any{1, 2, 3}}, + {depth: -1, in: []any{[]string{}, []int{1, 2}, 3}, expected: []any{1, 2, 3}}, { depth: -1, - in: []interface{}{[]string{"one"}, [][]int{{1, 2}}, 3}, - expected: []interface{}{"one", 1, 2, 3}, + in: []any{[]string{"one"}, [][]int{{1, 2}}, 3}, + expected: []any{"one", 1, 2, 3}, }, { depth: -1, - in: []interface{}{ + in: []any{ []string{"one"}, - []interface{}{ - []interface{}{ + []any{ + []any{ []int{1}, - []interface{}{2, []int{3}}, + []any{2, []int{3}}, }, []int{4, 5}, }, 6, }, - expected: []interface{}{"one", 1, 2, 3, 4, 5, 6}, + expected: []any{"one", 1, 2, 3, 4, 5, 6}, }, } @@ -549,17 +549,17 @@ func TestFlatten(t *testing.T) { } func BenchmarkFlatten(b *testing.B) { - data := []interface{}{ + data := []any{ []int{1, 2, 3}, [3]int{1, 2, 3}, - []interface{}{[]string{}, []int{1, 2}, 3}, - []interface{}{[]string{"one"}, [][]int{{1, 2}}, 3}, - []interface{}{ + []any{[]string{}, []int{1, 2}, 3}, + []any{[]string{"one"}, [][]int{{1, 2}}, 3}, + []any{ []string{"one"}, - []interface{}{ - []interface{}{ + []any{ + []any{ []int{1}, - []interface{}{2, []int{3}}, + []any{2, []int{3}}, }, []int{4, 5}, }, @@ -569,7 +569,7 @@ func BenchmarkFlatten(b *testing.B) { for depth := -1; depth <= 2; depth++ { for _, d := range data { b.Run(fmt.Sprintf("depth%d %T(%v)", depth, d, d), func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { Flatten(d, depth) } }) @@ -578,42 +578,42 @@ func BenchmarkFlatten(b *testing.B) { } func TestOmit(t *testing.T) { - in := map[string]interface{}{ + in := map[string]any{ "foo": "bar", "bar": true, "": "baz", } assert.EqualValues(t, in, Omit(in, "baz")) - expected := map[string]interface{}{ + expected := map[string]any{ "foo": "bar", "bar": true, } assert.EqualValues(t, expected, Omit(in, "")) - expected = map[string]interface{}{ + expected = map[string]any{ "": "baz", } assert.EqualValues(t, expected, Omit(in, "foo", "bar")) - assert.EqualValues(t, map[string]interface{}{}, Omit(in, "foo", "bar", "")) + assert.EqualValues(t, map[string]any{}, Omit(in, "foo", "bar", "")) } func TestPick(t *testing.T) { - in := map[string]interface{}{ + in := map[string]any{ "foo": "bar", "bar": true, "": "baz", } - expected := map[string]interface{}{} + expected := map[string]any{} assert.EqualValues(t, expected, Pick(in, "baz")) - expected = map[string]interface{}{ + expected = map[string]any{ "": "baz", } assert.EqualValues(t, expected, Pick(in, "")) - expected = map[string]interface{}{ + expected = map[string]any{ "foo": "bar", "bar": true, } diff --git a/coll/index.go b/coll/index.go index ba815320..46402a51 100644 --- a/coll/index.go +++ b/coll/index.go @@ -14,7 +14,7 @@ import ( // will return an error if the key is not found. Note that the argument order is // different from the template function definition found in `funcs/coll.go` to // allow for variadic indexes. -func Index(v interface{}, keys ...interface{}) (interface{}, error) { +func Index(v any, keys ...any) (any, error) { item := reflect.ValueOf(v) item = indirectInterface(item) if !item.IsValid() { diff --git a/coll/index_test.go b/coll/index_test.go index 521e8cef..88993e93 100644 --- a/coll/index_test.go +++ b/coll/index_test.go @@ -8,28 +8,28 @@ import ( ) func TestIndex(t *testing.T) { - out, err := Index(map[string]interface{}{ + out, err := Index(map[string]any{ "foo": "bar", "baz": "qux", }, "foo") require.NoError(t, err) assert.Equal(t, "bar", out) - out, err = Index(map[string]interface{}{ + out, err = Index(map[string]any{ "foo": "bar", "baz": "qux", "quux": "corge", }, "foo", 2) require.NoError(t, err) assert.Equal(t, byte('r'), out) - out, err = Index([]interface{}{"foo", "bar", "baz"}, 2) + out, err = Index([]any{"foo", "bar", "baz"}, 2) require.NoError(t, err) assert.Equal(t, "baz", out) - out, err = Index([]interface{}{"foo", "bar", "baz"}, 2, 2) + out, err = Index([]any{"foo", "bar", "baz"}, 2, 2) require.NoError(t, err) assert.Equal(t, byte('z'), out) // error cases - out, err = Index([]interface{}{"foo", "bar", "baz"}, 0, 1, 2) + out, err = Index([]any{"foo", "bar", "baz"}, 0, 1, 2) require.Error(t, err) assert.Nil(t, out) @@ -41,7 +41,7 @@ func TestIndex(t *testing.T) { require.Error(t, err) assert.Nil(t, out) - out, err = Index(map[interface{}]string{nil: "foo", 2: "bar"}, "baz") + out, err = Index(map[any]string{nil: "foo", 2: "bar"}, "baz") require.Error(t, err) assert.Nil(t, out) diff --git a/coll/jq.go b/coll/jq.go index 16dc8f0e..fa7777ff 100644 --- a/coll/jq.go +++ b/coll/jq.go @@ -10,7 +10,7 @@ import ( ) // JQ - -func JQ(ctx context.Context, jqExpr string, in interface{}) (interface{}, error) { +func JQ(ctx context.Context, jqExpr string, in any) (any, error) { query, err := gojq.Parse(jqExpr) if err != nil { return nil, fmt.Errorf("jq parsing expression %q: %w", jqExpr, err) @@ -23,8 +23,8 @@ func JQ(ctx context.Context, jqExpr string, in interface{}) (interface{}, error) } iter := query.RunWithContext(ctx, in) - var out interface{} - a := []interface{}{} + var out any + a := []any{} for { v, ok := iter.Next() if !ok { @@ -44,12 +44,12 @@ func JQ(ctx context.Context, jqExpr string, in interface{}) (interface{}, error) return out, nil } -// jqConvertType converts the input to a map[string]interface{}, []interface{}, +// jqConvertType converts the input to a map[string]any, []any, // or other supported primitive JSON types. -func jqConvertType(in interface{}) (interface{}, error) { +func jqConvertType(in any) (any, error) { // if it's already a supported type, pass it through switch in.(type) { - case map[string]interface{}, []interface{}, + case map[string]any, []any, string, []byte, nil, bool, int, int8, int16, int32, int64, @@ -67,8 +67,8 @@ func jqConvertType(in interface{}) (interface{}, error) { value = value.Elem() } - mapType := reflect.TypeOf(map[string]interface{}{}) - sliceType := reflect.TypeOf([]interface{}{}) + mapType := reflect.TypeOf(map[string]any{}) + sliceType := reflect.TypeOf([]any{}) // if it can be converted to a map or slice, do that if inType.ConvertibleTo(mapType) { return value.Convert(mapType).Interface(), nil @@ -83,7 +83,7 @@ func jqConvertType(in interface{}) (interface{}, error) { if err != nil { return nil, fmt.Errorf("json marshal struct: %w", err) } - var m map[string]interface{} + var m map[string]any err = json.Unmarshal(b, &m) if err != nil { return nil, fmt.Errorf("json unmarshal struct: %w", err) diff --git a/coll/jq_test.go b/coll/jq_test.go index e4af3847..0d45fdfb 100644 --- a/coll/jq_test.go +++ b/coll/jq_test.go @@ -11,29 +11,29 @@ import ( func TestJQ(t *testing.T) { ctx := context.Background() - in := map[string]interface{}{ - "store": map[string]interface{}{ - "book": []interface{}{ - map[string]interface{}{ + in := map[string]any{ + "store": map[string]any{ + "book": []any{ + map[string]any{ "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95, }, - map[string]interface{}{ + map[string]any{ "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99, }, - map[string]interface{}{ + map[string]any{ "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99, }, - map[string]interface{}{ + map[string]any{ "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", @@ -41,7 +41,7 @@ func TestJQ(t *testing.T) { "price": 22.99, }, }, - "bicycle": map[string]interface{}{ + "bicycle": map[string]any{ "color": "red", "price": 19.95, }, @@ -76,14 +76,14 @@ func TestJQ(t *testing.T) { out, err = JQ(ctx, ".store.book[]|select(.price < 10.0 )", in) require.NoError(t, err) - expected := []interface{}{ - map[string]interface{}{ + expected := []any{ + map[string]any{ "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95, }, - map[string]interface{}{ + map[string]any{ "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", @@ -93,20 +93,20 @@ func TestJQ(t *testing.T) { } assert.EqualValues(t, expected, out) - in = map[string]interface{}{ - "a": map[string]interface{}{ - "aa": map[string]interface{}{ - "foo": map[string]interface{}{ - "aaa": map[string]interface{}{ - "aaaa": map[string]interface{}{ + in = map[string]any{ + "a": map[string]any{ + "aa": map[string]any{ + "foo": map[string]any{ + "aaa": map[string]any{ + "aaaa": map[string]any{ "bar": 1234, }, }, }, }, - "ab": map[string]interface{}{ - "aba": map[string]interface{}{ - "foo": map[string]interface{}{ + "ab": map[string]any{ + "aba": map[string]any{ + "foo": map[string]any{ "abaa": true, "abab": "baz", }, @@ -117,7 +117,7 @@ func TestJQ(t *testing.T) { out, err = JQ(ctx, `tostream|select((.[0]|index("foo")) and (.[0][-1]!="foo") and (.[1])) as $s|($s[0]|index("foo")+1) as $ind|($ind|truncate_stream($s)) as $newstream|$newstream|reduce . as [$p,$v] ({};setpath($p;$v))|add`, in) require.NoError(t, err) assert.Len(t, out, 3) - assert.Contains(t, out, map[string]interface{}{"aaaa": map[string]interface{}{"bar": 1234}}) + assert.Contains(t, out, map[string]any{"aaaa": map[string]any{"bar": 1234}}) assert.Contains(t, out, true) assert.Contains(t, out, "baz") } @@ -130,7 +130,7 @@ func TestJQ_typeConversions(t *testing.T) { } type storeType struct { Bicycle *bicycleType - safe interface{} + safe any } structIn := &storeType{ @@ -151,9 +151,9 @@ func TestJQ_typeConversions(t *testing.T) { _, err = JQ(ctx, ".*", structIn) require.Error(t, err) - // a type with an underlying type of map[string]interface{}, just like + // a type with an underlying type of map[string]any, just like // gomplate.tmplctx - type mapType map[string]interface{} + type mapType map[string]any out, err = JQ(ctx, ".foo", mapType{"foo": "bar"}) require.NoError(t, err) @@ -165,7 +165,7 @@ func TestJQ_typeConversions(t *testing.T) { assert.Equal(t, "bar", out) // underlying slice type - type sliceType []interface{} + type sliceType []any out, err = JQ(ctx, ".[1]", sliceType{"foo", "bar"}) require.NoError(t, err) @@ -216,9 +216,9 @@ func TestJQConvertType_passthroughTypes(t *testing.T) { _, err := jqConvertType(v) require.Error(t, err) - testdata := []interface{}{ - map[string]interface{}{"foo": 1234}, - []interface{}{"foo", "bar", "baz", 1, 2, 3}, + testdata := []any{ + map[string]any{"foo": 1234}, + []any{"foo", "bar", "baz", 1, 2, 3}, "foo", []byte("foo"), json.RawMessage(`{"foo": "bar"}`), diff --git a/coll/jsonpath.go b/coll/jsonpath.go index 78ec5689..210280b6 100644 --- a/coll/jsonpath.go +++ b/coll/jsonpath.go @@ -8,7 +8,7 @@ import ( ) // JSONPath - -func JSONPath(p string, in interface{}) (interface{}, error) { +func JSONPath(p string, in any) (any, error) { jp, err := parsePath(p) if err != nil { return nil, fmt.Errorf("couldn't parse JSONPath %s: %w", p, err) @@ -18,7 +18,7 @@ func JSONPath(p string, in interface{}) (interface{}, error) { return nil, fmt.Errorf("executing JSONPath failed: %w", err) } - var out interface{} + var out any if len(results) == 1 && len(results[0]) == 1 { v := results[0][0] out, err = extractResult(v) @@ -26,7 +26,7 @@ func JSONPath(p string, in interface{}) (interface{}, error) { return nil, err } } else { - a := []interface{}{} + a := []any{} for _, r := range results { for _, v := range r { o, err := extractResult(v) @@ -54,7 +54,7 @@ func parsePath(p string) (*jsonpath.JSONPath, error) { return jp, nil } -func extractResult(v reflect.Value) (interface{}, error) { +func extractResult(v reflect.Value) (any, error) { if v.CanInterface() { return v.Interface(), nil } diff --git a/coll/jsonpath_test.go b/coll/jsonpath_test.go index a8bf02de..147fc2ea 100644 --- a/coll/jsonpath_test.go +++ b/coll/jsonpath_test.go @@ -8,8 +8,8 @@ import ( ) type ( - m = map[string]interface{} - ar = []interface{} + m = map[string]any + ar = []any ) func TestJSONPath(t *testing.T) { @@ -127,7 +127,7 @@ func TestJSONPath(t *testing.T) { } type storeType struct { Bicycle *bicycleType - safe interface{} + safe any } structIn := &storeType{ -- cgit v1.2.3