From 4b6721785a60e1c57762903f921bfaaab547b8b4 Mon Sep 17 00:00:00 2001 From: Dylan Bourque Date: Mon, 14 Nov 2022 16:04:37 -0600 Subject: [PATCH] feat: add Clone() function (#83) add new `csproto.Clone()` function and associated tests add `go.work*` to `.gitignore` --- .gitignore | 3 +++ clone.go | 25 +++++++++++++++++++++++++ example/proto2_gogo_test.go | 10 ++++++++++ example/proto2_googlev1_test.go | 10 ++++++++++ example/proto2_googlev2_test.go | 10 ++++++++++ example/proto3_gogo_test.go | 10 ++++++++++ example/proto3_googlev1_test.go | 10 ++++++++++ example/proto3_googlev2_test.go | 10 ++++++++++ 8 files changed, 88 insertions(+) create mode 100644 clone.go diff --git a/.gitignore b/.gitignore index 3ead75e..848c780 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ dist/ # editor-related files .vscode/ + +# Go workspace files +go.work* diff --git a/clone.go b/clone.go new file mode 100644 index 0000000..7b021f8 --- /dev/null +++ b/clone.go @@ -0,0 +1,25 @@ +package csproto + +import ( + gogo "github.com/gogo/protobuf/proto" + google "github.com/golang/protobuf/proto" //nolint: staticcheck // we're using this deprecated package intentionally + googlev2 "google.golang.org/protobuf/proto" +) + +// Clone returns a deep copy of m, delegating to the appropriate underlying Protobuf API based on +// the concrete type of m. Since the underlying runtimes return different types, this function returns +// interface{} and the caller will need to type-assert back to the concrete type of m. +// +// If m is not one of the supported message types, this function returns nil. +func Clone(m interface{}) interface{} { + switch MsgType(m) { + case MessageTypeGoogle: + return googlev2.Clone(m.(googlev2.Message)) + case MessageTypeGoogleV1: + return google.Clone(m.(google.Message)) + case MessageTypeGogo: + return gogo.Clone(m.(gogo.Message)) + default: + return nil + } +} diff --git a/example/proto2_gogo_test.go b/example/proto2_gogo_test.go index 89e9210..7406262 100644 --- a/example/proto2_gogo_test.go +++ b/example/proto2_gogo_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" "time" + "unsafe" "github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/types" @@ -251,6 +252,15 @@ func TestProto2GogoEqual(t *testing.T) { assert.True(t, csproto.Equal(m1, m2), "messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) } +func TestProto2GogoClone(t *testing.T) { + m1 := createTestProto2GogoMessage() + m2, ok := csproto.Clone(m1).(*gogo.BaseEvent) + + assert.True(t, ok, "type assertion to *gogo.BaseEvent should succeed") + assert.True(t, csproto.Equal(m1, m2), "cloned messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) + assert.NotEqual(t, unsafe.Pointer(m1), unsafe.Pointer(m2)) +} + func createTestProto2GogoMessage() *gogo.BaseEvent { now := uint64(time.Now().UTC().Unix()) et := gogo.EventType_EVENT_TYPE_ONE diff --git a/example/proto2_googlev1_test.go b/example/proto2_googlev1_test.go index 9a974da..80d38eb 100644 --- a/example/proto2_googlev1_test.go +++ b/example/proto2_googlev1_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" "time" + "unsafe" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" @@ -278,6 +279,15 @@ func TestProto2GoogleV1Equal(t *testing.T) { assert.True(t, csproto.Equal(m1, m2), "messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) } +func TestProto2GoogleV1Clone(t *testing.T) { + m1 := createTestProto2GoogleV1Message() + m2, ok := csproto.Clone(m1).(*googlev1.BaseEvent) + + assert.True(t, ok, "type assertion to *gogo.BaseEvent should succeed") + assert.True(t, csproto.Equal(m1, m2), "cloned messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) + assert.NotEqual(t, unsafe.Pointer(m1), unsafe.Pointer(m2)) +} + func createTestProto2GoogleV1Message() *googlev1.BaseEvent { now := uint64(time.Now().UTC().Unix()) et := googlev1.EventType_EVENT_TYPE_ONE diff --git a/example/proto2_googlev2_test.go b/example/proto2_googlev2_test.go index 9b141a9..f931ea2 100644 --- a/example/proto2_googlev2_test.go +++ b/example/proto2_googlev2_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" "time" + "unsafe" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" @@ -278,6 +279,15 @@ func TestProto2GoogleV2Equal(t *testing.T) { assert.True(t, csproto.Equal(m1, m2), "messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) } +func TestProto2GoogleV2Clone(t *testing.T) { + m1 := createTestProto2GoogleV2Message() + m2, ok := csproto.Clone(m1).(*googlev2.BaseEvent) + + assert.True(t, ok, "type assertion to *googlev2.BaseEvent should succeed") + assert.True(t, csproto.Equal(m1, m2), "cloned messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) + assert.NotEqual(t, unsafe.Pointer(m1), unsafe.Pointer(m2)) +} + func createTestProto2GoogleV2Message() *googlev2.BaseEvent { now := uint64(time.Now().UTC().Unix()) et := googlev2.EventType_EVENT_TYPE_ONE diff --git a/example/proto3_gogo_test.go b/example/proto3_gogo_test.go index fa4e8c0..85f12ad 100644 --- a/example/proto3_gogo_test.go +++ b/example/proto3_gogo_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" "time" + "unsafe" "github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/types" @@ -248,6 +249,15 @@ func TestProto3GogoEqual(t *testing.T) { assert.True(t, csproto.Equal(m1, m2), "messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) } +func TestProto3GogoClone(t *testing.T) { + m1 := createTestProto3GogoMessage() + m2, ok := csproto.Clone(m1).(*gogo.TestEvent) + + assert.True(t, ok, "type assertion to *gogo.TestEvent should succeed") + assert.True(t, csproto.Equal(m1, m2), "cloned messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) + assert.NotEqual(t, unsafe.Pointer(m1), unsafe.Pointer(m2)) +} + func createTestProto3GogoMessage() *gogo.TestEvent { event := gogo.TestEvent{ Name: "test", diff --git a/example/proto3_googlev1_test.go b/example/proto3_googlev1_test.go index ac245f7..156ff3a 100644 --- a/example/proto3_googlev1_test.go +++ b/example/proto3_googlev1_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" "time" + "unsafe" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" @@ -256,6 +257,15 @@ func TestProto3GoogleV1Equal(t *testing.T) { assert.True(t, csproto.Equal(m1, m2), "messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) } +func TestProto3GoogleV1Clone(t *testing.T) { + m1 := createTestProto3GoogleV1Message() + m2, ok := csproto.Clone(m1).(*googlev1.TestEvent) + + assert.True(t, ok, "type assertion to *googlev1.TestEvent should succeed") + assert.True(t, csproto.Equal(m1, m2), "cloned messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) + assert.NotEqual(t, unsafe.Pointer(m1), unsafe.Pointer(m2)) +} + func createTestProto3GoogleV1Message() *googlev1.TestEvent { event := googlev1.TestEvent{ Name: "test", diff --git a/example/proto3_googlev2_test.go b/example/proto3_googlev2_test.go index 88e0b12..f8ce533 100644 --- a/example/proto3_googlev2_test.go +++ b/example/proto3_googlev2_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" "time" + "unsafe" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" @@ -256,6 +257,15 @@ func TestProto3GoogleV2Equal(t *testing.T) { assert.True(t, csproto.Equal(m1, m2), "messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) } +func TestProto3GoogleV2Clone(t *testing.T) { + m1 := createTestProto3GoogleV2Message() + m2, ok := csproto.Clone(m1).(*googlev2.TestEvent) + + assert.True(t, ok, "type assertion to *googlev2.TestEvent should succeed") + assert.True(t, csproto.Equal(m1, m2), "cloned messages should be equal\nm1=%s\nm2=%s", m1.String(), m2.String()) + assert.NotEqual(t, unsafe.Pointer(m1), unsafe.Pointer(m2)) +} + func createTestProto3GoogleV2Message() *googlev2.TestEvent { event := googlev2.TestEvent{ Name: "test",