Repository: segmentio/parquet-go (go)

Issue:
GenericWriter doesn't support maps
The current implementation of the GenericWriter doesn't support the usage of maps within a schema.

Example:
```
type Span struct {
  ...
  Attributes map[string]json.RawMesssage
}
```

will panic with ```panic: cannot convert Go values of type map[string]json.RawMessage to parquet value```

Pull request:
Handle Optional Map Fields By Adding Map case in nullIndexFuncOf
The `optional` tag requires handling null value cases for the null index on optional columns.

This PR adds the necessary case statement to handle maps.

Fixes #302 

Patch:
diff --git a/null.go b/null.go
index 1a0edf2..ab0f752 100644
--- a/null.go
+++ b/null.go
@@ -77,6 +77,9 @@ func nullIndexFuncOf(t reflect.Type) nullIndexFunc {
 	case reflect.Slice:
 		return nullIndexSlice
 
+	case reflect.Map:
+		return nullIndexPointer
+
 	case reflect.Array:
 		if t.Elem().Kind() == reflect.Uint8 {
 			switch size := t.Len(); size {


Test patch:
diff --git a/writer_go18_test.go b/writer_go18_test.go
index 69224fc..ca08275 100644
--- a/writer_go18_test.go
+++ b/writer_go18_test.go
@@ -206,3 +206,90 @@ func TestIssue279(t *testing.T) {
 		}
 	}
 }
+
+func TestIssue302(t *testing.T) {
+	tests := []struct {
+		name string
+		fn   func(t *testing.T)
+	}{
+		{
+			name: "SimpleMap",
+			fn: func(t *testing.T) {
+				type M map[string]int
+
+				type T struct {
+					M M `parquet:","`
+				}
+
+				b := new(bytes.Buffer)
+				_ = parquet.NewGenericWriter[T](b)
+
+			},
+		},
+
+		{
+			name: "MapWithValueTag",
+			fn: func(t *testing.T) {
+				type M map[string]int
+
+				type T struct {
+					M M `parquet:"," parquet-value:",zstd"`
+				}
+
+				b := new(bytes.Buffer)
+				_ = parquet.NewGenericWriter[T](b)
+
+			},
+		},
+
+		{
+			name: "MapWithOptionalTag",
+			fn: func(t *testing.T) {
+				type M map[string]int
+
+				type T struct {
+					M M `parquet:",optional"`
+				}
+
+				b := new(bytes.Buffer)
+				w := parquet.NewGenericWriter[T](b)
+				expect := []T{
+					T{
+						M: M{
+							"Holden": 1,
+							"Naomi":  2,
+						},
+					},
+					T{
+						M: nil,
+					},
+					T{
+						M: M{
+							"Naomi":  1,
+							"Holden": 2,
+						},
+					},
+				}
+				_, err := w.Write(expect)
+				if err != nil {
+					t.Fatal(err)
+				}
+				if err = w.Close(); err != nil {
+					t.Fatal(err)
+				}
+
+				bufReader := bytes.NewReader(b.Bytes())
+				r := parquet.NewGenericReader[T](bufReader)
+				values := make([]T, 3)
+				_, err = r.Read(values)
+				if !reflect.DeepEqual(expect, values) {
+					t.Fatalf("values do not match.\n\texpect: %v\n\tactual: %v", expect, values)
+				}
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, test.fn)
+	}
+}