Skip to content

Commit 1ab2f48

Browse files
committed
fix(decode): choose TextUnmarshaler over BinaryUnmarshaler for str format (#10)
When a type implements both interfaces, the decoder now peeks at the wire format and dispatches to TextUnmarshaler for str codes and BinaryUnmarshaler for bin codes.
1 parent d5e2e42 commit 1ab2f48

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

decode_value.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"errors"
66
"fmt"
77
"reflect"
8+
9+
"github.com/vmihailenco/msgpack/v5/msgpcode"
810
)
911

1012
var (
@@ -71,6 +73,9 @@ func _getDecoder(typ reflect.Type) decoderFunc {
7173
return nilAwareDecoder(typ, unmarshalValue)
7274
}
7375
if typ.Implements(binaryUnmarshalerType) {
76+
if typ.Implements(textUnmarshalerType) {
77+
return nilAwareDecoder(typ, unmarshalBinaryOrTextValue)
78+
}
7479
return nilAwareDecoder(typ, unmarshalBinaryValue)
7580
}
7681
if typ.Implements(textUnmarshalerType) {
@@ -87,6 +92,9 @@ func _getDecoder(typ reflect.Type) decoderFunc {
8792
return addrDecoder(nilAwareDecoder(typ, unmarshalValue))
8893
}
8994
if ptr.Implements(binaryUnmarshalerType) {
95+
if ptr.Implements(textUnmarshalerType) {
96+
return addrDecoder(nilAwareDecoder(typ, unmarshalBinaryOrTextValue))
97+
}
9098
return addrDecoder(nilAwareDecoder(typ, unmarshalBinaryValue))
9199
}
92100
if ptr.Implements(textUnmarshalerType) {
@@ -238,6 +246,20 @@ func unmarshalValue(d *Decoder, v reflect.Value) error {
238246
return unmarshaler.UnmarshalMsgpack(b)
239247
}
240248

249+
// unmarshalBinaryOrTextValue peeks at the wire format to choose between
250+
// BinaryUnmarshaler (bin) and TextUnmarshaler (str). Used when a type
251+
// implements both interfaces.
252+
func unmarshalBinaryOrTextValue(d *Decoder, v reflect.Value) error {
253+
c, err := d.PeekCode()
254+
if err != nil {
255+
return err
256+
}
257+
if msgpcode.IsString(c) {
258+
return unmarshalTextValue(d, v)
259+
}
260+
return unmarshalBinaryValue(d, v)
261+
}
262+
241263
func unmarshalBinaryValue(d *Decoder, v reflect.Value) error {
242264
data, err := d.DecodeBytes()
243265
if err != nil {

types_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,40 @@ func TestFloat64(t *testing.T) {
12461246
}
12471247
}
12481248

1249+
// Issue #10: types implementing both TextUnmarshaler and BinaryUnmarshaler
1250+
// should use TextUnmarshaler when wire format is str.
1251+
type dualUnmarshaler struct {
1252+
text string
1253+
binary []byte
1254+
}
1255+
1256+
func (d *dualUnmarshaler) MarshalText() ([]byte, error) { return []byte(d.text), nil }
1257+
func (d *dualUnmarshaler) UnmarshalText(b []byte) error { d.text = string(b); return nil }
1258+
func (d *dualUnmarshaler) MarshalBinary() ([]byte, error) { return d.binary, nil }
1259+
func (d *dualUnmarshaler) UnmarshalBinary(b []byte) error { d.binary = b; return nil }
1260+
1261+
func TestTextUnmarshalerWithStrFormat(t *testing.T) {
1262+
// Encode a plain string (str format) and decode into a type that
1263+
// implements both BinaryUnmarshaler and TextUnmarshaler.
1264+
b, err := msgpack.Marshal("hello")
1265+
require.NoError(t, err)
1266+
1267+
var du dualUnmarshaler
1268+
require.NoError(t, msgpack.Unmarshal(b, &du))
1269+
// Should have used TextUnmarshaler because the wire format is str.
1270+
require.Equal(t, "hello", du.text)
1271+
require.Nil(t, du.binary)
1272+
1273+
// Encode as binary (bin format) and decode — should use BinaryUnmarshaler.
1274+
b, err = msgpack.Marshal([]byte{0xDE, 0xAD})
1275+
require.NoError(t, err)
1276+
1277+
var du2 dualUnmarshaler
1278+
require.NoError(t, msgpack.Unmarshal(b, &du2))
1279+
require.Equal(t, []byte{0xDE, 0xAD}, du2.binary)
1280+
require.Equal(t, "", du2.text)
1281+
}
1282+
12491283
func mustParseTime(format, s string) time.Time {
12501284
tm, err := time.Parse(format, s)
12511285
if err != nil {

0 commit comments

Comments
 (0)