Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions decode_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"reflect"

"github.com/vmihailenco/msgpack/v5/msgpcode"
)

var (
Expand Down Expand Up @@ -71,6 +73,9 @@ func _getDecoder(typ reflect.Type) decoderFunc {
return nilAwareDecoder(typ, unmarshalValue)
}
if typ.Implements(binaryUnmarshalerType) {
if typ.Implements(textUnmarshalerType) {
return nilAwareDecoder(typ, unmarshalBinaryOrTextValue)
}
return nilAwareDecoder(typ, unmarshalBinaryValue)
}
if typ.Implements(textUnmarshalerType) {
Expand All @@ -87,6 +92,9 @@ func _getDecoder(typ reflect.Type) decoderFunc {
return addrDecoder(nilAwareDecoder(typ, unmarshalValue))
}
if ptr.Implements(binaryUnmarshalerType) {
if ptr.Implements(textUnmarshalerType) {
return addrDecoder(nilAwareDecoder(typ, unmarshalBinaryOrTextValue))
}
return addrDecoder(nilAwareDecoder(typ, unmarshalBinaryValue))
}
if ptr.Implements(textUnmarshalerType) {
Expand Down Expand Up @@ -238,6 +246,20 @@ func unmarshalValue(d *Decoder, v reflect.Value) error {
return unmarshaler.UnmarshalMsgpack(b)
}

// unmarshalBinaryOrTextValue peeks at the wire format to choose between
// BinaryUnmarshaler (bin) and TextUnmarshaler (str). Used when a type
// implements both interfaces.
func unmarshalBinaryOrTextValue(d *Decoder, v reflect.Value) error {
c, err := d.PeekCode()
if err != nil {
return err
}
if msgpcode.IsString(c) {
return unmarshalTextValue(d, v)
}
return unmarshalBinaryValue(d, v)
}

func unmarshalBinaryValue(d *Decoder, v reflect.Value) error {
data, err := d.DecodeBytes()
if err != nil {
Expand Down
34 changes: 34 additions & 0 deletions types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,40 @@ func TestPoolReleasesOversizedBuffers(t *testing.T) {
require.Equal(t, "small", s)
}

// Issue #10: types implementing both TextUnmarshaler and BinaryUnmarshaler
// should use TextUnmarshaler when wire format is str.
type dualUnmarshaler struct {
text string
binary []byte
}

func (d *dualUnmarshaler) MarshalText() ([]byte, error) { return []byte(d.text), nil }
func (d *dualUnmarshaler) UnmarshalText(b []byte) error { d.text = string(b); return nil }
func (d *dualUnmarshaler) MarshalBinary() ([]byte, error) { return d.binary, nil }
func (d *dualUnmarshaler) UnmarshalBinary(b []byte) error { d.binary = b; return nil }

func TestTextUnmarshalerWithStrFormat(t *testing.T) {
// Encode a plain string (str format) and decode into a type that
// implements both BinaryUnmarshaler and TextUnmarshaler.
b, err := msgpack.Marshal("hello")
require.NoError(t, err)

var du dualUnmarshaler
require.NoError(t, msgpack.Unmarshal(b, &du))
// Should have used TextUnmarshaler because the wire format is str.
require.Equal(t, "hello", du.text)
require.Nil(t, du.binary)

// Encode as binary (bin format) and decode — should use BinaryUnmarshaler.
b, err = msgpack.Marshal([]byte{0xDE, 0xAD})
require.NoError(t, err)

var du2 dualUnmarshaler
require.NoError(t, msgpack.Unmarshal(b, &du2))
require.Equal(t, []byte{0xDE, 0xAD}, du2.binary)
require.Equal(t, "", du2.text)
}

func mustParseTime(format, s string) time.Time {
tm, err := time.Parse(format, s)
if err != nil {
Expand Down