Repository: hamba/avro

Issue:
OCF: Hard to compose EncoderFuncs to modify metadata
`ocf.WithMetadata(meta map[string][]byte) EncoderFunc` exists to modify an OCF encoder to set metadata that gets written to the OCF header. This is useful, but hard to compose, since WithMetadata overwrites any previously-set metadata in the encoder configuration.

I propose that a new `ocf.WithMetadataKeyVal(key string, val []byte) EncoderFunc` would be useful to help here. This function would add a single key-value pair to the existing metadata, rather than overwriting it.

Discussion:
@nrwiersma: Seems fair.

Solution patch:
diff --git a/ocf/ocf.go b/ocf/ocf.go
index b3d7408..ce552ec 100644
--- a/ocf/ocf.go
+++ b/ocf/ocf.go
@@ -266,6 +266,14 @@ func WithMetadata(meta map[string][]byte) EncoderFunc {
 	}
 }
 
+// WithMetadataKeyVal sets a single key-value pair for the metadata on
+// the encoder header.
+func WithMetadataKeyVal(key string, val []byte) EncoderFunc {
+	return func(cfg *encoderConfig) {
+		cfg.Metadata[key] = val
+	}
+}
+
 // WithEncoderSchemaCache sets the schema cache for the encoder.
 // If not specified, defaults to avro.DefaultSchemaCache.
 func WithEncoderSchemaCache(cache *avro.SchemaCache) EncoderFunc {



Test patch:
diff --git a/ocf/ocf_test.go b/ocf/ocf_test.go
index 3f5eb6a..2012c7e 100644
--- a/ocf/ocf_test.go
+++ b/ocf/ocf_test.go
@@ -1021,6 +1021,25 @@ func TestEncodeDecodeMetadata(t *testing.T) {
 	assert.Equal(t, []byte("foo"), dec.Metadata()["test"])
 }
 
+func TestEncodeDecodeMetadataKeyVal(t *testing.T) {
+	buf := &bytes.Buffer{}
+	enc, _ := ocf.NewEncoder(`"long"`, buf,
+		ocf.WithMetadataKeyVal("key1", []byte("val1")),
+		ocf.WithMetadataKeyVal("key2", []byte("val2")),
+	)
+
+	err := enc.Encode(int64(1))
+	require.NoError(t, err)
+
+	_ = enc.Close()
+
+	dec, err := ocf.NewDecoder(buf)
+
+	require.NoError(t, err)
+	assert.Equal(t, []byte("val1"), dec.Metadata()["key1"])
+	assert.Equal(t, []byte("val2"), dec.Metadata()["key2"])
+}
+
 func TestEncode_WithSyncBlock(t *testing.T) {
 	buf := &bytes.Buffer{}
 	syncBlock := [16]byte{9, 9, 9, 9, 9, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}