feat(codec): Add collections value codec for interfaces. (#15898)

Co-authored-by: unknown unknown <unknown@unknown>
This commit is contained in:
testinginprod
2023-04-24 08:34:42 +00:00
committed by GitHub
co-authored by unknown unknown
parent 294027cf2d
commit 295c261b18
3 changed files with 124 additions and 6 deletions
+47
View File
@@ -1,6 +1,9 @@
package codec
import (
"fmt"
"reflect"
gogotypes "github.com/cosmos/gogoproto/types"
"cosmossdk.io/collections"
@@ -79,3 +82,47 @@ func (c collValue[T, PT]) Stringify(value T) string {
func (c collValue[T, PT]) ValueType() string {
return "gogoproto/" + proto.MessageName(PT(new(T)))
}
// CollInterfaceValue instantiates a new collections.ValueCodec for a generic
// interface value. The codec must be able to marshal and unmarshal the
// interface.
func CollInterfaceValue[T proto.Message](codec BinaryCodec) collcodec.ValueCodec[T] {
var x T // assertion
if reflect.TypeOf(&x).Elem().Kind() != reflect.Interface {
panic("CollInterfaceValue can only be used with interface types")
}
return collInterfaceValue[T]{codec.(Codec)}
}
type collInterfaceValue[T proto.Message] struct {
codec Codec
}
func (c collInterfaceValue[T]) Encode(value T) ([]byte, error) {
return c.codec.MarshalInterface(value)
}
func (c collInterfaceValue[T]) Decode(b []byte) (T, error) {
var value T
err := c.codec.UnmarshalInterface(b, &value)
return value, err
}
func (c collInterfaceValue[T]) EncodeJSON(value T) ([]byte, error) {
return c.codec.MarshalInterfaceJSON(value)
}
func (c collInterfaceValue[T]) DecodeJSON(b []byte) (T, error) {
var value T
err := c.codec.UnmarshalInterfaceJSON(b, &value)
return value, err
}
func (c collInterfaceValue[T]) Stringify(value T) string {
return value.String()
}
func (c collInterfaceValue[T]) ValueType() string {
var t T
return fmt.Sprintf("%T", t)
}
+23 -6
View File
@@ -1,8 +1,11 @@
package codec
package codec_test
import (
"testing"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/stretchr/testify/require"
"cosmossdk.io/collections/colltest"
@@ -11,22 +14,22 @@ import (
)
func TestCollectionsCorrectness(t *testing.T) {
cdc := NewProtoCodec(codectypes.NewInterfaceRegistry())
t.Run("CollValue", func(t *testing.T) {
colltest.TestValueCodec(t, CollValue[gogotypes.UInt64Value](cdc), gogotypes.UInt64Value{
cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
colltest.TestValueCodec(t, codec.CollValue[gogotypes.UInt64Value](cdc), gogotypes.UInt64Value{
Value: 500,
})
})
t.Run("BoolValue", func(t *testing.T) {
colltest.TestValueCodec(t, BoolValue, true)
colltest.TestValueCodec(t, BoolValue, false)
colltest.TestValueCodec(t, codec.BoolValue, true)
colltest.TestValueCodec(t, codec.BoolValue, false)
// asserts produced bytes are equal
valueAssert := func(b bool) {
wantBytes, err := (&gogotypes.BoolValue{Value: b}).Marshal()
require.NoError(t, err)
gotBytes, err := BoolValue.Encode(b)
gotBytes, err := codec.BoolValue.Encode(b)
require.NoError(t, err)
require.Equal(t, wantBytes, gotBytes)
}
@@ -34,4 +37,18 @@ func TestCollectionsCorrectness(t *testing.T) {
valueAssert(true)
valueAssert(false)
})
t.Run("CollInterfaceValue", func(t *testing.T) {
cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
cdc.InterfaceRegistry().RegisterInterface("animal", (*testdata.Animal)(nil), &testdata.Dog{}, &testdata.Cat{})
valueCodec := codec.CollInterfaceValue[testdata.Animal](cdc)
colltest.TestValueCodec[testdata.Animal](t, valueCodec, &testdata.Dog{Name: "Doggo"})
colltest.TestValueCodec[testdata.Animal](t, valueCodec, &testdata.Cat{Moniker: "Kitty"})
// assert if used with a non interface type it yields a panic.
require.Panics(t, func() {
codec.CollInterfaceValue[*testdata.Dog](cdc)
})
})
}