From 9325e22871a4c996890c8f3d805e3aab73a5dc79 Mon Sep 17 00:00:00 2001
From: Leonard Lyubich <leonard@nspcc.ru>
Date: Fri, 13 Nov 2020 15:51:27 +0300
Subject: [PATCH] [#168] object: Implement binary/JSON encoders/decoders on
 Object

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
---
 pkg/object/object.go      | 11 -----------
 pkg/object/raw_test.go    | 25 +++++++++++++++++++++++++
 pkg/object/rw.go          | 32 ++++++++++++++++++++++++++++++++
 v2/object/json.go         | 20 ++++++++++++++++++++
 v2/object/json_test.go    | 12 ++++++++++++
 v2/object/marshal.go      | 11 +++++++++++
 v2/object/marshal_test.go |  6 ++----
 7 files changed, 102 insertions(+), 15 deletions(-)

diff --git a/pkg/object/object.go b/pkg/object/object.go
index 9de49a73..e447be59 100644
--- a/pkg/object/object.go
+++ b/pkg/object/object.go
@@ -37,14 +37,3 @@ func (o *Object) ToV2() *object.Object {
 
 	return nil
 }
-
-// FromBytes restores Object instance from a binary representation.
-func FromBytes(data []byte) (*Object, error) {
-	oV2 := new(object.Object)
-
-	if err := oV2.StableUnmarshal(data); err != nil {
-		return nil, err
-	}
-
-	return NewFromV2(oV2), nil
-}
diff --git a/pkg/object/raw_test.go b/pkg/object/raw_test.go
index 2e393220..344bd284 100644
--- a/pkg/object/raw_test.go
+++ b/pkg/object/raw_test.go
@@ -287,3 +287,28 @@ func TestRwObject_HasParent(t *testing.T) {
 
 	require.False(t, obj.HasParent())
 }
+
+func TestRWObjectEncoding(t *testing.T) {
+	o := NewRaw()
+	o.SetID(randID(t))
+
+	t.Run("binary", func(t *testing.T) {
+		data, err := o.Marshal()
+		require.NoError(t, err)
+
+		o2 := NewRaw()
+		require.NoError(t, o2.Unmarshal(data))
+
+		require.Equal(t, o, o2)
+	})
+
+	t.Run("json", func(t *testing.T) {
+		data, err := o.MarshalJSON()
+		require.NoError(t, err)
+
+		o2 := NewRaw()
+		require.NoError(t, o2.UnmarshalJSON(data))
+
+		require.Equal(t, o, o2)
+	})
+}
diff --git a/pkg/object/rw.go b/pkg/object/rw.go
index 0aab2c78..8d0380c8 100644
--- a/pkg/object/rw.go
+++ b/pkg/object/rw.go
@@ -352,3 +352,35 @@ func (o *rwObject) HasParent() bool {
 		GetHeader().
 		GetSplit() != nil
 }
+
+// Marshal marshals object into a protobuf binary form.
+//
+// Buffer is allocated when the argument is empty.
+// Otherwise, the first buffer is used.
+func (o *rwObject) Marshal(b ...[]byte) ([]byte, error) {
+	var buf []byte
+	if len(b) > 0 {
+		buf = b[0]
+	}
+
+	return (*object.Object)(o).
+		StableMarshal(buf)
+}
+
+// Unmarshal unmarshals protobuf binary representation of object.
+func (o *rwObject) Unmarshal(data []byte) error {
+	return (*object.Object)(o).
+		Unmarshal(data)
+}
+
+// MarshalJSON encodes object to protobuf JSON format.
+func (o *rwObject) MarshalJSON() ([]byte, error) {
+	return (*object.Object)(o).
+		MarshalJSON()
+}
+
+// UnmarshalJSON decodes object from protobuf JSON format.
+func (o *rwObject) UnmarshalJSON(data []byte) error {
+	return (*object.Object)(o).
+		UnmarshalJSON(data)
+}
diff --git a/v2/object/json.go b/v2/object/json.go
index 3cf35848..940aa7c3 100644
--- a/v2/object/json.go
+++ b/v2/object/json.go
@@ -104,3 +104,23 @@ func (h *HeaderWithSignature) UnmarshalJSON(data []byte) error {
 
 	return nil
 }
+
+func (o *Object) MarshalJSON() ([]byte, error) {
+	return protojson.MarshalOptions{
+		EmitUnpopulated: true,
+	}.Marshal(
+		ObjectToGRPCMessage(o),
+	)
+}
+
+func (o *Object) UnmarshalJSON(data []byte) error {
+	msg := new(object.Object)
+
+	if err := protojson.Unmarshal(data, msg); err != nil {
+		return err
+	}
+
+	*o = *ObjectFromGRPCMessage(msg)
+
+	return nil
+}
diff --git a/v2/object/json_test.go b/v2/object/json_test.go
index 87d05c98..ceefd96f 100644
--- a/v2/object/json_test.go
+++ b/v2/object/json_test.go
@@ -66,3 +66,15 @@ func TestHeaderWithSignatureJSON(t *testing.T) {
 
 	require.Equal(t, h, h2)
 }
+
+func TestObjectJSON(t *testing.T) {
+	o := generateObject("data")
+
+	data, err := o.MarshalJSON()
+	require.NoError(t, err)
+
+	o2 := new(object.Object)
+	require.NoError(t, o2.UnmarshalJSON(data))
+
+	require.Equal(t, o, o2)
+}
diff --git a/v2/object/marshal.go b/v2/object/marshal.go
index 0728cb6f..b59419e9 100644
--- a/v2/object/marshal.go
+++ b/v2/object/marshal.go
@@ -557,6 +557,17 @@ func (o *Object) StableUnmarshal(data []byte) error {
 	return nil
 }
 
+func (o *Object) Unmarshal(data []byte) error {
+	m := new(object.Object)
+	if err := goproto.Unmarshal(data, m); err != nil {
+		return err
+	}
+
+	*o = *ObjectFromGRPCMessage(m)
+
+	return nil
+}
+
 func (r *GetRequestBody) StableMarshal(buf []byte) ([]byte, error) {
 	if r == nil {
 		return []byte{}, nil
diff --git a/v2/object/marshal_test.go b/v2/object/marshal_test.go
index dbaf2f35..efd5df30 100644
--- a/v2/object/marshal_test.go
+++ b/v2/object/marshal_test.go
@@ -78,16 +78,14 @@ func TestHeader_StableMarshal(t *testing.T) {
 
 func TestObject_StableMarshal(t *testing.T) {
 	from := generateObject("Payload")
-	transport := new(grpc.Object)
 
 	t.Run("non empty", func(t *testing.T) {
 		wire, err := from.StableMarshal(nil)
 		require.NoError(t, err)
 
-		err = goproto.Unmarshal(wire, transport)
-		require.NoError(t, err)
+		to := new(object.Object)
+		require.NoError(t, to.Unmarshal(wire))
 
-		to := object.ObjectFromGRPCMessage(transport)
 		require.Equal(t, from, to)
 	})
 }