diff --git a/go.mod b/go.mod index e9a8d24084f..571c0fa69d0 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,7 @@ require ( github.com/ryanuber/go-glob v1.0.0 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 github.com/shirou/gopsutil/v3 v3.21.12 - github.com/shoenig/test v0.2.5 + github.com/shoenig/test v0.2.6 github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c github.com/stretchr/testify v1.7.1 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 @@ -118,6 +118,7 @@ require ( go.etcd.io/bbolt v1.3.5 go.uber.org/goleak v1.1.12 golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 + golang.org/x/exp v0.0.0-20220609121020-a51bd0440498 golang.org/x/net v0.0.0-20220225172249-27dd8689420f golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e @@ -264,7 +265,6 @@ require ( golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.10 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/api v0.60.0 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index 24118b96f60..d2eab222ec4 100644 --- a/go.sum +++ b/go.sum @@ -1166,8 +1166,8 @@ github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4 github.com/shirou/gopsutil/v3 v3.21.12 h1:VoGxEW2hpmz0Vt3wUvHIl9fquzYLNpVpgNNB7pGJimA= github.com/shirou/gopsutil/v3 v3.21.12/go.mod h1:BToYZVTlSVlfazpDDYFnsVZLaoRG+g8ufT6fPQLdJzA= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shoenig/test v0.2.5 h1:CfxxPAhW9sJt9nVI39cOHrb4krmHd28SmU66oCXi6sY= -github.com/shoenig/test v0.2.5/go.mod h1:xYtyGBC5Q3kzCNyJg/SjgNpfAa2kvmgA0i5+lQso8x0= +github.com/shoenig/test v0.2.6 h1:G7QP1jygTmhhNc0TKZ5O87CvB919YjL8EXnsD1aiaHo= +github.com/shoenig/test v0.2.6/go.mod h1:xYtyGBC5Q3kzCNyJg/SjgNpfAa2kvmgA0i5+lQso8x0= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= @@ -1341,6 +1341,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220609121020-a51bd0440498 h1:TF0FvLUGEq/8wOt/9AV1nj6D4ViZGUIGCMQfCv7VRXY= +golang.org/x/exp v0.0.0-20220609121020-a51bd0440498/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1660,7 +1662,6 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/helper/funcs.go b/helper/funcs.go index a6512d7eab9..665017222f2 100644 --- a/helper/funcs.go +++ b/helper/funcs.go @@ -11,6 +11,7 @@ import ( multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/hcl/hcl/ast" + "golang.org/x/exp/constraints" ) // validUUID is used to check if a given string looks like a UUID @@ -69,41 +70,57 @@ func HashUUID(input string) (output string, hashed bool) { } // BoolToPtr returns the pointer to a boolean. +// +// Deprecated; use pointer.Of instead. func BoolToPtr(b bool) *bool { return &b } -// IntToPtr returns the pointer to an int +// IntToPtr returns the pointer to an int. +// +// Deprecated; use pointer.Of instead. func IntToPtr(i int) *int { return &i } -// Int8ToPtr returns the pointer to an int8 +// Int8ToPtr returns the pointer to an int8. +// +// Deprecated; use pointer.Of instead. func Int8ToPtr(i int8) *int8 { return &i } -// Int32ToPtr returns the pointer to an int32 +// Int32ToPtr returns the pointer to an int32. +// +// Deprecated; use pointer.Of instead. func Int32ToPtr(i int32) *int32 { return &i } -// Int64ToPtr returns the pointer to an int64 +// Int64ToPtr returns the pointer to an int64. +// +// Deprecated; use pointer.Of instead. func Int64ToPtr(i int64) *int64 { return &i } -// Uint64ToPtr returns the pointer to an uint64 +// Uint64ToPtr returns the pointer to an uint64. +// +// Deprecated; use pointer.Of instead. func Uint64ToPtr(u uint64) *uint64 { return &u } -// UintToPtr returns the pointer to an uint +// UintToPtr returns the pointer to an uint. +// +// Deprecated; use pointer.Of instead. func UintToPtr(u uint) *uint { return &u } -// StringToPtr returns the pointer to a string +// StringToPtr returns the pointer to a string. +// +// Deprecated; use pointer.Of instead. func StringToPtr(str string) *string { return &str } @@ -121,11 +138,32 @@ func CompareTimePtrs(a, b *time.Duration) bool { return *a == *b } -// Float64ToPtr returns the pointer to an float64 +// Float64ToPtr returns the pointer to an float64. +// +// Deprecated; use pointer.Of instead. func Float64ToPtr(f float64) *float64 { return &f } +// Min returns the minimum of a and b. +func Min[T constraints.Ordered](a, b T) T { + if a < b { + return a + } + return b +} + +// Max returns the maximum of a and b. +func Max[T constraints.Ordered](a, b T) T { + if a > b { + return a + } + return b +} + +// IntMin returns the minimum of a and b. +// +// Deprecated; use Min instead. func IntMin(a, b int) int { if a < b { return a @@ -133,6 +171,9 @@ func IntMin(a, b int) int { return b } +// IntMax returns the maximum of a and b. +// +// Deprecated; use Max instead. func IntMax(a, b int) int { if a > b { return a @@ -140,6 +181,9 @@ func IntMax(a, b int) int { return b } +// Uint64Max returns the maximum of a and b. +// +// Deprecated; use Max instead. func Uint64Max(a, b uint64) uint64 { if a > b { return a @@ -311,8 +355,24 @@ func CompareMapStringString(a, b map[string]string) bool { return true } -// Below is helpers for copying generic structures. +// CopyMap creates a copy of m. Struct values are not deep copies. +// +// If m is nil or contains no elements, the return value is nil. +func CopyMap[M ~map[K]V, K comparable, V any](m M) M { + if len(m) == 0 { + return nil + } + result := make(M, len(m)) + for k, v := range m { + result[k] = v + } + return result +} + +// CopyMapStringString creates a copy of m. +// +// Deprecated; use CopyMap instead. func CopyMapStringString(m map[string]string) map[string]string { l := len(m) if l == 0 { @@ -326,6 +386,9 @@ func CopyMapStringString(m map[string]string) map[string]string { return c } +// CopyMapStringStruct creates a copy of m. +// +// Deprecated; use CopyMap instead. func CopyMapStringStruct(m map[string]struct{}) map[string]struct{} { l := len(m) if l == 0 { @@ -339,6 +402,9 @@ func CopyMapStringStruct(m map[string]struct{}) map[string]struct{} { return c } +// CopyMapStringInterface creates a copy of m. +// +// Deprecated; use CopyMap instead. func CopyMapStringInterface(m map[string]interface{}) map[string]interface{} { l := len(m) if l == 0 { @@ -375,6 +441,9 @@ func MergeMapStringString(m map[string]string, n map[string]string) map[string]s return result } +// CopyMapStringInt creates a copy of m. +// +// Deprecated; use CopyMap instead. func CopyMapStringInt(m map[string]int) map[string]int { l := len(m) if l == 0 { @@ -388,6 +457,9 @@ func CopyMapStringInt(m map[string]int) map[string]int { return c } +// CopyMapStringFloat64 creates a copy of m. +// +// Deprecated; use CopyMap instead. func CopyMapStringFloat64(m map[string]float64) map[string]float64 { l := len(m) if l == 0 { @@ -401,6 +473,9 @@ func CopyMapStringFloat64(m map[string]float64) map[string]float64 { return c } +// CopyMapStringSliceString creates a copy of m. +// +// todo: a deep value copy version of CopyMap. func CopyMapStringSliceString(m map[string][]string) map[string][]string { l := len(m) if l == 0 { @@ -414,6 +489,9 @@ func CopyMapStringSliceString(m map[string][]string) map[string][]string { return c } +// CopySliceString creates a copy of s. +// +// Deprecated; use slices.Clone instead. func CopySliceString(s []string) []string { l := len(s) if l == 0 { @@ -425,6 +503,9 @@ func CopySliceString(s []string) []string { return c } +// CopySliceInt creates a copy of s. +// +// Deprecated; use slices.Clone instead. func CopySliceInt(s []int) []int { l := len(s) if l == 0 { diff --git a/helper/funcs_test.go b/helper/funcs_test.go index 0b1598648cf..68543021029 100644 --- a/helper/funcs_test.go +++ b/helper/funcs_test.go @@ -8,9 +8,78 @@ import ( "testing" "time" + "github.com/shoenig/test/must" "github.com/stretchr/testify/require" ) +func Test_Min(t *testing.T) { + t.Run("int", func(t *testing.T) { + a := 1 + b := 2 + must.Eq(t, 1, Min(a, b)) + must.Eq(t, 1, Min(b, a)) + }) + + t.Run("float64", func(t *testing.T) { + a := 1.1 + b := 2.2 + must.Eq(t, 1.1, Min(a, b)) + must.Eq(t, 1.1, Min(b, a)) + }) + + t.Run("string", func(t *testing.T) { + a := "cat" + b := "dog" + must.Eq(t, "cat", Min(a, b)) + must.Eq(t, "cat", Min(b, a)) + }) +} + +func Test_Max(t *testing.T) { + t.Run("int", func(t *testing.T) { + a := 1 + b := 2 + must.Eq(t, 2, Max(a, b)) + must.Eq(t, 2, Max(b, a)) + }) + + t.Run("float64", func(t *testing.T) { + a := 1.1 + b := 2.2 + must.Eq(t, 2.2, Max(a, b)) + must.Eq(t, 2.2, Max(b, a)) + }) + + t.Run("string", func(t *testing.T) { + a := "cat" + b := "dog" + must.Eq(t, "dog", Max(a, b)) + must.Eq(t, "dog", Max(b, a)) + }) +} + +func Test_CopyMap(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var m map[string]int + result := CopyMap(m) + must.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + m := make(map[string]int, 10) + result := CopyMap(m) + must.Nil(t, result) + }) + + t.Run("elements", func(t *testing.T) { + m := map[string]int{"a": 1, "b": 2} + result := CopyMap(m) + result["a"] = -1 + must.MapEq(t, map[string]int{"a": -1, "b": 2}, result) + must.MapEq(t, map[string]int{"a": 1, "b": 2}, m) // not modified + }) +} + func TestSliceStringIsSubset(t *testing.T) { l := []string{"a", "b", "c"} s := []string{"d"} diff --git a/helper/pointer/pointer.go b/helper/pointer/pointer.go new file mode 100644 index 00000000000..766b4ba60a9 --- /dev/null +++ b/helper/pointer/pointer.go @@ -0,0 +1,7 @@ +// Package pointer provides helper functions related to Go pointers. +package pointer + +// Of returns a pointer to a. +func Of[A any](a A) *A { + return &a +} diff --git a/helper/pointer/pointer_test.go b/helper/pointer/pointer_test.go new file mode 100644 index 00000000000..e656cce591a --- /dev/null +++ b/helper/pointer/pointer_test.go @@ -0,0 +1,18 @@ +package pointer + +import ( + "testing" + + "github.com/shoenig/test/must" +) + +func Test_Of(t *testing.T) { + s := "hello" + sPtr := Of(s) + + must.Eq(t, s, *sPtr) + + b := "bye" + sPtr = &b + must.NotEq(t, s, *sPtr) +}