Skip to content

Commit

Permalink
Merge pull request #77 from whyrusleeping/feat/omitempty
Browse files Browse the repository at this point in the history
support omitting empty fields on map encoders
  • Loading branch information
whyrusleeping authored Dec 13, 2022
2 parents d523215 + 33755d0 commit c09a31a
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 10 deletions.
79 changes: 69 additions & 10 deletions gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type Field struct {
Type reflect.Type
Pkg string

OmitEmpty bool
IterLabel string

MaxLen int
Expand Down Expand Up @@ -203,13 +204,16 @@ func ParseTypeInfo(i interface{}) (*GenTypeInfo, error) {
usrMaxLen = val
}

_, omitempty := tags["omitempty"]

out.Fields = append(out.Fields, Field{
Name: f.Name,
MapKey: mapk,
Pointer: pointer,
Type: ft,
Pkg: pkg,
MaxLen: usrMaxLen,
Name: f.Name,
MapKey: mapk,
Pointer: pointer,
Type: ft,
Pkg: pkg,
OmitEmpty: omitempty,
MaxLen: usrMaxLen,
})
}

Expand All @@ -231,6 +235,8 @@ func tagparse(v string) (map[string]string, error) {
}

out[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
} else if elem == "omitempty" {
out["omitempty"] = "true"
} else {
out["name"] = elem
}
Expand All @@ -245,7 +251,10 @@ func (gti GenTypeInfo) TupleHeader() []byte {
}

func (gti GenTypeInfo) TupleHeaderAsByteString() string {
h := gti.TupleHeader()
return MakeByteString(gti.TupleHeader())
}

func MakeByteString(h []byte) string {
s := "[]byte{"
for _, b := range h {
s += fmt.Sprintf("%d,", b)
Expand Down Expand Up @@ -1199,33 +1208,77 @@ func GenTupleEncodersForType(gti *GenTypeInfo, w io.Writer) error {
}

func emitCborMarshalStructMap(w io.Writer, gti *GenTypeInfo) error {
var hasOmitEmpty bool
for _, f := range gti.Fields {
if f.OmitEmpty {
hasOmitEmpty = true
}
}

err := doTemplate(w, gti, `func (t *{{ .Name }}) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
cw := cbg.NewCborWriter(w)
`)
if err != nil {
return err
}

if hasOmitEmpty {
fmt.Fprintln(w, "var emptyFieldCount int")
for _, f := range gti.Fields {
if f.OmitEmpty {
err := doTemplate(w, f, `
if t.{{ .Name }} == nil {
emptyFieldCount++
}
`)
if err != nil {
return err
}
}
}

fmt.Fprintf(w, `
if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(%d - emptyFieldCount))); err != nil {
return err
}
`, len(gti.Fields))
if err != nil {
return err
}

} else {

err = doTemplate(w, gti, `
if _, err := cw.Write({{ .MapHeaderAsByteString }}); err != nil {
return err
}
`)
if err != nil {
return err
if err != nil {
return err
}
}

for _, f := range gti.Fields {
fmt.Fprintf(w, "\n\t// t.%s (%s) (%s)", f.Name, f.Type, f.Type.Kind())

if f.OmitEmpty {
if err := doTemplate(w, f, "\nif t.{{.Name}} != nil {\n"); err != nil {
return err
}
}

if err := emitCborMarshalStringField(w, Field{
Name: `"` + f.MapKey + `"`,
}); err != nil {
return err
}

f.Name = "t." + f.Name

switch f.Type.Kind() {
case reflect.String:
if err := emitCborMarshalStringField(w, f); err != nil {
Expand Down Expand Up @@ -1264,6 +1317,12 @@ func emitCborMarshalStructMap(w io.Writer, gti *GenTypeInfo) error {
default:
return fmt.Errorf("field %q of %q has unsupported kind %q", f.Name, gti.Name, f.Type.Kind())
}

if f.OmitEmpty {
if err := doTemplate(w, f, "}\n"); err != nil {
return err
}
}
}

fmt.Fprintf(w, "\treturn nil\n}\n\n")
Expand Down
1 change: 1 addition & 0 deletions testgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func main() {
types.SimpleStructV1{},
types.SimpleStructV2{},
types.RenamedFields{},
types.TestEmpty{},
); err != nil {
panic(err)
}
Expand Down
167 changes: 167 additions & 0 deletions testing/cbor_map_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions testing/roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,4 +378,14 @@ func TestLargeField(t *testing.T) {
}
}

func TestOmitEmpty(t *testing.T) {
et := TestEmpty{
Cat: 167,
}

recepticle := TestEmpty{}

testValueRoundtrip(t, &et, &recepticle)
}

//TODO same for strings
5 changes: 5 additions & 0 deletions testing/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,8 @@ type RenamedFields struct {
type BigField struct {
LargeBytes []byte `cborgen:"maxlen=10000000"`
}

type TestEmpty struct {
Foo *string `cborgen:"omitempty"`
Cat int64
}

0 comments on commit c09a31a

Please sign in to comment.