diff --git a/codec.go b/codec.go index 51f4fd7e..b692bd46 100644 --- a/codec.go +++ b/codec.go @@ -42,6 +42,8 @@ var ( MaxBlockSize = int64(math.MaxInt32) ) +type CodecModifier func(map[string]*Codec) + // Codec supports decoding binary and text Avro data to Go native data types, // and conversely encoding Go native data types to binary or text Avro data. A // Codec is created as a stateless structure that can be safely used in multiple @@ -86,7 +88,7 @@ type Codec struct { // if err != nil { // fmt.Println(err) // } -func NewCodec(schemaSpecification string) (*Codec, error) { +func NewCodec(schemaSpecification string, modifiers ...CodecModifier) (*Codec, error) { var schema interface{} if err := json.Unmarshal([]byte(schemaSpecification), &schema); err != nil { @@ -96,6 +98,10 @@ func NewCodec(schemaSpecification string) (*Codec, error) { // bootstrap a symbol table with primitive type codecs for the new codec st := newSymbolTable() + for _, modifier := range modifiers { + modifier(st) + } + c, err := buildCodec(st, nullNamespace, schema) if err != nil { return nil, err diff --git a/record_test.go b/record_test.go index 21100d61..5d354a69 100644 --- a/record_test.go +++ b/record_test.go @@ -445,6 +445,82 @@ func TestRecordRecursiveRoundTrip(t *testing.T) { } } +func TestUnknownTypeCodecError(t *testing.T) { + _, err := NewCodec(` +{ + "type": "record", + "name": "Parent", + "fields" : [ + {"name": "child", "type": "Child"} + ] +} +`) + ensureError(t, err, "Record \"Parent\" field 1 ought to be valid Avro named type: unknown type name: \"Child\"") +} + +func TestCodecModifier(t *testing.T) { + childCodec, err := NewCodec(` +{ + "type": "record", + "name": "Child", + "fields" : [ + {"name": "age", "type": "int"} + ] +} +`) + ensureError(t, err) + + modifier := func(st map[string]*Codec) { + st["Child"] = childCodec + } + + codec, err := NewCodec(` +{ + "type": "record", + "name": "Parent", + "fields" : [ + {"name": "child", "type": "Child"} + ] +} +`, modifier) + ensureError(t, err) + + child := map[string]interface{} { + "age": 7, + } + parent := map[string]interface{} { + "child": child, + } + + // Convert native Go form to binary Avro data + buf, err := codec.BinaryFromNative(nil, parent) + ensureError(t, err) + + // Convert binary Avro data back to native Go form + datum, _, err := codec.NativeFromBinary(buf) + ensureError(t, err) + + actual, ok := datum.(map[string]interface{}) + if !ok { + t.Fatalf("origin data contaminated") + } + + child, ok = actual["child"].(map[string]interface{}) + if !ok { + t.Fatalf("child type contaminated") + } + + age, ok := child["age"].(int32) + if !ok { + t.Fatalf("child age field contaminated") + } + + if age != 7 { + t.Fatalf("child age data contaminated") + } +} + + func ExampleRecordRecursiveRoundTrip() { codec, err := NewCodec(` {