Skip to content

Commit ad2d16a

Browse files
authored
Support marshalling value via function (#124)
1 parent 938207e commit ad2d16a

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

marshal.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,31 @@ func marshalImpl(vo reflect.Value, w io.Writer, pos uint64, parent *Element, opt
266266
}
267267
pos, err = writeOne(lst[0])
268268
}
269+
case reflect.Func:
270+
ret := vn.Call(nil)
271+
lenRet := len(ret)
272+
if lenRet != 1 && lenRet != 2 {
273+
return pos, wrapErrorf(ErrIncompatibleType, "number of return value must be 1 or 2 but %d", lenRet)
274+
}
275+
val := ret[0]
276+
if lenRet == 2 {
277+
errVal := ret[1]
278+
if errVal.Type().String() != "error" {
279+
return pos, wrapErrorf(ErrIncompatibleType, "2nd return value must be error but %s", errVal.Type())
280+
}
281+
if iFace := errVal.Interface(); iFace != nil {
282+
return pos, iFace.(error)
283+
}
284+
}
285+
lst, ok := pealElem(val, e.t == DataTypeBinary, tag.omitEmpty)
286+
if !ok {
287+
return pos, wrapErrorf(
288+
ErrIncompatibleType, "marshalling %s from func", val.Type(),
289+
)
290+
}
291+
for _, l := range lst {
292+
pos, err = writeOne(l)
293+
}
269294
default:
270295
pos, err = writeOne(vn)
271296
}

marshal_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,151 @@ func TestMarshal_Chan(t *testing.T) {
543543
})
544544
}
545545

