diff --git a/.gitignore b/.gitignore index 9ed3b07..c4cd784 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.test +*.code-workspace + diff --git a/codec.go b/codec.go index 51f4fd7..13fceb0 100644 --- a/codec.go +++ b/codec.go @@ -52,6 +52,8 @@ type Codec struct { schemaCanonical string typeName *name + wrapDefault func(interface{}) (interface{}, error) + nativeFromTextual func([]byte) (interface{}, []byte, error) binaryFromNative func([]byte, interface{}) ([]byte, error) nativeFromBinary func([]byte) (interface{}, []byte, error) diff --git a/examples/ab2t/main.go b/examples/ab2t/main.go index 8e2e855..6349e18 100644 --- a/examples/ab2t/main.go +++ b/examples/ab2t/main.go @@ -9,7 +9,7 @@ import ( "path/filepath" "sync" - "github.com/linkedin/goavro/v2" + "github.com/realityengines/goavro" ) func usage() { diff --git a/examples/nested/main.go b/examples/nested/main.go index 1dccbf9..a49432d 100644 --- a/examples/nested/main.go +++ b/examples/nested/main.go @@ -6,7 +6,7 @@ import ( "os" "reflect" - "github.com/linkedin/goavro/v2" + "github.com/realityengines/goavro" ) var ( @@ -42,12 +42,18 @@ func main() { } fmt.Printf("user in=%+v\n", user) - + fmt.Printf("address in=%+v\n", user.Address) ///Convert Binary From Native binary, err := codec.BinaryFromNative(nil, user.ToStringMap()) if err != nil { panic(err) } + fmt.Println("Textual") + text, err := codec.TextualFromNative(nil, user.ToStringMap()) + if err != nil { + panic(err) + } + fmt.Println(string(text)) ///Convert Native from Binary native, _, err := codec.NativeFromBinary(binary) @@ -58,6 +64,7 @@ func main() { //Convert it back tp Native userOut := StringMapToUser(native.(map[string]interface{})) fmt.Printf("user out=%+v\n", userOut) + fmt.Printf("address out=%+v\n", userOut.Address) if ok := reflect.DeepEqual(user, userOut); !ok { fmt.Fprintf(os.Stderr, "struct Compare Failed ok=%t\n", ok) os.Exit(1) @@ -89,9 +96,9 @@ func (u *User) ToStringMap() map[string]interface{} { } if len(u.Errors) > 0 { - datumIn["Errors"] = goavro.Union("array", u.Errors) + datumIn["Errors"] = u.Errors } else { - datumIn["Errors"] = goavro.Union("null", nil) + datumIn["Errors"] = nil } if u.Address != nil { @@ -102,16 +109,16 @@ func (u *User) ToStringMap() map[string]interface{} { "Zip": int(u.Address.Zip), } if u.Address.Address2 != "" { - addDatum["Address2"] = goavro.Union("string", u.Address.Address2) + addDatum["Address2"] = u.Address.Address2 } else { - addDatum["Address2"] = goavro.Union("null", nil) + addDatum["Address2"] = nil } //important need namespace and record name - datumIn["Address"] = goavro.Union("my.namespace.com.address", addDatum) + datumIn["Address"] = addDatum } else { - datumIn["Address"] = goavro.Union("null", nil) + datumIn["Address"] = nil } return datumIn } @@ -131,38 +138,37 @@ func StringMapToUser(data map[string]interface{}) *User { ind.LastName = value } case "Errors": - if value, ok := v.(map[string]interface{}); ok { - for _, item := range value["array"].([]interface{}) { + if value, ok := v.([]interface{}); ok { + for _, item := range value { ind.Errors = append(ind.Errors, item.(string)) } } case "Address": - if vmap, ok := v.(map[string]interface{}); ok { - //important need namespace and record name - if cookieSMap, ok := vmap["my.namespace.com.address"].(map[string]interface{}); ok { - add := &Address{} - for k, v := range cookieSMap { - switch k { - case "Address1": - if value, ok := v.(string); ok { - add.Address1 = value - } - case "Address2": - if value, ok := v.(string); ok { - add.Address2 = value - } - case "City": - if value, ok := v.(string); ok { - add.City = value - } - case "Zip": - if value, ok := v.(int); ok { - add.Zip = value - } + if cookieSMap, ok := v.(map[string]interface{}); ok { + add := &Address{} + for k, v := range cookieSMap { + switch k { + case "Address1": + if value, ok := v.(string); ok { + add.Address1 = value + } + case "Address2": + if value, ok := v.(string); ok { + add.Address2 = value + } + case "City": + if value, ok := v.(string); ok { + add.City = value + } + case "State": + if value, ok := v.(string); ok { + add.State = value } + case "Zip": + add.Zip = int(v.(int32)) } - ind.Address = add } + ind.Address = add } } diff --git a/examples/soe/main.go b/examples/soe/main.go index a615024..a0a28b7 100644 --- a/examples/soe/main.go +++ b/examples/soe/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/linkedin/goavro" + "github.com/realityengines/goavro" ) func main() { diff --git a/go.mod b/go.mod index 7573301..134f321 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,8 @@ -module github.com/linkedin/goavro/v2 +module github.com/realityengines/goavro go 1.12 -require github.com/golang/snappy v0.0.1 +require ( + github.com/golang/snappy v0.0.1 + github.com/linkedin/goavro/v2 v2.9.7 // indirect +) diff --git a/go.sum b/go.sum index 331e6a1..ac9df57 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,9 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/linkedin/goavro v1.0.5 h1:6ds0AI8upkEoafDk0a5r9q1p/xRtMq47jCilZYEqbmg= +github.com/linkedin/goavro v2.1.0+incompatible h1:DV2aUlj2xZiuxQyvag8Dy7zjY69ENjS66bWkSfdpddY= +github.com/linkedin/goavro v2.1.0+incompatible/go.mod h1:bBCwI2eGYpUI/4820s67MElg9tdeLbINjLjiM2xZFYM= +github.com/linkedin/goavro/v2 v2.9.7 h1:Vd++Rb/RKcmNJjM0HP/JJFMEWa21eUBVKPYlKehOGrM= +github.com/linkedin/goavro/v2 v2.9.7/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA= +gopkg.in/linkedin/goavro.v1 v1.0.5 h1:BJa69CDh0awSsLUmZ9+BowBdokpduDZSM9Zk8oKHfN4= +gopkg.in/linkedin/goavro.v1 v1.0.5/go.mod h1:Aw5GdAbizjOEl0kAMHV9iHmA8reZzW/OKuJAl4Hb9F0= diff --git a/logical_type_test.go b/logical_type_test.go index 6591b51..f75712f 100644 --- a/logical_type_test.go +++ b/logical_type_test.go @@ -43,8 +43,9 @@ func TestTimeStampMillisLogicalTypeEncode(t *testing.T) { func TestTimeStampMillisLogicalTypeUnionEncode(t *testing.T) { schema := `{"type": ["null", {"type": "long", "logicalType": "timestamp-millis"}]}` - testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null long.timestamp-millis]") - testBinaryCodecPass(t, schema, Union("long.timestamp-millis", time.Date(2006, 1, 2, 15, 04, 05, 565000000, time.UTC)), []byte("\x02\xfa\x82\xac\xba\x91\x42")) + testBinaryEncodeFail(t, schema, "test", "cannot transform binary timestamp-millis, expected time.Time, received string") + testBinaryCodecPass(t, schema, nil, []byte("\x00")) + testBinaryCodecPass(t, schema, time.Date(2006, 1, 2, 15, 04, 05, 565000000, time.UTC), []byte("\x02\xfa\x82\xac\xba\x91\x42")) } func TestTimeStampMicrosLogicalTypeEncode(t *testing.T) { @@ -56,8 +57,8 @@ func TestTimeStampMicrosLogicalTypeEncode(t *testing.T) { func TestTimeStampMicrosLogicalTypeUnionEncode(t *testing.T) { schema := `{"type": ["null", {"type": "long", "logicalType": "timestamp-micros"}]}` - testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null long.timestamp-micros]") - testBinaryCodecPass(t, schema, Union("long.timestamp-micros", time.Date(2006, 1, 2, 15, 04, 05, 565283000, time.UTC)), []byte("\x02\xc6\x8d\xf7\xe7\xaf\xd8\x84\x04")) + testBinaryEncodeFail(t, schema, "test", "cannot transform binary timestamp-micros, expected time.Time, received string") + testBinaryCodecPass(t, schema, time.Date(2006, 1, 2, 15, 04, 05, 565283000, time.UTC), []byte("\x02\xc6\x8d\xf7\xe7\xaf\xd8\x84\x04")) } func TestTimeMillisLogicalTypeEncode(t *testing.T) { @@ -69,8 +70,8 @@ func TestTimeMillisLogicalTypeEncode(t *testing.T) { func TestTimeMillisLogicalTypeUnionEncode(t *testing.T) { schema := `{"type": ["null", {"type": "int", "logicalType": "time-millis"}]}` - testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null int.time-millis]") - testBinaryCodecPass(t, schema, Union("int.time-millis", 66904022*time.Millisecond), []byte("\x02\xac\xff\xe6\x3f")) + testBinaryEncodeFail(t, schema, "test", "cannot transform to binary time-millis, expected time.Duration, received string") + testBinaryCodecPass(t, schema, 66904022*time.Millisecond, []byte("\x02\xac\xff\xe6\x3f")) } func TestTimeMicrosLogicalTypeEncode(t *testing.T) { @@ -82,8 +83,8 @@ func TestTimeMicrosLogicalTypeEncode(t *testing.T) { func TestTimeMicrosLogicalTypeUnionEncode(t *testing.T) { schema := `{"type": ["null", {"type": "long", "logicalType": "time-micros"}]}` - testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null long.time-micros]") - testBinaryCodecPass(t, schema, Union("long.time-micros", 66904022566*time.Microsecond), []byte("\x02\xcc\xf8\xd2\xbc\xf2\x03")) + testBinaryEncodeFail(t, schema, "test", "cannot transform to binary time-micros, expected time.Duration, received string") + testBinaryCodecPass(t, schema, 66904022566*time.Microsecond, []byte("\x02\xcc\xf8\xd2\xbc\xf2\x03")) } func TestDateLogicalTypeEncode(t *testing.T) { schema := `{"type": "int", "logicalType": "date"}` @@ -132,7 +133,7 @@ func ExampleUnion_logicalType() { // Note the usage of type.logicalType i.e. `long.timestamp-millis` to denote the type in a union. This is due to the single string naming format // used by goavro. Decimal can be both bytes.decimal or fixed.decimal - bytes, err := codec.BinaryFromNative(nil, map[string]interface{}{"long.timestamp-millis": time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC)}) + bytes, err := codec.BinaryFromNative(nil, time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC)) if err != nil { fmt.Println(err) } @@ -141,7 +142,6 @@ func ExampleUnion_logicalType() { if err != nil { fmt.Println(err) } - out := decoded.(map[string]interface{}) - fmt.Printf("%#v\n", out["long.timestamp-millis"].(time.Time).String()) + fmt.Printf("%#v\n", decoded.(time.Time).String()) // Output: "2006-01-02 15:04:05 +0000 UTC" } diff --git a/record.go b/record.go index 63ad985..0eb9e78 100644 --- a/record.go +++ b/record.go @@ -115,7 +115,11 @@ func makeRecordCodec(st map[string]*Codec, enclosingNamespace string, schemaMap // NOTE: To support record field default values, union schema // set to the type name of first member // TODO: change to schemaCanonical below - defaultValue = Union(fieldCodec.schemaOriginal, defaultValue) + var err error + defaultValue, err = fieldCodec.wrapDefault(defaultValue) + if err != nil { + return nil, err + } default: debug("fieldName: %q; type: %q; defaultValue: %T(%#v)\n", fieldName, c.typeName, defaultValue, defaultValue) } diff --git a/record_test.go b/record_test.go index 21100d6..d82cf96 100644 --- a/record_test.go +++ b/record_test.go @@ -400,10 +400,10 @@ func TestRecordFieldUnionDefaultValue(t *testing.T) { func TestRecordFieldUnionInvalidDefaultValue(t *testing.T) { testSchemaInvalid(t, `{"type":"record","name":"r1","fields":[{"name":"f1","type":["null","int"],"default":13}]}`, - "default value ought to encode using field schema") + "default value ought to encode using first union type") testSchemaInvalid(t, `{"type":"record","name":"r1","fields":[{"name":"f1","type":["int","null"],"default":null}]}`, - "default value ought to encode using field schema") + "default value ought to encode using first union type") } func TestRecordRecursiveRoundTrip(t *testing.T) { @@ -509,12 +509,8 @@ func ExampleBinaryFromNative() { // Convert native Go form to binary Avro data binary, err := codec.BinaryFromNative(nil, map[string]interface{}{ "next": map[string]interface{}{ - "LongList": map[string]interface{}{ - "next": map[string]interface{}{ - "LongList": map[string]interface{}{ - // NOTE: May omit fields when using default value - }, - }, + "next": map[string]interface{}{ + // NOTE: May omit fields when using default value }, }, }) @@ -549,7 +545,7 @@ func ExampleNativeFromBinary() { } fmt.Printf("%v", native) - // Output: map[next:map[LongList:map[next:map[LongList:map[next:]]]]] + // Output: map[next:map[next:map[next:]]] } func ExampleNativeFromTextual() { @@ -575,7 +571,7 @@ func ExampleNativeFromTextual() { } fmt.Printf("%v", native) - // Output: map[next:map[LongList:map[next:map[LongList:map[next:]]]]] + // Output: map[next:map[next:map[next:]]] } func ExampleTextualFromNative() { @@ -595,12 +591,8 @@ func ExampleTextualFromNative() { // Convert native Go form to text Avro data text, err := codec.TextualFromNative(nil, map[string]interface{}{ "next": map[string]interface{}{ - "LongList": map[string]interface{}{ "next": map[string]interface{}{ - "LongList": map[string]interface{}{ - // NOTE: May omit fields when using default value - }, - }, + // NOTE: May omit fields when using default value }, }, }) diff --git a/union.go b/union.go index 1a44cd6..a5df461 100644 --- a/union.go +++ b/union.go @@ -53,12 +53,16 @@ func buildCodecForTypeDescribedBySlice(st map[string]*Codec, enclosingNamespace codecFromName := make(map[string]*Codec, len(schemaArray)) indexFromName := make(map[string]int, len(schemaArray)) + nullIndex := -1 for i, unionMemberSchema := range schemaArray { unionMemberCodec, err := buildCodec(st, enclosingNamespace, unionMemberSchema) if err != nil { return nil, fmt.Errorf("Union item %d ought to be valid Avro type: %s", i+1, err) } fullName := unionMemberCodec.typeName.fullName + if fullName == "null" { + nullIndex = i + } if _, ok := indexFromName[fullName]; ok { return nil, fmt.Errorf("Union item %d ought to be unique type: %s", i+1, unionMemberCodec.typeName) } @@ -68,13 +72,48 @@ func buildCodecForTypeDescribedBySlice(st map[string]*Codec, enclosingNamespace indexFromName[fullName] = i } + isOptional := (nullIndex > -1) && (len(schemaArray) == 2) + + writeBinaryDatum := func(buf []byte, index int, datum interface{}) ([]byte, error) { + c := codecFromIndex[index] + buf, _ = longBinaryFromNative(buf, index) + return c.binaryFromNative(buf, datum) + } + + writeTextDatum := func(buf []byte, index int, key string, datum interface{}) ([]byte, error) { + buf = append(buf, '{') + var err error + buf, err = stringTextualFromNative(buf, key) + if err != nil { + return nil, fmt.Errorf("cannot encode textual union: %s", err) + } + buf = append(buf, ':') + c := codecFromIndex[index] + buf, err = c.textualFromNative(buf, datum) + if err != nil { + return nil, fmt.Errorf("cannot encode textual union: %s", err) + } + return append(buf, '}'), nil + } + return &Codec{ // NOTE: To support record field default values, union schema set to the // type name of first member // TODO: add/change to schemaCanonical below schemaOriginal: codecFromIndex[0].typeName.fullName, + typeName: &name{"union", nullNamespace}, + + wrapDefault: func(defaultValue interface{}) (interface{}, error) { + if isOptional { + if (defaultValue == nil && nullIndex == 1) || + (defaultValue != nil && nullIndex == 0) { + return nil, fmt.Errorf("default value ought to encode using first union type") + } + return defaultValue, nil + } + return Union(codecFromIndex[0].typeName.fullName, defaultValue), nil + }, - typeName: &name{"union", nullNamespace}, nativeFromBinary: func(buf []byte) (interface{}, []byte, error) { var decoded interface{} var err error @@ -92,21 +131,22 @@ func buildCodecForTypeDescribedBySlice(st map[string]*Codec, enclosingNamespace if err != nil { return nil, nil, fmt.Errorf("cannot decode binary union item %d: %s", index+1, err) } - if decoded == nil { + if decoded == nil || isOptional { // do not wrap a nil value in a map - return nil, buf, nil + return decoded, buf, nil } - // Non-nil values are wrapped in a map with single key set to type name of value return Union(allowedTypes[index], decoded), buf, nil }, binaryFromNative: func(buf []byte, datum interface{}) ([]byte, error) { + if isOptional && datum != nil { + return writeBinaryDatum(buf, 1-nullIndex, datum) + } switch v := datum.(type) { case nil: - index, ok := indexFromName["null"] - if !ok { + if nullIndex == -1 { return nil, fmt.Errorf("cannot encode binary union: no member schema types support datum: allowed types: %v; received: %T", allowedTypes, datum) } - return longBinaryFromNative(buf, index) + return longBinaryFromNative(buf, nullIndex) case map[string]interface{}: if len(v) != 1 { return nil, fmt.Errorf("cannot encode binary union: non-nil Union values ought to be specified with Go map[string]interface{}, with single key equal to type name, and value equal to datum value: %v; received: %T", allowedTypes, datum) @@ -117,9 +157,7 @@ func buildCodecForTypeDescribedBySlice(st map[string]*Codec, enclosingNamespace if !ok { return nil, fmt.Errorf("cannot encode binary union: no member schema types support datum: allowed types: %v; received: %T", allowedTypes, datum) } - c := codecFromIndex[index] - buf, _ = longBinaryFromNative(buf, index) - return c.binaryFromNative(buf, value) + return writeBinaryDatum(buf, index, value) } } return nil, fmt.Errorf("cannot encode binary union: non-nil Union values ought to be specified with Go map[string]interface{}, with single key equal to type name, and value equal to datum value: %v; received: %T", allowedTypes, datum) @@ -134,6 +172,14 @@ func buildCodecForTypeDescribedBySlice(st map[string]*Codec, enclosingNamespace var datum interface{} var err error datum, buf, err = genericMapTextDecoder(buf, nil, codecFromName) + if isOptional && datum != nil { + index := 1 - nullIndex + if wrapper, ok := datum.(map[string]interface{}); !ok || len(wrapper) != 1 { + err = fmt.Errorf("expected a map with exactly one element") + } else if datum, ok = wrapper[allowedTypes[index]]; !ok { + err = fmt.Errorf("expected %s in union map", allowedTypes[index]) + } + } if err != nil { return nil, nil, fmt.Errorf("cannot decode textual union: %s", err) } @@ -141,6 +187,10 @@ func buildCodecForTypeDescribedBySlice(st map[string]*Codec, enclosingNamespace return datum, buf, nil }, textualFromNative: func(buf []byte, datum interface{}) ([]byte, error) { + if isOptional && datum != nil { + index := 1 - nullIndex + return writeTextDatum(buf, index, allowedTypes[index], datum) + } switch v := datum.(type) { case nil: _, ok := indexFromName["null"] @@ -158,19 +208,7 @@ func buildCodecForTypeDescribedBySlice(st map[string]*Codec, enclosingNamespace if !ok { return nil, fmt.Errorf("cannot encode textual union: no member schema types support datum: allowed types: %v; received: %T", allowedTypes, datum) } - buf = append(buf, '{') - var err error - buf, err = stringTextualFromNative(buf, key) - if err != nil { - return nil, fmt.Errorf("cannot encode textual union: %s", err) - } - buf = append(buf, ':') - c := codecFromIndex[index] - buf, err = c.textualFromNative(buf, value) - if err != nil { - return nil, fmt.Errorf("cannot encode textual union: %s", err) - } - return append(buf, '}'), nil + return writeTextDatum(buf, index, key, value) } } return nil, fmt.Errorf("cannot encode textual union: non-nil values ought to be specified with Go map[string]interface{}, with single key equal to type name, and value equal to datum value: %v; received: %T", allowedTypes, datum) diff --git a/union_test.go b/union_test.go index b3b6e85..adf7e4f 100644 --- a/union_test.go +++ b/union_test.go @@ -27,11 +27,8 @@ func TestUnion(t *testing.T) { testBinaryCodecPass(t, `["null","int"]`, Union("null", nil), []byte("\x00")) testBinaryCodecPass(t, `["int","null"]`, Union("null", nil), []byte("\x02")) - testBinaryCodecPass(t, `["null","int"]`, Union("int", 3), []byte("\x02\x06")) - testBinaryCodecPass(t, `["null","long"]`, Union("long", 3), []byte("\x02\x06")) - - testBinaryCodecPass(t, `["int","null"]`, Union("int", 3), []byte("\x00\x06")) - testBinaryEncodePass(t, `["int","null"]`, Union("int", 3), []byte("\x00\x06")) // can encode a bare 3 + testBinaryCodecPass(t, `["null","int"]`, 3, []byte("\x02\x06")) + testBinaryCodecPass(t, `["null","long"]`, 3, []byte("\x02\x06")) testBinaryEncodeFail(t, `[{"type":"enum","name":"colors","symbols":["red","green","blue"]},{"type":"enum","name":"animals","symbols":["dog","cat"]}]`, Union("colors", "bravo"), "value ought to be member of symbols") testBinaryEncodeFail(t, `[{"type":"enum","name":"colors","symbols":["red","green","blue"]},{"type":"enum","name":"animals","symbols":["dog","cat"]}]`, Union("animals", "bravo"), "value ought to be member of symbols") @@ -40,9 +37,8 @@ func TestUnion(t *testing.T) { } func TestUnionRejectInvalidType(t *testing.T) { - testBinaryEncodeFailBadDatumType(t, `["null","long"]`, 3) + testBinaryEncodeFailBadDatumType(t, `["null","long"]`, Union("long", 3)) testBinaryEncodeFailBadDatumType(t, `["null","int","long","float"]`, float64(3.5)) - testBinaryEncodeFailBadDatumType(t, `["null","long"]`, Union("int", 3)) testBinaryEncodeFailBadDatumType(t, `["null","int","long","float"]`, Union("double", float64(3.5))) } @@ -58,19 +54,19 @@ func TestUnionNumericCoercionGuardsPrecision(t *testing.T) { } func TestUnionWithArray(t *testing.T) { - testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, Union("null", nil), []byte("\x00")) + testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, nil, []byte("\x00")) - testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, Union("array", []interface{}{}), []byte("\x02\x00")) - testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, Union("array", []interface{}{1}), []byte("\x02\x02\x02\x00")) - testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, Union("array", []interface{}{1, 2}), []byte("\x02\x04\x02\x04\x00")) + testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, []interface{}{}, []byte("\x02\x00")) + testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, []interface{}{1}, []byte("\x02\x02\x02\x00")) + testBinaryCodecPass(t, `["null",{"type":"array","items":"int"}]`, []interface{}{1, 2}, []byte("\x02\x04\x02\x04\x00")) - testBinaryCodecPass(t, `[{"type": "array", "items": "string"}, "null"]`, Union("null", nil), []byte{2}) - testBinaryCodecPass(t, `[{"type": "array", "items": "string"}, "null"]`, Union("array", []string{"foo"}), []byte("\x00\x02\x06foo\x00")) - testBinaryCodecPass(t, `[{"type": "array", "items": "string"}, "null"]`, Union("array", []string{"foo", "bar"}), []byte("\x00\x04\x06foo\x06bar\x00")) + testBinaryCodecPass(t, `[{"type": "array", "items": "string"}, "null"]`, nil, []byte{2}) + testBinaryCodecPass(t, `[{"type": "array", "items": "string"}, "null"]`, []string{"foo"}, []byte("\x00\x02\x06foo\x00")) + testBinaryCodecPass(t, `[{"type": "array", "items": "string"}, "null"]`, []string{"foo", "bar"}, []byte("\x00\x04\x06foo\x06bar\x00")) } func TestUnionWithMap(t *testing.T) { - testBinaryCodecPass(t, `["null",{"type":"map","values":"string"}]`, Union("null", nil), []byte("\x00")) + testBinaryCodecPass(t, `["null",{"type":"map","values":"string"}]`, nil, []byte("\x00")) testBinaryCodecPass(t, `["string",{"type":"map","values":"string"}]`, Union("map", map[string]interface{}{"He": "Helium"}), []byte("\x02\x02\x04He\x0cHelium\x00")) testBinaryCodecPass(t, `["string",{"type":"array","items":"string"}]`, Union("string", "Helium"), []byte("\x00\x0cHelium")) } @@ -145,17 +141,16 @@ func TestUnionRecordFieldWhenNull(t *testing.T) { ] }` - testBinaryCodecPass(t, schema, map[string]interface{}{"f1": Union("array", []interface{}{})}, []byte("\x00\x00")) - testBinaryCodecPass(t, schema, map[string]interface{}{"f1": Union("array", []string{"bar"})}, []byte("\x00\x02\x06bar\x00")) - testBinaryCodecPass(t, schema, map[string]interface{}{"f1": Union("array", []string{})}, []byte("\x00\x00")) - testBinaryCodecPass(t, schema, map[string]interface{}{"f1": Union("null", nil)}, []byte("\x02")) + testBinaryCodecPass(t, schema, map[string]interface{}{"f1": []interface{}{}}, []byte("\x00\x00")) + testBinaryCodecPass(t, schema, map[string]interface{}{"f1": []string{"bar"}}, []byte("\x00\x02\x06bar\x00")) + testBinaryCodecPass(t, schema, map[string]interface{}{"f1": []string{}}, []byte("\x00\x00")) testBinaryCodecPass(t, schema, map[string]interface{}{"f1": nil}, []byte("\x02")) } func TestUnionText(t *testing.T) { testTextEncodeFail(t, `["null","int"]`, Union("null", 3), "expected") - testTextCodecPass(t, `["null","int"]`, Union("null", nil), []byte("null")) - testTextCodecPass(t, `["null","int"]`, Union("int", 3), []byte(`{"int":3}`)) + testTextCodecPass(t, `["null","int"]`, nil, []byte("null")) + testTextCodecPass(t, `["null","int"]`, 3, []byte(`{"int":3}`)) testTextCodecPass(t, `["null","int","string"]`, Union("string", "😂 "), []byte(`{"string":"\u0001\uD83D\uDE02 "}`)) }