gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit f5750264fb7b954483282400685ca5a75d6f5537
parent 1492d642246881248a5e9466fa81e3362d1067e7
Author: francoispqt <francois@parquet.ninja>
Date:   Mon, 18 Jun 2018 21:21:03 +0800

refactor generator, add tests and tag handling

Diffstat:
M.travis.yml | 1+
Mgojay/Makefile | 12++++++++++--
Mgojay/gen.go | 34+++++++++++++++++++++++++---------
Mgojay/gen_array.go | 2+-
Mgojay/gen_array_marshal.go | 52++++++++++++++++++++++++++--------------------------
Agojay/gen_array_test.go | 366+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgojay/gen_array_unmarshal.go | 48++++++++++++++++++++++++------------------------
Agojay/gen_parse.go | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgojay/gen_struct_marshal.go | 113++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mgojay/gen_struct_marshal_tpl.go | 3+++
Agojay/gen_struct_test.go | 373+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgojay/gen_struct_unmarshal.go | 156++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mgojay/gen_struct_unmarshal_tpl.go | 14++++++++++++--
Mgojay/gen_stuct.go | 14+++++++++++++-
Agojay/gen_tag.go | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_test.go | 24++++++++++++++++++++++++
Mgojay/main.go | 78++++++++++++++++++++++++++++++++++++++----------------------------------------
Dgojay/tests/basic_slices_gojay.go | 113-------------------------------------------------------------------------------
Mgojay/tests/basic_structs.go | 2+-
Dgojay/tests/basic_structs_gojay.go | 149-------------------------------------------------------------------------------
Mgojay/visitor.go | 35+++++------------------------------
21 files changed, 1210 insertions(+), 497 deletions(-)