546+
func TestMarshal_Func(t *testing.T) {
547+
expected := []byte{
548+
0x18, 0x53, 0x80, 0x67, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
549+
0x1F, 0x43, 0xB6, 0x75, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
550+
0xE7, 0x81, 0x01,
551+
}
552+
type Cluster struct {
553+
Timecode uint64 `ebml:"Timecode"`
554+
}
555+
556+
t.Run("FuncStruct", func(t *testing.T) {
557+
input := &struct {
558+
Segment struct {
559+
Cluster func() Cluster `ebml:"Cluster,size=unknown"`
560+
} `ebml:"Segment,size=unknown"`
561+
}{}
562+
input.Segment.Cluster = func() Cluster {
563+
return Cluster{Timecode: 0x01}
564+
}
565+
566+
var b bytes.Buffer
567+
if err := Marshal(input, &b); err != nil {
568+
t.Fatalf("Unexpected error: '%v'", err)
569+
}
570+
if !bytes.Equal(expected, b.Bytes()) {
571+
t.Errorf("Marshaled binary doesn't match:\n expected: %v,\n got: %v", expected, b.Bytes())
572+
}
573+
})
574+
t.Run("FuncStructPtr", func(t *testing.T) {
575+
input := &struct {
576+
Segment struct {
577+
Cluster func() *Cluster `ebml:"Cluster,size=unknown"`
578+
} `ebml:"Segment,size=unknown"`
579+
}{}
580+
581+
t.Run("Valid", func(t *testing.T) {
582+
input.Segment.Cluster = func() *Cluster {
583+
return &Cluster{Timecode: 0x01}
584+
}
585+
586+
var b bytes.Buffer
587+
if err := Marshal(input, &b); err != nil {
588+
t.Fatalf("Unexpected error: '%v'", err)
589+
}
590+
if !bytes.Equal(expected, b.Bytes()) {
591+
t.Errorf("Marshaled binary doesn't match:\n expected: %v,\n got: %v", expected, b.Bytes())
592+
}
593+
})
594+
t.Run("Nil", func(t *testing.T) {
595+
input.Segment.Cluster = func() *Cluster {
596+
return nil
597+
}
598+
599+
if err := Marshal(input, &bytes.Buffer{}); !errs.Is(err, ErrIncompatibleType) {
600+
t.Fatalf("Expected error: '%v', got: '%v'", ErrIncompatibleType, err)
601+
}
602+
})
603+
})
604+
t.Run("FuncStructSlice", func(t *testing.T) {
605+
input := &struct {
606+
Segment struct {
607+
Cluster func() []Cluster `ebml:"Cluster,size=unknown"`
608+
} `ebml:"Segment,size=unknown"`
609+
}{}
610+
input.Segment.Cluster = func() []Cluster {
611+
return []Cluster{
612+
{Timecode: 0x01},
613+
{Timecode: 0x02},
614+
}
615+
}
616+
617+
var b bytes.Buffer
618+
if err := Marshal(input, &b); err != nil {
619+
t.Fatalf("Unexpected error: '%v'", err)
620+
}
621+
expected := []byte{
622+
0x18, 0x53, 0x80, 0x67, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
623+
0x1F, 0x43, 0xB6, 0x75, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
624+
0xE7, 0x81, 0x01,
625+
0x1F, 0x43, 0xB6, 0x75, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
626+
0xE7, 0x81, 0x02,
627+
}
628+
if !bytes.Equal(expected, b.Bytes()) {
629+
t.Errorf("Marshaled binary doesn't match:\n expected: %v,\n got: %v", expected, b.Bytes())
630+
}
631+
})
632+
t.Run("FuncWithError", func(t *testing.T) {
633+
input := &struct {
634+
Segment struct {
635+
Cluster func() (Cluster, error) `ebml:"Cluster,size=unknown"`
636+
} `ebml:"Segment,size=unknown"`
637+
}{}
638+
639+
t.Run("Valid", func(t *testing.T) {
640+
input.Segment.Cluster = func() (Cluster, error) {
641+
return Cluster{Timecode: 0x01}, nil
642+
}
643+
644+
var b bytes.Buffer
645+
if err := Marshal(input, &b); err != nil {
646+
t.Fatalf("Unexpected error: '%v'", err)
647+
}
648+
if !bytes.Equal(expected, b.Bytes()) {
649+
t.Errorf("Marshaled binary doesn't match:\n expected: %v,\n got: %v", expected, b.Bytes())
650+
}
651+
})
652+
t.Run("Error", func(t *testing.T) {
653+
expectedErr := errors.New("an error")
654+
input.Segment.Cluster = func() (Cluster, error) {
655+
return Cluster{Timecode: 0x01}, expectedErr
656+
}
657+
658+
if err := Marshal(input, &bytes.Buffer{}); !errs.Is(err, expectedErr) {
659+
t.Fatalf("Expected error: '%v', got: '%v'", expectedErr, err)
660+
}
661+
})
662+
t.Run("NonErrorType", func(t *testing.T) {
663+
input := &struct {
664+
Segment struct {
665+
Cluster func() (*Cluster, int) `ebml:"Cluster,size=unknown"`
666+
} `ebml:"Segment,size=unknown"`
667+
}{}
668+
input.Segment.Cluster = func() (*Cluster, int) {
669+
return nil, 1
670+
}
671+
672+
if err := Marshal(input, &bytes.Buffer{}); !errs.Is(err, ErrIncompatibleType) {
673+
t.Fatalf("Expected error: '%v', got: '%v'", ErrIncompatibleType, err)
674+
}
675+
})
676+
})
677+
t.Run("InvalidFunc", func(t *testing.T) {
678+
input := &struct {
679+
Segment struct {
680+
Cluster func() `ebml:"Cluster,size=unknown"`
681+
} `ebml:"Segment,size=unknown"`
682+
}{}
683+
input.Segment.Cluster = func() {}
684+
685+
if err := Marshal(input, &bytes.Buffer{}); !errs.Is(err, ErrIncompatibleType) {
686+
t.Fatalf("Expected error: '%v', got: '%v'", ErrIncompatibleType, err)
687+
}
688+
})
689+
}
690+
546691
func BenchmarkMarshal(b *testing.B) {
547692
type EBMLHeader struct {
548693
DocType string `ebml:"EBMLDocType"`

0 commit comments

Comments
 (0)