diff --git a/go.mod b/go.mod index f47eb26..8f617dc 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.18 require ( github.com/fatih/structtag v1.2.0 - github.com/google/go-cmp v0.5.5 + github.com/google/go-cmp v0.6.0 github.com/google/gofuzz v1.0.0 github.com/unravelin/null v2.1.2+incompatible ) require ( github.com/mailru/easyjson v0.7.0 // indirect - github.com/stretchr/testify v1.6.1 // indirect + github.com/stretchr/testify v1.8.2 // indirect ) diff --git a/go.sum b/go.sum index 517f233..237e234 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,10 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= @@ -11,12 +12,15 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/unravelin/null v2.1.2+incompatible h1:JF+JHiWWs8jDsAAwquTKddV+Rq4moaRzfLzeWW3n1CM= github.com/unravelin/null v2.1.2+incompatible/go.mod h1:b0vttVn4a95rTkkj/DT8R6UGjS0Xr+Aw/q7a00kaOM0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/null/null.go b/null/null.go index 9640a60..fe42dc8 100644 --- a/null/null.go +++ b/null/null.go @@ -67,6 +67,12 @@ func (c nullIntCodec) New() unsafe.Pointer { return unsafe.Pointer(&null.Int{}) } +func (c nullIntCodec) Descriptor() plenccodec.Descriptor { + d := c.IntCodec.Descriptor() + d.ExplicitPresence = true + return d +} + type nullBoolCodec struct { plenccodec.BoolCodec } @@ -101,6 +107,12 @@ func (c nullBoolCodec) New() unsafe.Pointer { return unsafe.Pointer(&null.Bool{}) } +func (c nullBoolCodec) Descriptor() plenccodec.Descriptor { + d := c.BoolCodec.Descriptor() + d.ExplicitPresence = true + return d +} + type nullFloatCodec struct { plenccodec.Float64Codec } @@ -136,6 +148,12 @@ func (c nullFloatCodec) New() unsafe.Pointer { return unsafe.Pointer(&null.Float{}) } +func (c nullFloatCodec) Descriptor() plenccodec.Descriptor { + d := c.Float64Codec.Descriptor() + d.ExplicitPresence = true + return d +} + type nullStringCodec struct { plenccodec.StringCodec } @@ -169,6 +187,12 @@ func (c nullStringCodec) New() unsafe.Pointer { return unsafe.Pointer(&null.String{}) } +func (c nullStringCodec) Descriptor() plenccodec.Descriptor { + d := c.StringCodec.Descriptor() + d.ExplicitPresence = true + return d +} + func (nullStringCodec) WithInterning() plenccodec.Codec { c, _ := plenccodec.StringCodec{}.WithInterning().(*plenccodec.InternedStringCodec) return &internedNullStringCodec{ @@ -225,3 +249,9 @@ func (c *nullTimeCodec) Read(data []byte, ptr unsafe.Pointer, wt plenccore.WireT func (c *nullTimeCodec) New() unsafe.Pointer { return unsafe.Pointer(&null.Time{}) } + +func (c nullTimeCodec) Descriptor() plenccodec.Descriptor { + d := c.TimeCodec.Descriptor() + d.ExplicitPresence = true + return d +} diff --git a/plenccodec/descriptor.go b/plenccodec/descriptor.go index 1f98fb4..76377d7 100644 --- a/plenccodec/descriptor.go +++ b/plenccodec/descriptor.go @@ -42,12 +42,20 @@ type Descriptor struct { Name string `plenc:"2"` // Type is the type of the field Type FieldType `plenc:"3"` + // TypeName is used for struct types and is the name of the struct. + TypeName string `plenc:"5"` // Elements is valid for FieldTypeSlice, FieldTypeStruct & FieldTypeMap. For // FieldTypeSlice we expect one entry that describes the elements of the // slice. For FieldTypeStruct we expect an entry for each field in the // struct. For FieldTypeMap we expect two entries. The first is for the key // type and the second is for the map type Elements []Descriptor `plenc:"4"` + + // ExplicitPresence is set if the field has a mechanism to distinguish when + // it is not present. So either a pointer type or something from the null + // package. If this is not set then a missing value indicates the zero + // value, not a null or nil entry. + ExplicitPresence bool `plenc:"6"` } func (d *Descriptor) Read(out Outputter, data []byte) (err error) { diff --git a/plenccodec/map.go b/plenccodec/map.go index e78ada5..699a267 100644 --- a/plenccodec/map.go +++ b/plenccodec/map.go @@ -267,11 +267,20 @@ func (c *MapCodec) Descriptor() Descriptor { vDesc.Index = 2 vDesc.Name = "value" + kTypeName, vTypeName := kDesc.TypeName, vDesc.TypeName + if kTypeName == "" { + kTypeName = kDesc.Type.String() + } + if vTypeName == "" { + vTypeName = vDesc.Type.String() + } + return Descriptor{ Type: FieldTypeSlice, Elements: []Descriptor{ { - Type: FieldTypeStruct, + Type: FieldTypeStruct, + TypeName: fmt.Sprintf("map_%s_%s", kTypeName, vTypeName), Elements: []Descriptor{ kDesc, vDesc, diff --git a/plenccodec/struct.go b/plenccodec/struct.go index 603b382..c658134 100644 --- a/plenccodec/struct.go +++ b/plenccodec/struct.go @@ -232,6 +232,7 @@ func (c *StructCodec) WireType() plenccore.WireType { func (c *StructCodec) Descriptor() Descriptor { var d Descriptor d.Type = FieldTypeStruct + d.TypeName = c.rtype.Name() d.Elements = make([]Descriptor, len(c.fields)) for i, f := range c.fields { d.Elements[i] = f.codec.Descriptor() diff --git a/plenccodec/struct_test.go b/plenccodec/struct_test.go index c62f7f0..577d157 100644 --- a/plenccodec/struct_test.go +++ b/plenccodec/struct_test.go @@ -2,10 +2,12 @@ package plenccodec_test import ( "fmt" + "reflect" "testing" "github.com/google/go-cmp/cmp" "github.com/philpearl/plenc" + "github.com/philpearl/plenc/plenccodec" ) func TestFieldRemoval(t *testing.T) { @@ -188,3 +190,39 @@ func TestZeroReuse(t *testing.T) { t.Fatal(diff) } } + +func TestStructDescriptor(t *testing.T) { + type s2 struct { + A string `plenc:"1"` + } + type s1 struct { + A int `plenc:"1,flat"` + B int `plenc:"2"` + C s2 `plenc:"3"` + } + + c, err := plenc.CodecForType(reflect.TypeOf(s1{})) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(plenccodec.Descriptor{ + TypeName: "s1", + Type: plenccodec.FieldTypeStruct, + Elements: []plenccodec.Descriptor{ + {Index: 1, Name: "A", Type: plenccodec.FieldTypeFlatInt}, + {Index: 2, Name: "B", Type: plenccodec.FieldTypeInt}, + { + Index: 3, + Name: "C", + Type: plenccodec.FieldTypeStruct, + TypeName: "s2", + Elements: []plenccodec.Descriptor{ + {Index: 1, Name: "A", Type: plenccodec.FieldTypeString}, + }, + }, + }, + }, c.Descriptor()); diff != "" { + t.Fatal(diff) + } +} diff --git a/plenccodec/wrapper.go b/plenccodec/wrapper.go index 7e5d2e8..f2c094a 100644 --- a/plenccodec/wrapper.go +++ b/plenccodec/wrapper.go @@ -36,7 +36,9 @@ func (p PointerWrapper) WireType() plenccore.WireType { } func (p PointerWrapper) Descriptor() Descriptor { - return p.Underlying.Descriptor() + d := p.Underlying.Descriptor() + d.ExplicitPresence = true + return d } func (p PointerWrapper) Size(ptr unsafe.Pointer, tag []byte) int {