diff --git a/.travis.yml b/.travis.yml @@ -6,6 +6,7 @@ go: script: - go get github.com/stretchr/testify + - go test ./gojay -race - go test -race -coverprofile=coverage.txt -covermode=atomic after_success: diff --git a/gojay/Makefile b/gojay/Makefile @@ -1,3 +1,11 @@ .PHONY: build build: - go build ./ -\ No newline at end of file + go build ./ + +.PHONY: test +test: + go test -race . + +.PHONY: cover +cover: + go test -tags test -coverprofile=profile.out +\ No newline at end of file diff --git a/gojay/gen.go b/gojay/gen.go @@ -3,17 +3,20 @@ package main import ( "go/ast" "log" - "os" + "strings" "text/template" ) +const gojayAnnotation = "//gojay:json" const genFileSuffix = "_gojay.go" var pkgTpl *template.Template var gojayImport = []byte("import \"github.com/francoispqt/gojay\"\n") -type gen struct { - f *os.File +type Gen struct { + b *strings.Builder + pkg string + src string genTypes map[string]*ast.TypeSpec vis *vis } @@ -44,8 +47,18 @@ func parseTemplates(tpls templateList, pfx string) { } } -func (g *gen) writePkg(pkg string) error { - err := pkgTpl.Execute(g.f, struct { +// NewGen returns a new generator +func NewGen(p string) *Gen { + g := &Gen{ + src: p, + b: &strings.Builder{}, + genTypes: make(map[string]*ast.TypeSpec), + } + return g +} + +func (g *Gen) writePkg(pkg string) error { + err := pkgTpl.Execute(g.b, struct { PkgName string }{ PkgName: pkg, @@ -56,17 +69,17 @@ func (g *gen) writePkg(pkg string) error { return nil } -func (g *gen) writeGojayImport() error { - _, err := g.f.Write(gojayImport) +func (g *Gen) writeGojayImport() error { + _, err := g.b.Write(gojayImport) if err != nil { return err } return nil } -func (g *gen) gen(pkg string) error { +func (g *Gen) Gen() error { // write package - err := g.writePkg(pkg) + err := g.writePkg(g.pkg) if err != nil { return err } @@ -79,16 +92,19 @@ func (g *gen) gen(pkg string) error { // generate interfaces implementations based on type for _, s := range g.genTypes { switch t := s.Type.(type) { + // is struct case *ast.StructType: err = g.genStruct(s.Name.String(), t) if err != nil { return err } + // is array case *ast.ArrayType: err = g.genArray(s.Name.String(), t) if err != nil { return err } + // is map case *ast.MapType: // TODO: generate for map type } diff --git a/gojay/gen_array.go b/gojay/gen_array.go @@ -4,7 +4,7 @@ import ( "go/ast" ) -func (g *gen) genArray(n string, s *ast.ArrayType) error { +func (g *Gen) genArray(n string, s *ast.ArrayType) error { err := g.arrGenUnmarshal(n, s) if err != nil { return err diff --git a/gojay/gen_array_marshal.go b/gojay/gen_array_marshal.go @@ -7,8 +7,8 @@ import ( func init() {} -func (g *gen) arrGenIsNil(n string) error { - err := arrMarshalTpl["isNil"].tpl.Execute(g.f, struct { +func (g *Gen) arrGenIsNil(n string) error { + err := arrMarshalTpl["isNil"].tpl.Execute(g.b, struct { TypeName string }{ TypeName: n, @@ -16,8 +16,8 @@ func (g *gen) arrGenIsNil(n string) error { return err } -func (g *gen) arrGenMarshal(n string, s *ast.ArrayType) error { - err := arrMarshalTpl["def"].tpl.Execute(g.f, struct { +func (g *Gen) arrGenMarshal(n string, s *ast.ArrayType) error { + err := arrMarshalTpl["def"].tpl.Execute(g.b, struct { TypeName string }{ TypeName: n, @@ -43,14 +43,14 @@ func (g *gen) arrGenMarshal(n string, s *ast.ArrayType) error { return errors.New("Unknown type") } } - _, err = g.f.Write([]byte("}\n")) + _, err = g.b.Write([]byte("}\n")) if err != nil { return err } return err } -func (g *gen) arrGenMarshalIdent(i *ast.Ident, ptr bool) error { +func (g *Gen) arrGenMarshalIdent(i *ast.Ident, ptr bool) error { switch i.String() { case "string": return g.arrMarshalString(ptr) @@ -76,7 +76,7 @@ func (g *gen) arrGenMarshalIdent(i *ast.Ident, ptr bool) error { return g.arrMarshalUint("8", ptr) default: // if ident is already in our spec list - if sp, ok := g.vis.specs[i.Name]; ok { + if sp, ok := g.genTypes[i.Name]; ok { return g.arrMarshalNonPrim(sp, ptr) } else if i.Obj != nil { // else check the obj infos @@ -91,7 +91,7 @@ func (g *gen) arrGenMarshalIdent(i *ast.Ident, ptr bool) error { } } -func (g *gen) arrMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { +func (g *Gen) arrMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { switch sp.Type.(type) { case *ast.StructType: return g.arrMarshalStruct(sp, ptr) @@ -101,16 +101,16 @@ func (g *gen) arrMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { return nil } -func (g *gen) arrMarshalString(ptr bool) error { +func (g *Gen) arrMarshalString(ptr bool) error { if ptr { - err := arrMarshalTpl["stringPtr"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["stringPtr"].tpl.Execute(g.b, struct { Ptr string }{""}) if err != nil { return err } } else { - err := arrMarshalTpl["string"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["string"].tpl.Execute(g.b, struct { Ptr string }{"&"}) if err != nil { @@ -120,16 +120,16 @@ func (g *gen) arrMarshalString(ptr bool) error { return nil } -func (g *gen) arrMarshalBool(ptr bool) error { +func (g *Gen) arrMarshalBool(ptr bool) error { if ptr { - err := arrMarshalTpl["bool"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["bool"].tpl.Execute(g.b, struct { Ptr string }{""}) if err != nil { return err } } else { - err := arrMarshalTpl["bool"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["bool"].tpl.Execute(g.b, struct { Ptr string }{"&"}) if err != nil { @@ -139,9 +139,9 @@ func (g *gen) arrMarshalBool(ptr bool) error { return nil } -func (g *gen) arrMarshalInt(intLen string, ptr bool) error { +func (g *Gen) arrMarshalInt(intLen string, ptr bool) error { if ptr { - err := arrMarshalTpl["int"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["int"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, ""}) @@ -149,7 +149,7 @@ func (g *gen) arrMarshalInt(intLen string, ptr bool) error { return err } } else { - err := arrMarshalTpl["int"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["int"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, "&"}) @@ -160,9 +160,9 @@ func (g *gen) arrMarshalInt(intLen string, ptr bool) error { return nil } -func (g *gen) arrMarshalUint(intLen string, ptr bool) error { +func (g *Gen) arrMarshalUint(intLen string, ptr bool) error { if ptr { - err := arrMarshalTpl["uint"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["uint"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, ""}) @@ -170,7 +170,7 @@ func (g *gen) arrMarshalUint(intLen string, ptr bool) error { return err } } else { - err := arrMarshalTpl["uint"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["uint"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, "&"}) @@ -181,16 +181,16 @@ func (g *gen) arrMarshalUint(intLen string, ptr bool) error { return nil } -func (g *gen) arrMarshalStruct(st *ast.TypeSpec, ptr bool) error { +func (g *Gen) arrMarshalStruct(st *ast.TypeSpec, ptr bool) error { if ptr { - err := arrMarshalTpl["structPtr"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["structPtr"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { return err } } else { - err := arrMarshalTpl["struct"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["struct"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { @@ -200,16 +200,16 @@ func (g *gen) arrMarshalStruct(st *ast.TypeSpec, ptr bool) error { return nil } -func (g *gen) arrMarshalArr(st *ast.TypeSpec, ptr bool) error { +func (g *Gen) arrMarshalArr(st *ast.TypeSpec, ptr bool) error { if ptr { - err := arrMarshalTpl["arrPtr"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["arrPtr"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { return err } } else { - err := arrMarshalTpl["arr"].tpl.Execute(g.f, struct { + err := arrMarshalTpl["arr"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { diff --git a/gojay/gen_array_test.go b/gojay/gen_array_test.go @@ -0,0 +1,366 @@ +package main + +import ( + "io" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGenArray(t *testing.T) { + testCases := map[string]struct { + input io.Reader + expectedResult string + }{ + "basicStringSlice": { + input: strings.NewReader(`package test + +//gojay:json +type StrSlice []string + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *StrSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var str string + if err := dec.String(&str); err != nil { + return err + } + *v = append(*v, str) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *StrSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.String(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *StrSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicStringBool": { + input: strings.NewReader(`package test + +//gojay:json +type BoolSlice []bool + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *BoolSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var b bool + if err := dec.Bool(&b); err != nil { + return err + } + *v = append(*v, b) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *BoolSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Bool(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *BoolSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicIntSlice": { + input: strings.NewReader(`package test + +//gojay:json +type IntSlice []int + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var i int + if err := dec.Int(&i); err != nil { + return err + } + *v = append(*v, i) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Int(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *IntSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicInt8Slice": { + input: strings.NewReader(`package test + + //gojay:json + type IntSlice []int8 + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var i int8 + if err := dec.Int8(&i); err != nil { + return err + } + *v = append(*v, i) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Int8(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *IntSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicInt16Slice": { + input: strings.NewReader(`package test + +//gojay:json +type IntSlice []int16 + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var i int16 + if err := dec.Int16(&i); err != nil { + return err + } + *v = append(*v, i) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Int16(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *IntSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicInt32Slice": { + input: strings.NewReader(`package test + + //gojay:json + type IntSlice []int32 + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var i int32 + if err := dec.Int32(&i); err != nil { + return err + } + *v = append(*v, i) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Int32(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *IntSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicInt64Slice": { + input: strings.NewReader(`package test + + //gojay:json + type IntSlice []int64 + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var i int64 + if err := dec.Int64(&i); err != nil { + return err + } + *v = append(*v, i) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Int64(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *IntSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicUint64Slice": { + input: strings.NewReader(`package test + + //gojay:json + type IntSlice []uint64 + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var i uint64 + if err := dec.Uint64(&i); err != nil { + return err + } + *v = append(*v, i) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Uint64(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *IntSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicStructSlice": { + input: strings.NewReader(`package test + +//gojay:json +type StructSlice []*Struct + +type Struct struct{ + Str string +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *StructSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var s = &Struct{} + if err := dec.Object(s); err != nil { + return err + } + *v = append(*v, s) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *StructSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Object(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *StructSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + "basicSliceSlice": { + input: strings.NewReader(`package test + +//gojay:json +type SliceStrSlice []StrSlice + +type StrSlice []string + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray +func (v *SliceStrSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { + var s = make(StrSlice, 0) + if err := dec.Array(&s); err != nil { + return err + } + *v = append(*v, s) + return nil +} + +// MarshalJSONArray implements gojay's MarshalerJSONArray +func (v *SliceStrSlice) MarshalJSONArray(enc *gojay.Encoder) { + for _, s := range *v { + enc.Array(s) + } +} + +// IsNil implements gojay's MarshalerJSONArray +func (v *SliceStrSlice) IsNil() bool { + return *v == nil || len(*v) == 0 +} +`, + }, + } + for n, testCase := range testCases { + t.Run(n, func(t *testing.T) { + g, err := MakeGenFromReader(testCase.input) + if err != nil { + t.Fatal(err) + } + err = g.Gen() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, testCase.expectedResult, g.b.String()) + }) + } +} diff --git a/gojay/gen_array_unmarshal.go b/gojay/gen_array_unmarshal.go @@ -5,8 +5,8 @@ import ( "go/ast" ) -func (g *gen) arrGenUnmarshal(n string, s *ast.ArrayType) error { - err := arrUnmarshalTpl["def"].tpl.Execute(g.f, struct { +func (g *Gen) arrGenUnmarshal(n string, s *ast.ArrayType) error { + err := arrUnmarshalTpl["def"].tpl.Execute(g.b, struct { TypeName string }{ TypeName: n, @@ -32,14 +32,14 @@ func (g *gen) arrGenUnmarshal(n string, s *ast.ArrayType) error { return errors.New("Unknown type") } } - _, err = g.f.Write(structUnmarshalClose) + _, err = g.b.Write(structUnmarshalClose) if err != nil { return err } return err } -func (g *gen) arrGenUnmarshalIdent(i *ast.Ident, ptr bool) error { +func (g *Gen) arrGenUnmarshalIdent(i *ast.Ident, ptr bool) error { switch i.String() { case "string": return g.arrUnmarshalString(ptr) @@ -65,7 +65,7 @@ func (g *gen) arrGenUnmarshalIdent(i *ast.Ident, ptr bool) error { return g.arrUnmarshalUint("8", ptr) default: // if ident is already in our spec list - if sp, ok := g.vis.specs[i.Name]; ok { + if sp, ok := g.genTypes[i.Name]; ok { return g.arrUnmarshalNonPrim(sp, ptr) } else if i.Obj != nil { // else check the obj infos @@ -80,7 +80,7 @@ func (g *gen) arrGenUnmarshalIdent(i *ast.Ident, ptr bool) error { } } -func (g *gen) arrUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { +func (g *Gen) arrUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { switch sp.Type.(type) { case *ast.StructType: return g.arrUnmarshalStruct(sp, ptr) @@ -90,16 +90,16 @@ func (g *gen) arrUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { return nil } -func (g *gen) arrUnmarshalString(ptr bool) error { +func (g *Gen) arrUnmarshalString(ptr bool) error { if ptr { - err := arrUnmarshalTpl["stringPtr"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["stringPtr"].tpl.Execute(g.b, struct { Ptr string }{""}) if err != nil { return err } } else { - err := arrUnmarshalTpl["string"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["string"].tpl.Execute(g.b, struct { Ptr string }{"&"}) if err != nil { @@ -109,16 +109,16 @@ func (g *gen) arrUnmarshalString(ptr bool) error { return nil } -func (g *gen) arrUnmarshalBool(ptr bool) error { +func (g *Gen) arrUnmarshalBool(ptr bool) error { if ptr { - err := arrUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["bool"].tpl.Execute(g.b, struct { Ptr string }{""}) if err != nil { return err } } else { - err := arrUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["bool"].tpl.Execute(g.b, struct { Ptr string }{"&"}) if err != nil { @@ -128,9 +128,9 @@ func (g *gen) arrUnmarshalBool(ptr bool) error { return nil } -func (g *gen) arrUnmarshalInt(intLen string, ptr bool) error { +func (g *Gen) arrUnmarshalInt(intLen string, ptr bool) error { if ptr { - err := arrUnmarshalTpl["int"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["int"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, ""}) @@ -138,7 +138,7 @@ func (g *gen) arrUnmarshalInt(intLen string, ptr bool) error { return err } } else { - err := arrUnmarshalTpl["int"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["int"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, "&"}) @@ -149,9 +149,9 @@ func (g *gen) arrUnmarshalInt(intLen string, ptr bool) error { return nil } -func (g *gen) arrUnmarshalUint(intLen string, ptr bool) error { +func (g *Gen) arrUnmarshalUint(intLen string, ptr bool) error { if ptr { - err := arrUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["uint"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, ""}) @@ -159,7 +159,7 @@ func (g *gen) arrUnmarshalUint(intLen string, ptr bool) error { return err } } else { - err := arrUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["uint"].tpl.Execute(g.b, struct { IntLen string Ptr string }{intLen, "&"}) @@ -170,16 +170,16 @@ func (g *gen) arrUnmarshalUint(intLen string, ptr bool) error { return nil } -func (g *gen) arrUnmarshalStruct(st *ast.TypeSpec, ptr bool) error { +func (g *Gen) arrUnmarshalStruct(st *ast.TypeSpec, ptr bool) error { if ptr { - err := arrUnmarshalTpl["structPtr"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { return err } } else { - err := arrUnmarshalTpl["struct"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["struct"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { @@ -189,16 +189,16 @@ func (g *gen) arrUnmarshalStruct(st *ast.TypeSpec, ptr bool) error { return nil } -func (g *gen) arrUnmarshalArr(st *ast.TypeSpec, ptr bool) error { +func (g *Gen) arrUnmarshalArr(st *ast.TypeSpec, ptr bool) error { if ptr { - err := arrUnmarshalTpl["arrPtr"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["arrPtr"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { return err } } else { - err := arrUnmarshalTpl["arr"].tpl.Execute(g.f, struct { + err := arrUnmarshalTpl["arr"].tpl.Execute(g.b, struct { StructName string }{st.Name.String()}) if err != nil { diff --git a/gojay/gen_parse.go b/gojay/gen_parse.go @@ -0,0 +1,63 @@ +//+build !test + +package main + +import ( + "go/ast" + "go/parser" + "go/token" + "os" +) + +func (g *Gen) parse() error { + var f, err = os.Stat(g.src) + if err != nil { + return err + } + if f.IsDir() { + err = g.parseDir() + } else { + err = g.parseFile() + } + return err +} + +func (g *Gen) parseDir() error { + // parse the given path + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, g.src, nil, parser.ParseComments) + if err != nil { + return err + } + // range across packages + for pkgName, pkg := range pkgs { + v := NewVisitor(g, pkgName) + g.pkg = pkgName + // range on files in package + for _, f := range pkg.Files { + ast.Walk(v, f) + if err != nil { + return err + } + } + g.vis = v + } + return nil +} + +func (g *Gen) parseFile() error { + // parse the given path + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, g.src, nil, parser.ParseComments) + if err != nil { + return err + } + g.pkg = f.Name.Name + v := NewVisitor(g, g.pkg) + ast.Walk(v, f) + if err != nil { + return err + } + g.vis = v + return nil +} diff --git a/gojay/gen_struct_marshal.go b/gojay/gen_struct_marshal.go @@ -3,11 +3,10 @@ package main import ( "errors" "go/ast" - "strings" ) -func (g *gen) structGenIsNil(n string) error { - err := structMarshalTpl["isNil"].tpl.Execute(g.f, struct { +func (g *Gen) structGenIsNil(n string) error { + err := structMarshalTpl["isNil"].tpl.Execute(g.b, struct { StructName string }{ StructName: n, @@ -15,8 +14,8 @@ func (g *gen) structGenIsNil(n string) error { return err } -func (g *gen) structGenMarshalObj(n string, s *ast.StructType) (int, error) { - err := structMarshalTpl["def"].tpl.Execute(g.f, struct { +func (g *Gen) structGenMarshalObj(n string, s *ast.StructType) (int, error) { + err := structMarshalTpl["def"].tpl.Execute(g.b, struct { StructName string }{ StructName: n, @@ -28,6 +27,10 @@ func (g *gen) structGenMarshalObj(n string, s *ast.StructType) (int, error) { if len(s.Fields.List) > 0 { // TODO: check tags for _, field := range s.Fields.List { + // check if has hide tag + if field.Tag != nil && hasTagMarshalHide(field.Tag) { + continue + } switch t := field.Type.(type) { case *ast.Ident: var err error @@ -49,85 +52,93 @@ func (g *gen) structGenMarshalObj(n string, s *ast.StructType) (int, error) { } } } - _, err = g.f.Write([]byte("}\n")) + _, err = g.b.Write([]byte("}\n")) if err != nil { return 0, err } return keys, nil } -func (g *gen) structGenMarshalIdent(field *ast.Field, i *ast.Ident, keys int, ptr bool) (int, error) { +func (g *Gen) structGenMarshalIdent(field *ast.Field, i *ast.Ident, keys int, ptr bool) (int, error) { + var keyV = getStructFieldJSONKey(field) + switch i.String() { case "string": - var err = g.structMarshalString(field, ptr) + var err = g.structMarshalString(field, keyV, ptr) if err != nil { return 0, err } keys++ case "bool": - var err = g.structMarshalBool(field, ptr) + var err = g.structMarshalBool(field, keyV, ptr) if err != nil { return 0, err } keys++ case "int": - var err = g.structMarshalInt(field, "", ptr) + var err = g.structMarshalInt(field, keyV, "", ptr) if err != nil { return 0, err } keys++ case "int64": - var err = g.structMarshalInt(field, "64", ptr) + var err = g.structMarshalInt(field, keyV, "64", ptr) if err != nil { return 0, err } keys++ case "int32": - var err = g.structMarshalInt(field, "32", ptr) + var err = g.structMarshalInt(field, keyV, "32", ptr) if err != nil { return 0, err } keys++ case "int16": - var err = g.structMarshalInt(field, "16", ptr) + var err = g.structMarshalInt(field, keyV, "16", ptr) if err != nil { return 0, err } keys++ case "int8": - var err = g.structMarshalInt(field, "8", ptr) + var err = g.structMarshalInt(field, keyV, "8", ptr) if err != nil { return 0, err } keys++ case "uint64": - var err = g.structMarshalUint(field, "64", ptr) + var err = g.structMarshalUint(field, keyV, "64", ptr) if err != nil { return 0, err } keys++ case "uint32": - var err = g.structMarshalUint(field, "32", ptr) + var err = g.structMarshalUint(field, keyV, "32", ptr) if err != nil { return 0, err } keys++ case "uint16": - var err = g.structMarshalUint(field, "16", ptr) + var err = g.structMarshalUint(field, keyV, "16", ptr) if err != nil { return 0, err } keys++ case "uint8": - var err = g.structMarshalUint(field, "8", ptr) + var err = g.structMarshalUint(field, keyV, "8", ptr) + if err != nil { + return 0, err + } + keys++ + case "float64": + var err = g.structMarshalFloat(field, keyV, "", ptr) if err != nil { return 0, err } keys++ default: // if ident is already in our spec list - if sp, ok := g.vis.specs[i.Name]; ok { - err := g.structMarshalNonPrim(field, sp, ptr) + if sp, ok := g.genTypes[i.Name]; ok { + err := g.structMarshalNonPrim(field, keyV, sp, ptr) if err != nil { return 0, err } @@ -135,7 +146,7 @@ func (g *gen) structGenMarshalIdent(field *ast.Field, i *ast.Ident, keys int, pt } else if i.Obj != nil { switch t := i.Obj.Decl.(type) { case *ast.TypeSpec: - var err = g.structMarshalNonPrim(field, t, ptr) + var err = g.structMarshalNonPrim(field, keyV, t, ptr) if err != nil { return 0, err } @@ -150,114 +161,132 @@ func (g *gen) structGenMarshalIdent(field *ast.Field, i *ast.Ident, keys int, pt return keys, nil } -func (g *gen) structMarshalNonPrim(field *ast.Field, sp *ast.TypeSpec, ptr bool) error { +func (g *Gen) structMarshalNonPrim(field *ast.Field, keyV string, sp *ast.TypeSpec, ptr bool) error { switch sp.Type.(type) { case *ast.StructType: - return g.structMarshalStruct(field, sp, ptr) + return g.structMarshalStruct(field, keyV, sp, ptr) case *ast.ArrayType: - return g.structMarshalArr(field, sp, ptr) + return g.structMarshalArr(field, keyV, sp, ptr) } return nil } -func (g *gen) structMarshalString(field *ast.Field, ptr bool) error { +func (g *Gen) structMarshalString(field *ast.Field, keyV string, ptr bool) error { key := field.Names[0].String() ptrStr := "" if ptr { ptrStr = "*" } - err := structMarshalTpl["string"].tpl.Execute(g.f, struct { + err := structMarshalTpl["string"].tpl.Execute(g.b, struct { Field string Key string Ptr string - }{key, strings.ToLower(key), ptrStr}) + }{key, keyV, ptrStr}) if err != nil { return err } return nil } -func (g *gen) structMarshalBool(field *ast.Field, ptr bool) error { +func (g *Gen) structMarshalBool(field *ast.Field, keyV string, ptr bool) error { key := field.Names[0].String() ptrStr := "" if ptr { ptrStr = "*" } - err := structMarshalTpl["bool"].tpl.Execute(g.f, struct { + err := structMarshalTpl["bool"].tpl.Execute(g.b, struct { Field string Key string Ptr string - }{key, strings.ToLower(key), ptrStr}) + }{key, keyV, ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *Gen) structMarshalInt(field *ast.Field, keyV string, intLen string, ptr bool) error { + key := field.Names[0].String() + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := structMarshalTpl["int"].tpl.Execute(g.b, struct { + Field string + IntLen string + Key string + Ptr string + }{key, intLen, keyV, ptrStr}) if err != nil { return err } return nil } -func (g *gen) structMarshalInt(field *ast.Field, intLen string, ptr bool) error { +func (g *Gen) structMarshalUint(field *ast.Field, keyV string, intLen string, ptr bool) error { key := field.Names[0].String() ptrStr := "" if ptr { ptrStr = "*" } - err := structMarshalTpl["int"].tpl.Execute(g.f, struct { + err := structMarshalTpl["uint"].tpl.Execute(g.b, struct { Field string IntLen string Key string Ptr string - }{key, intLen, strings.ToLower(key), ptrStr}) + }{key, intLen, keyV, ptrStr}) if err != nil { return err } return nil } -func (g *gen) structMarshalUint(field *ast.Field, intLen string, ptr bool) error { +func (g *Gen) structMarshalFloat(field *ast.Field, keyV string, intLen string, ptr bool) error { key := field.Names[0].String() ptrStr := "" if ptr { ptrStr = "*" } - err := structMarshalTpl["uint"].tpl.Execute(g.f, struct { + err := structMarshalTpl["float"].tpl.Execute(g.b, struct { Field string IntLen string Key string Ptr string - }{key, intLen, strings.ToLower(key), ptrStr}) + }{key, intLen, keyV, ptrStr}) if err != nil { return err } return nil } -func (g *gen) structMarshalStruct(field *ast.Field, st *ast.TypeSpec, ptr bool) error { +func (g *Gen) structMarshalStruct(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) error { key := field.Names[0].String() ptrStr := "" if ptr { ptrStr = "*" } - var err = structMarshalTpl["struct"].tpl.Execute(g.f, struct { + var err = structMarshalTpl["struct"].tpl.Execute(g.b, struct { Key string Field string Ptr string - }{strings.ToLower(key), key, ptrStr}) + }{keyV, key, ptrStr}) if err != nil { return err } return nil } -func (g *gen) structMarshalArr(field *ast.Field, st *ast.TypeSpec, ptr bool) error { +func (g *Gen) structMarshalArr(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) error { key := field.Names[0].String() ptrStr := "" if ptr { ptrStr = "*" } - var err = structMarshalTpl["arr"].tpl.Execute(g.f, struct { + var err = structMarshalTpl["arr"].tpl.Execute(g.b, struct { Key string Field string Ptr string - }{strings.ToLower(key), key, ptrStr}) + }{keyV, key, ptrStr}) if err != nil { return err } diff --git a/gojay/gen_struct_marshal_tpl.go b/gojay/gen_struct_marshal_tpl.go @@ -20,6 +20,9 @@ func (v *{{.StructName}}) IsNil() bool { return v == nil } "uint": &genTpl{ strTpl: "\tenc.Uint{{.IntLen}}Key(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", }, + "float": &genTpl{ + strTpl: "\tenc.Float{{.IntLen}}Key(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", + }, "bool": &genTpl{ strTpl: "\tenc.BoolKey(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", }, diff --git a/gojay/gen_struct_test.go b/gojay/gen_struct_test.go @@ -0,0 +1,373 @@ +package main + +import ( + "io" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGenStruct(t *testing.T) { + testCases := map[string]struct { + input io.Reader + expectedResult string + }{ + "basicStruct": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + Uint64 uint64 + Float float64 + Str string + Bool bool +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + case "int": + return dec.Int(&v.Int) + case "int8": + return dec.Int8(&v.Int8) + case "int16": + return dec.Int16(&v.Int16) + case "int32": + return dec.Int32(&v.Int32) + case "int64": + return dec.Int64(&v.Int64) + case "uint64": + return dec.Uint64(&v.Uint64) + case "float": + return dec.Float(&v.Float) + case "str": + return dec.String(&v.Str) + case "bool": + return dec.Bool(&v.Bool) + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 9 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("int", v.Int) + enc.Int8Key("int8", v.Int8) + enc.Int16Key("int16", v.Int16) + enc.Int32Key("int32", v.Int32) + enc.Int64Key("int64", v.Int64) + enc.Uint64Key("uint64", v.Uint64) + enc.FloatKey("float", v.Float) + enc.StringKey("str", v.Str) + enc.BoolKey("bool", v.Bool) +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "basicStructPtr": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Int *int + Str *string +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + case "int": + return dec.Int(v.Int) + case "str": + return dec.String(v.Str) + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 2 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("int", *v.Int) + enc.StringKey("str", *v.Str) +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "basicStructTags": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Int int ` + "`gojay:\"someInt\"`" + ` + Str string ` + "`gojay:\"someStr\"`" + ` +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + case "someInt": + return dec.Int(&v.Int) + case "someStr": + return dec.String(&v.Str) + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 2 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("someInt", v.Int) + enc.StringKey("someStr", v.Str) +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "basicStructTagsHideUnmarshal": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Int int ` + "`gojay:\"-u\"`" + ` + Str string ` + "`gojay:\"-u\"`" + ` +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 0 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("int", v.Int) + enc.StringKey("str", v.Str) +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "basicStructTagsHideUnmarshal2": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Int int ` + "`gojay:\"someInt,-u\"`" + ` + Str string ` + "`gojay:\"someStr,-u\"`" + ` +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 0 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("someInt", v.Int) + enc.StringKey("someStr", v.Str) +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "basicStructTagsHideUnmarshal3": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Int int ` + "`gojay:\"someInt,-m\"`" + ` + Str string ` + "`gojay:\"someStr,-m\"`" + ` +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + case "someInt": + return dec.Int(&v.Int) + case "someStr": + return dec.String(&v.Str) + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 2 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "basicStructTagsHideUnmarshal4": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Int int ` + "`gojay:\"-\"`" + ` + Str string ` + "`gojay:\"-\"`" + ` +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 0 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "complexStructStructTag": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Struct Struct ` + "`gojay:\"someStruct\"`" + ` +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + case "someStruct": + if v.Struct == nil { + v.Struct = Struct{} + } + return dec.Object(v.Struct) + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 1 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { + enc.ObjectKey("someStruct", v.Struct) +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + "complexStructStructPtrTag": { + input: strings.NewReader(`package test + +//gojay:json +type Struct struct{ + Struct *Struct ` + "`gojay:\"someStruct\"`" + ` +} + `), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + switch k { + case "someStruct": + if v.Struct == nil { + v.Struct = &Struct{} + } + return dec.Object(v.Struct) + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v *Struct) NKeys() int { return 1 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) { + enc.ObjectKey("someStruct", v.Struct) +} + +// IsNil returns wether the structure is nil value or not +func (v *Struct) IsNil() bool { return v == nil } +`, + }, + } + for n, testCase := range testCases { + t.Run(n, func(t *testing.T) { + g, err := MakeGenFromReader(testCase.input) + if err != nil { + t.Fatal(err) + } + err = g.Gen() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, testCase.expectedResult, g.b.String()) + }) + } +} diff --git a/gojay/gen_struct_unmarshal.go b/gojay/gen_struct_unmarshal.go @@ -3,14 +3,13 @@ package main import ( "errors" "go/ast" - "strings" ) var structUnmarshalSwitchOpen = []byte("\tswitch k {\n") var structUnmarshalClose = []byte("\treturn nil\n}\n") -func (g *gen) structGenNKeys(n string, count int) error { - err := structUnmarshalTpl["nKeys"].tpl.Execute(g.f, struct { +func (g *Gen) structGenNKeys(n string, count int) error { + err := structUnmarshalTpl["nKeys"].tpl.Execute(g.b, struct { NKeys int StructName string }{ @@ -20,8 +19,8 @@ func (g *gen) structGenNKeys(n string, count int) error { return err } -func (g *gen) structGenUnmarshalObj(n string, s *ast.StructType) (int, error) { - err := structUnmarshalTpl["def"].tpl.Execute(g.f, struct { +func (g *Gen) structGenUnmarshalObj(n string, s *ast.StructType) (int, error) { + err := structUnmarshalTpl["def"].tpl.Execute(g.b, struct { StructName string }{ StructName: n, @@ -32,10 +31,15 @@ func (g *gen) structGenUnmarshalObj(n string, s *ast.StructType) (int, error) { keys := 0 if len(s.Fields.List) > 0 { // open switch statement - g.f.Write(structUnmarshalSwitchOpen) - + g.b.Write(structUnmarshalSwitchOpen) // TODO: check tags + // check type of field + // add accordingly for _, field := range s.Fields.List { + // check if has hide tag + if field.Tag != nil && hasTagUnmarshalHide(field.Tag) { + continue + } switch t := field.Type.(type) { case *ast.Ident: var err error @@ -52,92 +56,100 @@ func (g *gen) structGenUnmarshalObj(n string, s *ast.StructType) (int, error) { return 0, err } default: - return 0, errors.New("Unknown type1") + return 0, errors.New("Unknown type") } } } // close switch statement - g.f.Write([]byte("\t}\n")) + g.b.Write([]byte("\t}\n")) } - _, err = g.f.Write(structUnmarshalClose) + _, err = g.b.Write(structUnmarshalClose) if err != nil { return 0, err } return keys, nil } -func (g *gen) structGenUnmarshalIdent(field *ast.Field, i *ast.Ident, keys int, ptr bool) (int, error) { +func (g *Gen) structGenUnmarshalIdent(field *ast.Field, i *ast.Ident, keys int, ptr bool) (int, error) { + var keyV = getStructFieldJSONKey(field) + switch i.String() { case "string": - var err = g.structUnmarshalString(field, ptr) + var err = g.structUnmarshalString(field, keyV, ptr) if err != nil { return 0, err } keys++ case "bool": - var err = g.structUnmarshalBool(field, ptr) + var err = g.structUnmarshalBool(field, keyV, ptr) if err != nil { return 0, err } keys++ case "int": - var err = g.structUnmarshalInt(field, "", ptr) + var err = g.structUnmarshalInt(field, keyV, "", ptr) if err != nil { return 0, err } keys++ case "int64": - var err = g.structUnmarshalInt(field, "64", ptr) + var err = g.structUnmarshalInt(field, keyV, "64", ptr) if err != nil { return 0, err } keys++ case "int32": - var err = g.structUnmarshalInt(field, "32", ptr) + var err = g.structUnmarshalInt(field, keyV, "32", ptr) if err != nil { return 0, err } keys++ case "int16": - var err = g.structUnmarshalInt(field, "16", ptr) + var err = g.structUnmarshalInt(field, keyV, "16", ptr) if err != nil { return 0, err } keys++ case "int8": - var err = g.structUnmarshalInt(field, "8", ptr) + var err = g.structUnmarshalInt(field, keyV, "8", ptr) if err != nil { return 0, err } keys++ case "uint64": - var err = g.structUnmarshalUint(field, "64", ptr) + var err = g.structUnmarshalUint(field, keyV, "64", ptr) if err != nil { return 0, err } keys++ case "uint32": - var err = g.structUnmarshalUint(field, "32", ptr) + var err = g.structUnmarshalUint(field, keyV, "32", ptr) if err != nil { return 0, err } keys++ case "uint16": - var err = g.structUnmarshalUint(field, "16", ptr) + var err = g.structUnmarshalUint(field, keyV, "16", ptr) if err != nil { return 0, err } keys++ case "uint8": - var err = g.structUnmarshalUint(field, "8", ptr) + var err = g.structUnmarshalUint(field, keyV, "8", ptr) + if err != nil { + return 0, err + } + keys++ + case "float64": + var err = g.structUnmarshalFloat(field, keyV, "", ptr) if err != nil { return 0, err } keys++ default: // if ident is already in our spec list - if sp, ok := g.vis.specs[i.Name]; ok { - err := g.structUnmarshalNonPrim(field, sp, ptr) + if sp, ok := g.genTypes[i.Name]; ok { + err := g.structUnmarshalNonPrim(field, keyV, sp, ptr) if err != nil { return 0, err } @@ -146,7 +158,7 @@ func (g *gen) structGenUnmarshalIdent(field *ast.Field, i *ast.Ident, keys int, // else check the obj infos switch t := i.Obj.Decl.(type) { case *ast.TypeSpec: - err := g.structUnmarshalNonPrim(field, t, ptr) + err := g.structUnmarshalNonPrim(field, keyV, t, ptr) if err != nil { return 0, err } @@ -161,26 +173,26 @@ func (g *gen) structGenUnmarshalIdent(field *ast.Field, i *ast.Ident, keys int, return keys, nil } -func (g *gen) structUnmarshalNonPrim(field *ast.Field, sp *ast.TypeSpec, ptr bool) error { +func (g *Gen) structUnmarshalNonPrim(field *ast.Field, keyV string, sp *ast.TypeSpec, ptr bool) error { switch sp.Type.(type) { case *ast.StructType: - return g.structUnmarshalStruct(field, sp, ptr) + return g.structUnmarshalStruct(field, keyV, sp, ptr) case *ast.ArrayType: - return g.structUnmarshalArr(field, sp, ptr) + return g.structUnmarshalArr(field, keyV, sp, ptr) } return errors.New("Unknown type") } -func (g *gen) structUnmarshalString(field *ast.Field, ptr bool) error { +func (g *Gen) structUnmarshalString(field *ast.Field, keyV string, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { Key string - }{strings.ToLower(key)}) + }{keyV}) if err != nil { return err } if ptr { - err = structUnmarshalTpl["string"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["string"].tpl.Execute(g.b, struct { Field string Ptr string }{key, ""}) @@ -188,7 +200,7 @@ func (g *gen) structUnmarshalString(field *ast.Field, ptr bool) error { return err } } else { - err = structUnmarshalTpl["string"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["string"].tpl.Execute(g.b, struct { Field string Ptr string }{key, "&"}) @@ -199,16 +211,16 @@ func (g *gen) structUnmarshalString(field *ast.Field, ptr bool) error { return nil } -func (g *gen) structUnmarshalBool(field *ast.Field, ptr bool) error { +func (g *Gen) structUnmarshalBool(field *ast.Field, keyV string, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { Key string - }{strings.ToLower(key)}) + }{keyV}) if err != nil { return err } if ptr { - err = structUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["bool"].tpl.Execute(g.b, struct { Field string Ptr string }{key, ""}) @@ -216,7 +228,7 @@ func (g *gen) structUnmarshalBool(field *ast.Field, ptr bool) error { return err } } else { - err = structUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["bool"].tpl.Execute(g.b, struct { Field string Ptr string }{key, "&"}) @@ -227,16 +239,46 @@ func (g *gen) structUnmarshalBool(field *ast.Field, ptr bool) error { return nil } -func (g *gen) structUnmarshalInt(field *ast.Field, intLen string, ptr bool) error { +func (g *Gen) structUnmarshalInt(field *ast.Field, keyV string, intLen string, ptr bool) error { + key := field.Names[0].String() + err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { + Key string + }{keyV}) + if err != nil { + return err + } + if ptr { + err = structUnmarshalTpl["int"].tpl.Execute(g.b, struct { + Field string + IntLen string + Ptr string + }{key, intLen, ""}) + if err != nil { + return err + } + } else { + err = structUnmarshalTpl["int"].tpl.Execute(g.b, struct { + Field string + IntLen string + Ptr string + }{key, intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *Gen) structUnmarshalUint(field *ast.Field, keyV string, intLen string, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { Key string - }{strings.ToLower(key)}) + }{keyV}) if err != nil { return err } if ptr { - err = structUnmarshalTpl["int"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["uint"].tpl.Execute(g.b, struct { Field string IntLen string Ptr string @@ -245,7 +287,7 @@ func (g *gen) structUnmarshalInt(field *ast.Field, intLen string, ptr bool) erro return err } } else { - err = structUnmarshalTpl["int"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["uint"].tpl.Execute(g.b, struct { Field string IntLen string Ptr string @@ -257,16 +299,16 @@ func (g *gen) structUnmarshalInt(field *ast.Field, intLen string, ptr bool) erro return nil } -func (g *gen) structUnmarshalUint(field *ast.Field, intLen string, ptr bool) error { +func (g *Gen) structUnmarshalFloat(field *ast.Field, keyV string, intLen string, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { Key string - }{strings.ToLower(key)}) + }{keyV}) if err != nil { return err } if ptr { - err = structUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["float"].tpl.Execute(g.b, struct { Field string IntLen string Ptr string @@ -275,7 +317,7 @@ func (g *gen) structUnmarshalUint(field *ast.Field, intLen string, ptr bool) err return err } } else { - err = structUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["float"].tpl.Execute(g.b, struct { Field string IntLen string Ptr string @@ -287,16 +329,16 @@ func (g *gen) structUnmarshalUint(field *ast.Field, intLen string, ptr bool) err return nil } -func (g *gen) structUnmarshalStruct(field *ast.Field, st *ast.TypeSpec, ptr bool) error { +func (g *Gen) structUnmarshalStruct(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { Key string - }{strings.ToLower(key)}) + }{keyV}) if err != nil { return err } if ptr { - err = structUnmarshalTpl["struct"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct { Field string StructName string }{key, st.Name.String()}) @@ -304,7 +346,7 @@ func (g *gen) structUnmarshalStruct(field *ast.Field, st *ast.TypeSpec, ptr bool return err } } else { - err = structUnmarshalTpl["struct"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["struct"].tpl.Execute(g.b, struct { Field string StructName string }{key, st.Name.String()}) @@ -315,15 +357,15 @@ func (g *gen) structUnmarshalStruct(field *ast.Field, st *ast.TypeSpec, ptr bool return nil } -func (g *gen) structUnmarshalArr(field *ast.Field, st *ast.TypeSpec, ptr bool) error { +func (g *Gen) structUnmarshalArr(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { Key string - }{strings.ToLower(key)}) + }{keyV}) if err != nil { return err } - err = structUnmarshalTpl["arr"].tpl.Execute(g.f, struct { + err = structUnmarshalTpl["arr"].tpl.Execute(g.b, struct { Field string TypeName string }{key, st.Name.String()}) diff --git a/gojay/gen_struct_unmarshal_tpl.go b/gojay/gen_struct_unmarshal_tpl.go @@ -23,14 +23,24 @@ func (v *{{.StructName}}) NKeys() int { return {{.NKeys}} } "uint": &genTpl{ strTpl: "\t\treturn dec.Uint{{.IntLen}}({{.Ptr}}v.{{.Field}})\n", }, + "float": &genTpl{ + strTpl: "\t\treturn dec.Float{{.IntLen}}({{.Ptr}}v.{{.Field}})\n", + }, "bool": &genTpl{ strTpl: "\t\treturn dec.Bool({{.Ptr}}v.{{.Field}})\n", }, "struct": &genTpl{ strTpl: ` if v.{{.Field}} == nil { + v.{{.Field}} = {{.StructName}}{} + } + return dec.Object(v.{{.Field}}) +`, + }, + "structPtr": &genTpl{ + strTpl: ` if v.{{.Field}} == nil { v.{{.Field}} = &{{.StructName}}{} } - dec.Object(v.{{.Field}}) + return dec.Object(v.{{.Field}}) `, }, "arr": &genTpl{ @@ -38,7 +48,7 @@ func (v *{{.StructName}}) NKeys() int { return {{.NKeys}} } arr := make({{.TypeName}}, 0) v.{{.Field}} = &arr } - dec.Array(v.{{.Field}}) + return dec.Array(v.{{.Field}}) `, }, } diff --git a/gojay/gen_stuct.go b/gojay/gen_stuct.go @@ -2,9 +2,21 @@ package main import ( "go/ast" + "strings" ) -func (g *gen) genStruct(n string, s *ast.StructType) error { +func getStructFieldJSONKey(field *ast.Field) string { + var keyV string + if field.Tag != nil { + keyV = tagKeyName(field.Tag) + } + if keyV == "" { + keyV = strings.ToLower(field.Names[0].String()[:1]) + field.Names[0].String()[1:] + } + return keyV +} + +func (g *Gen) genStruct(n string, s *ast.StructType) error { keys, err := g.structGenUnmarshalObj(n, s) if err != nil { return err diff --git a/gojay/gen_tag.go b/gojay/gen_tag.go @@ -0,0 +1,55 @@ +package main + +import ( + "go/ast" + "log" + + "github.com/fatih/structtag" +) + +const gojayTag = "gojay" +const hideTag = "-" +const unmarshalHideTag = "-u" +const marshalHideTag = "-m" + +func getGojayTagValue(tags *ast.BasicLit) (*structtag.Tag, error) { + t, err := structtag.Parse(tags.Value[1 : len(tags.Value)-1]) + if err != nil { + return nil, err + } + v, err := t.Get(gojayTag) + if err != nil { + return nil, err + } + return v, nil +} + +func hasTagUnmarshalHide(tags *ast.BasicLit) bool { + v, err := getGojayTagValue(tags) + if err != nil { + log.Print(err) + return false + } + return (v.Name == unmarshalHideTag || v.Name == hideTag) || v.HasOption(unmarshalHideTag) +} + +func hasTagMarshalHide(tags *ast.BasicLit) bool { + v, err := getGojayTagValue(tags) + if err != nil { + log.Print(err) + return false + } + return (v.Name == marshalHideTag || v.Name == hideTag) || v.HasOption(marshalHideTag) +} + +func tagKeyName(tags *ast.BasicLit) string { + v, err := getGojayTagValue(tags) + if err != nil { + log.Print(err) + return "" + } + if v.Name == hideTag || v.Name == unmarshalHideTag || v.Name == marshalHideTag { + return "" + } + return v.Name +} diff --git a/gojay/gen_test.go b/gojay/gen_test.go @@ -0,0 +1,24 @@ +package main + +import ( + "go/ast" + "go/parser" + "go/token" + "io" +) + +func MakeGenFromReader(input io.Reader) (*Gen, error) { + g := NewGen("") + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", input, parser.ParseComments) + if err != nil { + return nil, err + } + v := NewVisitor(g, g.pkg) + ast.Walk(v, f) + if err != nil { + return nil, err + } + g.vis = v + return g, nil +} diff --git a/gojay/main.go b/gojay/main.go @@ -1,10 +1,9 @@ +//+build !test + package main import ( - "errors" - "go/ast" - "go/parser" - "go/token" + "flag" "io/ioutil" "log" "os" @@ -12,7 +11,8 @@ import ( "strings" ) -const gojayAnnotation = "//gojay:json" +var dst = flag.String("o", "", "destination file to output generated implementations") +var src = flag.String("s", "", "source dir or file") func hasAnnotation(fP string) bool { b, err := ioutil.ReadFile(fP) @@ -22,56 +22,54 @@ func hasAnnotation(fP string) bool { return strings.Contains(string(b), gojayAnnotation) } +// getPath returns either the path given as argument or current working directory func getPath() (string, error) { - p := os.Args[1] - return filepath.Abs(p) -} - -func getFiles() ([]string, error) { - if len(os.Args) < 2 { - return nil, errors.New("Gojay generator takes one argument, 0 given") - } - p, err := getPath() - if err != nil { - return nil, err - } - files, err := ioutil.ReadDir(p) - if err != nil { - return nil, err - } - r := make([]string, 0) - for _, f := range files { - fP := filepath.Join(p, f.Name()) - if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") && hasAnnotation(fP) { - r = append(r, fP) + var err error + var p string + if *src != "" { + p, err = filepath.Abs(*src) + if err != nil { + return "", err + } + } else { + p, err = os.Getwd() + if err != nil { + return "", err } } - return r, nil + return p, nil } func main() { + flag.Parse() + // get path p, err := getPath() if err != nil { log.Fatal(err) } - // for _, f := range files { - fset := token.NewFileSet() - pkgs, err := parser.ParseDir(fset, p, nil, parser.ParseComments) + // parse source files + g := NewGen(p) + err = g.parse() if err != nil { log.Fatal(err) + return } - for pkgName, pkg := range pkgs { - v := NewVisitor(pkgName) - for fileName, f := range pkg.Files { - v.file = fileName[:len(fileName)-3] + genFileSuffix - ast.Walk(v, f) - if err != nil { - log.Fatal(err) - } - } - err = v.gen() + // generate output + err = g.Gen() + if err != nil { + log.Fatal(err) + return + } + // if has dst file, write to file + if *dst != "" { + f, err := os.OpenFile(*dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { log.Fatal(err) + return } + f.WriteString(g.b.String()) + return } + // else just print to stdout + os.Stdout.WriteString(g.b.String()) } diff --git a/gojay/tests/basic_slices_gojay.go b/gojay/tests/basic_slices_gojay.go @@ -1,113 +0,0 @@ -package tests - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray -func (v *BoolSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - var b bool - if err := dec.Bool(&b); err != nil { - return err - } - *v = append(*v, b) - return nil -} - -// MarshalJSONArray implements gojay's MarshalerJSONArray -func (v *BoolSlice) MarshalJSONArray(enc *gojay.Encoder) { - for _, s := range *v { - enc.Bool(s) - } -} - -// IsNil implements gojay's MarshalerJSONArray -func (v *BoolSlice) IsNil() bool { - return *v == nil || len(*v) == 0 -} - -// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray -func (v *StructSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - var s = &A{} - if err := dec.Object(s); err != nil { - return err - } - *v = append(*v, s) - return nil -} - -// MarshalJSONArray implements gojay's MarshalerJSONArray -func (v *StructSlice) MarshalJSONArray(enc *gojay.Encoder) { - for _, s := range *v { - enc.Object(s) - } -} - -// IsNil implements gojay's MarshalerJSONArray -func (v *StructSlice) IsNil() bool { - return *v == nil || len(*v) == 0 -} - -// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray -func (v *SliceSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - var s = make(StrSlice, 0) - if err := dec.Array(&s); err != nil { - return err - } - *v = append(*v, &s) - return nil -} - -// MarshalJSONArray implements gojay's MarshalerJSONArray -func (v *SliceSlice) MarshalJSONArray(enc *gojay.Encoder) { - for _, s := range *v { - enc.Array(s) - } -} - -// IsNil implements gojay's MarshalerJSONArray -func (v *SliceSlice) IsNil() bool { - return *v == nil || len(*v) == 0 -} - -// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray -func (v *StrSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - var str string - if err := dec.String(&str); err != nil { - return err - } - *v = append(*v, str) - return nil -} - -// MarshalJSONArray implements gojay's MarshalerJSONArray -func (v *StrSlice) MarshalJSONArray(enc *gojay.Encoder) { - for _, s := range *v { - enc.String(s) - } -} - -// IsNil implements gojay's MarshalerJSONArray -func (v *StrSlice) IsNil() bool { - return *v == nil || len(*v) == 0 -} - -// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray -func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - var i int - if err := dec.Int(&i); err != nil { - return err - } - *v = append(*v, i) - return nil -} - -// MarshalJSONArray implements gojay's MarshalerJSONArray -func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) { - for _, s := range *v { - enc.Int(s) - } -} - -// IsNil implements gojay's MarshalerJSONArray -func (v *IntSlice) IsNil() bool { - return *v == nil || len(*v) == 0 -} diff --git a/gojay/tests/basic_structs.go b/gojay/tests/basic_structs.go @@ -2,7 +2,7 @@ package tests //gojay:json type A struct { - Str string + Str string `gojay:"string"` Bool bool Int int Int64 int64 diff --git a/gojay/tests/basic_structs_gojay.go b/gojay/tests/basic_structs_gojay.go @@ -1,149 +0,0 @@ -package tests - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v *A) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - switch k { - case "str": - return dec.String(&v.Str) - case "bool": - return dec.Bool(&v.Bool) - case "int": - return dec.Int(&v.Int) - case "int64": - return dec.Int64(&v.Int64) - case "int32": - return dec.Int32(&v.Int32) - case "int16": - return dec.Int16(&v.Int16) - case "int8": - return dec.Int8(&v.Int8) - case "uint64": - return dec.Uint64(&v.Uint64) - case "uint32": - return dec.Uint32(&v.Uint32) - case "uint16": - return dec.Uint16(&v.Uint16) - case "uint8": - return dec.Uint8(&v.Uint8) - case "bval": - if v.Bval == nil { - v.Bval = &B{} - } - dec.Object(v.Bval) - case "arrval": - if v.Arrval == nil { - arr := make(StrSlice, 0) - v.Arrval = &arr - } - dec.Array(v.Arrval) - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (v *A) NKeys() int { return 13 } - -// MarshalJSONObject implements gojay's MarshalerJSONObject -func (v *A) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("str", v.Str) - enc.BoolKey("bool", v.Bool) - enc.IntKey("int", v.Int) - enc.Int64Key("int64", v.Int64) - enc.Int32Key("int32", v.Int32) - enc.Int16Key("int16", v.Int16) - enc.Int8Key("int8", v.Int8) - enc.Uint64Key("uint64", v.Uint64) - enc.Uint32Key("uint32", v.Uint32) - enc.Uint16Key("uint16", v.Uint16) - enc.Uint8Key("uint8", v.Uint8) - enc.ObjectKey("bval", v.Bval) - enc.ArrayKey("arrval", v.Arrval) -} - -// IsNil returns wether the structure is nil value or not -func (v *A) IsNil() bool { return v == nil } - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v *B) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - switch k { - case "str": - return dec.String(&v.Str) - case "bool": - return dec.Bool(&v.Bool) - case "int": - return dec.Int(&v.Int) - case "int64": - return dec.Int64(&v.Int64) - case "int32": - return dec.Int32(&v.Int32) - case "int16": - return dec.Int16(&v.Int16) - case "int8": - return dec.Int8(&v.Int8) - case "uint64": - return dec.Uint64(&v.Uint64) - case "uint32": - return dec.Uint32(&v.Uint32) - case "uint16": - return dec.Uint16(&v.Uint16) - case "uint8": - return dec.Uint8(&v.Uint8) - case "strptr": - return dec.String(v.StrPtr) - case "boolptr": - return dec.Bool(v.BoolPtr) - case "intptr": - return dec.Int(v.IntPtr) - case "int64ptr": - return dec.Int64(v.Int64Ptr) - case "int32ptr": - return dec.Int32(v.Int32Ptr) - case "int16ptr": - return dec.Int16(v.Int16Ptr) - case "int8ptr": - return dec.Int8(v.Int8Ptr) - case "uint64ptr": - return dec.Uint64(v.Uint64Ptr) - case "uint32ptr": - return dec.Uint32(v.Uint32Ptr) - case "uint16ptr": - return dec.Uint16(v.Uint16Ptr) - case "uint8ptr": - return dec.Uint8(v.Uint8PTr) - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (v *B) NKeys() int { return 22 } - -// MarshalJSONObject implements gojay's MarshalerJSONObject -func (v *B) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("str", v.Str) - enc.BoolKey("bool", v.Bool) - enc.IntKey("int", v.Int) - enc.Int64Key("int64", v.Int64) - enc.Int32Key("int32", v.Int32) - enc.Int16Key("int16", v.Int16) - enc.Int8Key("int8", v.Int8) - enc.Uint64Key("uint64", v.Uint64) - enc.Uint32Key("uint32", v.Uint32) - enc.Uint16Key("uint16", v.Uint16) - enc.Uint8Key("uint8", v.Uint8) - enc.StringKey("strptr", *v.StrPtr) - enc.BoolKey("boolptr", *v.BoolPtr) - enc.IntKey("intptr", *v.IntPtr) - enc.Int64Key("int64ptr", *v.Int64Ptr) - enc.Int32Key("int32ptr", *v.Int32Ptr) - enc.Int16Key("int16ptr", *v.Int16Ptr) - enc.Int8Key("int8ptr", *v.Int8Ptr) - enc.Uint64Key("uint64ptr", *v.Uint64Ptr) - enc.Uint32Key("uint32ptr", *v.Uint32Ptr) - enc.Uint16Key("uint16ptr", *v.Uint16Ptr) - enc.Uint8Key("uint8ptr", *v.Uint8PTr) -} - -// IsNil returns wether the structure is nil value or not -func (v *B) IsNil() bool { return v == nil } diff --git a/gojay/visitor.go b/gojay/visitor.go @@ -2,7 +2,6 @@ package main import ( "go/ast" - "os" "strings" ) @@ -17,9 +16,7 @@ func docContains(n *ast.CommentGroup, s string) bool { type vis struct { pkg string - specs map[string]*ast.TypeSpec - files map[string]map[string]*ast.TypeSpec - file string + g *Gen commentFound bool } @@ -38,11 +35,7 @@ func (v *vis) Visit(n ast.Node) (w ast.Visitor) { return v case *ast.TypeSpec: if v.commentFound { - v.specs[n.Name.Name] = n - if v.files[v.file] == nil { - v.files[v.file] = make(map[string]*ast.TypeSpec) - } - v.files[v.file][n.Name.Name] = n + v.g.genTypes[n.Name.Name] = n } v.commentFound = false return v @@ -53,27 +46,9 @@ func (v *vis) Visit(n ast.Node) (w ast.Visitor) { return v } -func (v *vis) gen() error { - for fileName, genTypes := range v.files { - // open the file - f, err := os.OpenFile(fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777) - if err != nil { - return err - } - defer f.Close() - g := &gen{f, genTypes, v} - err = g.gen(v.pkg) - if err != nil { - return err - } - } - return nil -} - -func NewVisitor(pkgName string) *vis { +func NewVisitor(g *Gen, pkgName string) *vis { return &vis{ - pkg: pkgName, - specs: make(map[string]*ast.TypeSpec), - files: make(map[string]map[string]*ast.TypeSpec), + g: g, + pkg: pkgName, } }