29260d44b53accd338319ee13a8b68c42cd794ba — Daniel Martí 3 months ago f56bf90
allow unions to roundtrip without errors

If a union contains a type T, when decoding into a struct field of said
union type, we would end up with a value of type *T.

This is fine for Unmarshal itself. However, Marshal errors when it
encounters *T instead of T for that union, because the ut.tags map does
not contain *T.

Teach it to reach past the pointer if it encounters *T instead of T, so
that it can encode the type T. Add a simple test round-tripping what's
explained above, which fails without the fix:

		Error Trace: marshal_test.go:274
		Error:       Expected nil, but got: &errors.errorString{s:"Invalid union value: <*bare.Age Value>"}
		Test:        TestRoundtrip
2 files changed, 28 insertions(+), 1 deletions(-)

M marshal.go
M marshal_test.go
M marshal.go => marshal.go +7 -1
@@ 212,7 212,13 @@ func encodeUnion(t reflect.Type) encodeFunc {

	return func(w *Writer, v reflect.Value) error {
		tag, ok := ut.tags[v.Elem().Type()]
		t := v.Elem().Type()
		if t.Kind() == reflect.Ptr {
			// If T is a valid union value type, *T is valid too.
			t = t.Elem()
			v = v.Elem()
		tag, ok := ut.tags[t]
		if !ok {
			return fmt.Errorf("Invalid union value: %s", v.Elem().String())

M marshal_test.go => marshal_test.go +21 -0
@@ 255,6 255,27 @@ func TestMarshalUnion(t *testing.T) {
	assert.Equal(t, reference, data)

func TestRoundtrip(t *testing.T) {
	type T struct {
		// Ensure that unions roundtrip correctly.
		NameAge NameAge
	val := T{
		NameAge: Age(25),
	data, err := Marshal(&val)
	assert.Nil(t, err)

	var val2 T
	err = Unmarshal(data, &val2)
	assert.Nil(t, err)

	data2, err := Marshal(&val2)
	assert.Nil(t, err)

	assert.Equal(t, data, data2)

func TestMarshalCustom(t *testing.T) {
	var val = Custom(0x42)
	data, err := Marshal(&val)