-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
<!-- !!!! README !!!! Please fill this out. Please follow the PR naming conventions: https://outreach-io.atlassian.net/wiki/spaces/EN/pages/1902444645/Conventional+Commits --> <!-- A short description of what your PR does and what it solves. --> ## What this PR does / why we need it This PR aims to add a pointer package to be used across our services and librairies. I had to do pointer conversion in API-Proxy [here](https://github.com/getoutreach/apiproxy/blob/f3707830d6d25f94057182027037c709d7a14c4d/internal/apiv2/openapi.go#L33) and saw `ptr.SomeType()` in several places (not using generic). As we are using gql federation in our services we will surely need more pointer conversion and I would like to have the possibility to use generics. - Added tests with different types including some that are not supported by [github.com/aws/smithy-go/blob/main/ptr](https://github.com/aws/smithy-go/blob/main/ptr) <!-- <<Stencil::Block(jiraPrefix)>> --> ## Jira ID No Jira, just an idea of package that would be nice to have <!-- <</Stencil::Block>> --> <!-- Notes that may be helpful for anyone reviewing this PR --> ## Notes for your reviewers <!-- <<Stencil::Block(custom)>> --> <!-- <</Stencil::Block>> -->
- Loading branch information
1 parent
7280625
commit c8b3c7e
Showing
2 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright 2023 Outreach Corporation. All Rights Reserved. | ||
|
||
// Description: Implements the pointer package. | ||
|
||
// Package pointer is an attempt to provide functions to convert data to pointers using generics. | ||
// inspired by https://pkg.go.dev/github.com/aws/smithy-go/ptr | ||
// This is intended to replace the following patterns that we've seen across our codebase: | ||
// - myVar := "value"; return &myVar | ||
// - ptr.{SomeType}("value") | ||
// Also Enables the possibility to use: var res *myStructType = ToPtr(myStructType{}) and any derivate | ||
// with map / slice | ||
package pointer | ||
|
||
// ToPtr returns a pointer to {object} of type *T | ||
func ToPtr[T any](object T) *T { | ||
return &object | ||
} | ||
|
||
// ToSlicePtr returns a slice of pointers of type *T pointing to each element of {objects} | ||
func ToSlicePtr[T any](objects []T) []*T { | ||
ptrObj := make([]*T, 0, len(objects)) | ||
for _, o := range objects { | ||
ptrObj = append(ptrObj, ToPtr(o)) | ||
} | ||
return ptrObj | ||
} | ||
|
||
// ToMapPtr returns a map of pointers of type *T pointing to each element of {objectMap} | ||
func ToMapPtr[K comparable, T any](objectMap map[K]T) map[K]*T { | ||
ptrObj := make(map[K]*T, len(objectMap)) | ||
for k, o := range objectMap { | ||
ptrObj[k] = ToPtr(o) | ||
} | ||
return ptrObj | ||
} | ||
|
||
// ToValue returns the value of type T pointed by {ptr} | ||
// if {ptr} is nil, return 0 value of T | ||
func ToValue[T any](ptr *T) (res T) { | ||
if ptr == nil { | ||
return res | ||
} | ||
return *ptr | ||
} | ||
|
||
// ToSliceValue returns a slice of values of type T pointed by each pointers in {ptrs} | ||
// if a pointer in {ptrs} is nil the value at the corresponding index will be the 0 of T | ||
func ToSliceValue[T any](ptrs []*T) []T { | ||
res := make([]T, 0, len(ptrs)) | ||
for _, ptr := range ptrs { | ||
res = append(res, ToValue(ptr)) | ||
} | ||
return res | ||
} | ||
|
||
// ToSliceValue returns a map of values of type T pointed by each pointers in {ptrs} | ||
// if a pointer in {ptrs} is nil the value at the corresponding key will be the 0 of T | ||
func ToMapValue[K comparable, T any](ptrs map[K]*T) map[K]T { | ||
res := make(map[K]T, len(ptrs)) | ||
for v, ptr := range ptrs { | ||
res[v] = ToValue(ptr) | ||
} | ||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
package pointer | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
type customStruct struct { | ||
name string | ||
} | ||
|
||
func TestPtrStruct(t *testing.T) { | ||
oPtr := ToPtr(customStruct{name: "value"}) | ||
if oPtr == nil { | ||
t.Error("expected a non-nil pointer") | ||
} | ||
s := *oPtr | ||
if s.name != "value" { | ||
t.Errorf("expected %s, but received %s", "value", s.name) | ||
} | ||
oValue := ToValue(oPtr) | ||
if oValue.name != "value" { | ||
t.Errorf("expected %s, but received %s", "value", oValue.name) | ||
} | ||
} | ||
|
||
func TestSlicePtrStruct(t *testing.T) { | ||
value := []customStruct{{name: "one"}, {name: "two"}} | ||
oSlice := ToSlicePtr(value) | ||
if len(oSlice) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oSlice)) | ||
} | ||
for i, v := range value { | ||
if oSlice[i] == nil { | ||
t.Error("unexpected nil value in slice") | ||
} | ||
s := *oSlice[i] | ||
if s.name != v.name { | ||
t.Errorf("expected value of size %s, but got %s", v.name, s.name) | ||
} | ||
} | ||
oSliceValue := ToSliceValue(oSlice) | ||
if len(oSliceValue) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oSliceValue)) | ||
} | ||
for i, v := range value { | ||
if oSliceValue[i].name != v.name { | ||
t.Errorf("expected value of size %s, but got %s", v.name, oSliceValue[i].name) | ||
} | ||
} | ||
} | ||
|
||
func TestMapPtrStruct(t *testing.T) { | ||
value := map[string]customStruct{ | ||
"1": {name: "One"}, | ||
"2": {name: "Two"}, | ||
"10": {name: "Three??"}, | ||
} | ||
oMap := ToMapPtr(value) | ||
if len(oMap) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oMap)) | ||
} | ||
for i, v := range value { | ||
if oMap[i] == nil { | ||
t.Error("unexpected nil value in slice") | ||
} | ||
s := *oMap[i] | ||
if s.name != v.name { | ||
t.Errorf("expected value of size %s, but got %s", v.name, s.name) | ||
} | ||
} | ||
oMapValue := ToMapValue(oMap) | ||
if len(oMapValue) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oMapValue)) | ||
} | ||
for i, v := range value { | ||
if oMapValue[i].name != v.name { | ||
t.Errorf("expected value of size %s, but got %s", v.name, oMapValue[i].name) | ||
} | ||
} | ||
} | ||
|
||
func TestPtrString(t *testing.T) { | ||
oPtr := ToPtr("value") | ||
if oPtr == nil { | ||
t.Error("expected a non-nil pointer") | ||
} | ||
if *oPtr != "value" { | ||
t.Errorf("expected %s, but received %s", "value", *oPtr) | ||
} | ||
oValue := ToValue(oPtr) | ||
if oValue != "value" { | ||
t.Errorf("expected %s, but received %s", "value", oValue) | ||
} | ||
} | ||
|
||
func TestSlicePtrString(t *testing.T) { | ||
value := []string{"a", "b", "c"} | ||
oSlice := ToSlicePtr(value) | ||
if len(oSlice) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oSlice)) | ||
} | ||
for i, v := range value { | ||
if oSlice[i] == nil { | ||
t.Error("unexpected nil value in slice") | ||
} | ||
if *oSlice[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, *oSlice[i]) | ||
} | ||
} | ||
oSliceValue := ToSliceValue(oSlice) | ||
if len(oSliceValue) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oSliceValue)) | ||
} | ||
for i, v := range value { | ||
if oSliceValue[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, oSliceValue[i]) | ||
} | ||
} | ||
} | ||
|
||
func TestMapPtrString(t *testing.T) { | ||
value := map[int]string{ | ||
1: "a", | ||
2: "b", | ||
10: "c", | ||
} | ||
oMap := ToMapPtr(value) | ||
if len(oMap) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oMap)) | ||
} | ||
for i, v := range value { | ||
if oMap[i] == nil { | ||
t.Error("unexpected nil value in slice") | ||
} | ||
if *oMap[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, *oMap[i]) | ||
} | ||
} | ||
oMapValue := ToMapValue(oMap) | ||
if len(oMapValue) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oMapValue)) | ||
} | ||
for i, v := range value { | ||
if oMapValue[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, oMapValue[i]) | ||
} | ||
} | ||
} | ||
|
||
func TestPtrInterface(t *testing.T) { | ||
var value interface{} = "some interface" | ||
oPtr := ToPtr(value) | ||
if oPtr == nil { | ||
t.Error("expected a non-nil pointer") | ||
} | ||
if *oPtr != value { | ||
t.Errorf("expected %s, but received %s", "value", *oPtr) | ||
} | ||
oValue := ToValue(oPtr) | ||
if oValue != value { | ||
t.Errorf("expected %s, but received %s", "value", oValue) | ||
} | ||
} | ||
|
||
func TestSlicePtrInterface(t *testing.T) { | ||
value := []interface{}{"a", 2, true} | ||
oSlice := ToSlicePtr(value) | ||
if len(oSlice) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oSlice)) | ||
} | ||
for i, v := range value { | ||
if oSlice[i] == nil { | ||
t.Error("unexpected nil value in slice") | ||
} | ||
if *oSlice[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, *oSlice[i]) | ||
} | ||
} | ||
oSliceValue := ToSliceValue(oSlice) | ||
if len(oSliceValue) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oSliceValue)) | ||
} | ||
for i, v := range value { | ||
if oSliceValue[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, oSliceValue[i]) | ||
} | ||
} | ||
} | ||
|
||
func TestMapPtrInterface(t *testing.T) { | ||
value := map[int]interface{}{ | ||
1: "a", | ||
2: 50, | ||
10: true, | ||
} | ||
oMap := ToMapPtr(value) | ||
if len(oMap) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oMap)) | ||
} | ||
for i, v := range value { | ||
if oMap[i] == nil { | ||
t.Error("unexpected nil value in slice") | ||
} | ||
if *oMap[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, *oMap[i]) | ||
} | ||
} | ||
oMapValue := ToMapValue(oMap) | ||
if len(oMapValue) != len(value) { | ||
t.Errorf("expected slice of size %d, but got %d", len(value), len(oMapValue)) | ||
} | ||
for i, v := range value { | ||
if oMapValue[i] != v { | ||
t.Errorf("expected value of size %s, but got %s", v, oMapValue[i]) | ||
} | ||
} | ||
} | ||
|
||
func TestPtrFunc(t *testing.T) { | ||
f := func() string { | ||
return "value" | ||
} | ||
oPtr := ToPtr(f) | ||
if oPtr == nil { | ||
t.Error("expected a non-nil pointer") | ||
} | ||
s := *oPtr | ||
if s() != "value" { | ||
t.Errorf("expected %s, but received %s", "value", s()) | ||
} | ||
oValue := ToValue(oPtr) | ||
if oValue() != "value" { | ||
t.Errorf("expected %s, but received %s", "value", oValue()) | ||
} | ||
} | ||
|
||
func TestPtrOfPtr(t *testing.T) { | ||
value := ToPtr("value") | ||
oPtr := ToPtr(value) | ||
if oPtr == nil { | ||
t.Error("expected a non-nil pointer") | ||
} | ||
if *oPtr != value { | ||
t.Errorf("expected %p, but received %p", *oPtr, value) | ||
} | ||
oValue := ToValue(oPtr) | ||
if oValue != value { | ||
t.Errorf("expected %p, but received %p", oValue, value) | ||
} | ||
} |