Repository: hamba/avro
Pull request #143 (2022-01-24 09:07:38)

Expose Write method on ocf encoder
From discussion on issue https://github.com/hamba/avro/issues/142

- [x] Expose Write method on ocf encoder

Signed-off-by: Jonas Brunsgaard <jonas.brunsgaard@gmail.com>

nrwiersma: Perhaps a test to cover this case would be good. Thanks for the contribution.
brunsgaard: What style do you prefer, no-error assertion on everything or only the method that is part of that specific test? It seems a bit mixed in the rest of the tests.

```
    enc, err := ocf.NewEncoder(schema, buf)
    assert.NoError(t, err)

    encodedBytes, err := avro.Marshal(avro.MustParse(schema), record)
    assert.NoError(t, err)

    err = enc.Write(encodedBytes)
    assert.NoError(t, err)

    err = enc.Close()
    assert.NoError(t, err)

    assert.Equal(t, 957, buf.Len())
```
 
 or 
 
```
    enc, _ := ocf.NewEncoder(schema, buf)

    encodedBytes, _ := avro.Marshal(avro.MustParse(schema), record)

    err := enc.Write(encodedBytes)
    assert.NoError(t, err)

    _ = enc.Close()
    
    assert.Equal(t, 957, buf.Len())
```
nrwiersma: The first, but with `require.NoError` to kill the test if an error pops up that we are not expecting.

Patch:
diff --git a/ocf/ocf.go b/ocf/ocf.go
index b42864d..a585b9a 100644
--- a/ocf/ocf.go
+++ b/ocf/ocf.go
@@ -243,6 +243,25 @@ func NewEncoder(s string, w io.Writer, opts ...EncoderFunc) (*Encoder, error) {
 	return e, nil
 }
 
+// Write v to the internal buffer. This method skips the internal encoder and
+// therefore the caller is responsible for encoding the bytes. No error will be
+// thrown if the bytes does not conform to the schema given to NewEncoder, but
+// the final ocf data will be corrupted.
+func (e *Encoder) Write(v []byte) error {
+	if _, err := e.buf.Write(v); err != nil {
+		return err
+	}
+
+	e.count++
+	if e.count >= e.blockLength {
+		if err := e.writerBlock(); err != nil {
+			return err
+		}
+	}
+
+	return e.writer.Error
+}
+
 // Encode writes the Avro encoding of v to the stream.
 func (e *Encoder) Encode(v interface{}) error {
 	if err := e.encoder.Encode(v); err != nil {


Test patch:
diff --git a/ocf/ocf_test.go b/ocf/ocf_test.go
index d7b2392..b12a608 100644
--- a/ocf/ocf_test.go
+++ b/ocf/ocf_test.go
@@ -6,8 +6,10 @@ import (
 	"os"
 	"testing"
 
+	"github.com/hamba/avro"
 	"github.com/hamba/avro/ocf"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 var schema = `{
@@ -423,6 +425,47 @@ func TestEncoder(t *testing.T) {
 	assert.NoError(t, err)
 }
 
+func TestEncoder_Write(t *testing.T) {
+	unionStr := "union value"
+	record := FullRecord{
+		Strings: []string{"string1", "string2", "string3", "string4", "string5"},
+		Longs:   []int64{1, 2, 3, 4, 5},
+		Enum:    "C",
+		Map: map[string]int{
+			"key1": 1,
+			"key2": 2,
+			"key3": 3,
+			"key4": 4,
+			"key5": 5,
+		},
+		Nullable: &unionStr,
+		Fixed:    [16]byte{0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04},
+		Record: &TestRecord{
+			Long:   1925639126735,
+			String: "I am a test record",
+			Int:    666,
+			Float:  7171.17,
+			Double: 916734926348163.01973408746523,
+			Bool:   true,
+		},
+	}
+
+	buf := &bytes.Buffer{}
+	enc, err := ocf.NewEncoder(schema, buf)
+	require.NoError(t, err)
+
+	encodedBytes, err := avro.Marshal(avro.MustParse(schema), record)
+	require.NoError(t, err)
+
+	err = enc.Write(encodedBytes)
+	require.NoError(t, err)
+
+	err = enc.Close()
+	require.NoError(t, err)
+
+	require.Equal(t, 957, buf.Len())
+}
+
 func TestEncoder_EncodeCompressesDeflate(t *testing.T) {
 	unionStr := "union value"
 	record := FullRecord{
@@ -450,7 +493,6 @@ func TestEncoder_EncodeCompressesDeflate(t *testing.T) {
 
 	buf := &bytes.Buffer{}
 	enc, _ := ocf.NewEncoder(schema, buf, ocf.WithCodec(ocf.Deflate))
-	defer enc.Close()
 
 	err := enc.Encode(record)
 	assert.NoError(t, err)