From d2b3b987d9643f8891e172606c86aafdf4811a43 Mon Sep 17 00:00:00 2001 From: Bill Gale Date: Wed, 3 Jul 2024 13:58:29 +0100 Subject: [PATCH] feat: add DerefOrEmpty func to the client --- next/deref.go | 22 ++++++ next/deref_test.go | 167 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 next/deref.go create mode 100644 next/deref_test.go diff --git a/next/deref.go b/next/deref.go new file mode 100644 index 0000000..b948f43 --- /dev/null +++ b/next/deref.go @@ -0,0 +1,22 @@ +package next + +import "reflect" + +// DerefOrEmpty returns the dereferenced value of the input pointer, or the zero +// value of the type if the input is nil. +func DerefOrEmpty[T any](in *T) T { + if in == nil { + tType := reflect.TypeOf(*new(T)) + switch tType.Kind() { + case reflect.Slice, reflect.Array: + return reflect.MakeSlice(tType, 0, 0).Interface().(T) + case reflect.Map: + return reflect.MakeMap(tType).Interface().(T) + default: + var empty T + return empty + } + } + + return *in +} diff --git a/next/deref_test.go b/next/deref_test.go new file mode 100644 index 0000000..a3fa2c5 --- /dev/null +++ b/next/deref_test.go @@ -0,0 +1,167 @@ +package next_test + +import ( + "testing" + + "github.com/krystal/go-katapult/next" + "github.com/stretchr/testify/require" +) + +func ptr[T any](v T) *T { + return &v +} + +func TestDerefOrEmpty_string(t *testing.T) { + tests := []struct { + name string + in *string + want string + }{ + { + name: "Nil input", + in: nil, + want: "", + }, + { + name: "Non-nil input", + in: ptr("hello"), + want: "hello", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, next.DerefOrEmpty(tt.in)) + }) + } +} + +func TestDerefOrEmpty_int(t *testing.T) { + tests := []struct { + name string + in *int + want int + }{ + { + name: "Nil input", + in: nil, + want: 0, + }, + { + name: "Non-nil input", + in: ptr(22), + want: 22, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, next.DerefOrEmpty(tt.in)) + }) + } +} + +func TestDerefOrEmpty_float64(t *testing.T) { + tests := []struct { + name string + in *float64 + want float64 + }{ + { + name: "Nil input", + in: nil, + want: 0.0, + }, + { + name: "Non-nil input", + in: ptr(3.14), + want: 3.14, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, next.DerefOrEmpty(tt.in)) + }) + } +} + +func TestDerefOrEmpty_slice(t *testing.T) { + tests := []struct { + name string + in *[]string + want []string + }{ + { + name: "Nil input", + in: nil, + want: []string{}, + }, + { + name: "Non-nil input", + in: ptr([]string{"hello", "world"}), + want: []string{"hello", "world"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, next.DerefOrEmpty(tt.in)) + }) + } +} + +func TestDerefOrEmpty_map(t *testing.T) { + tests := []struct { + name string + in *map[string]int + want map[string]int + }{ + { + name: "Nil input", + in: nil, + want: map[string]int{}, + }, + { + name: "Non-nil input", + in: ptr(map[string]int{"hello": 32}), + want: map[string]int{"hello": 32}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, next.DerefOrEmpty(tt.in)) + }) + } +} + +func TestDerefOrEmpty_struct(t *testing.T) { + type testStruct struct { + Name string + Age int + } + + tests := []struct { + name string + in *testStruct + want testStruct + }{ + { + name: "Nil input", + in: nil, + want: testStruct{}, + }, + { + name: "Non-nil input", + in: ptr(testStruct{Name: "Alice", Age: 22}), + want: testStruct{Name: "Alice", Age: 22}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, next.DerefOrEmpty(tt.in)) + }) + } +}