gojay

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

commit 14fa9d63bddeff0c0ea1834428f4b78bfac227a5
parent a667dd76ca13c376331e6d9ec2d8746d085ff985
Author: francoispqt <francois@parquet.ninja>
Date:   Sun,  3 Jun 2018 23:59:42 +0800

refactor generator code, add array

Diffstat:
Mgojay/gen.go | 88+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Agojay/gen_array.go | 20++++++++++++++++++++
Agojay/gen_array_marshal.go | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_array_marshal_tpl.go | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_array_unmarshal.go | 209+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_array_unmarshal_tpl.go | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgojay/gen_struct_marshal.go | 219+++++++++++++++++++++++++++++++++++++------------------------------------------
Agojay/gen_struct_marshal_tpl.go | 36++++++++++++++++++++++++++++++++++++
Mgojay/gen_struct_unmarshal.go | 306++++++++++++++++++++++++++++++++++++++++---------------------------------------
Agojay/gen_struct_unmarshal_tpl.go | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mgojay/gen_stuct.go | 11+++++------
Mgojay/main.go | 23+++++++++++++++--------
Agojay/tests/basic_slices.go | 16++++++++++++++++
Agojay/tests/basic_slices_gojay.go | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgojay/tests/basic_structs.go | 34+++++++++++++++++++++++-----------
Mgojay/tests/basic_structs_gojay.go | 46+++++++++++++++++++++++++++++++++++++++++++---
Mgojay/visitor.go | 37++++++++++++++++++++++++++++++++++---
17 files changed, 1215 insertions(+), 333 deletions(-)

diff --git a/gojay/gen.go b/gojay/gen.go @@ -2,11 +2,9 @@ package main import ( "go/ast" - "html/template" "log" "os" - - "github.com/davecgh/go-spew/spew" + "text/template" ) const genFileSuffix = "_gojay.go" @@ -14,6 +12,19 @@ const genFileSuffix = "_gojay.go" var pkgTpl *template.Template var gojayImport = []byte("import \"github.com/francoispqt/gojay\"\n") +type gen struct { + f *os.File + genTypes map[string]*ast.TypeSpec + vis *vis +} + +type genTpl struct { + strTpl string + tpl *template.Template +} + +type templateList map[string]*genTpl + func init() { t, err := template.New("pkgDef"). Parse("package {{.PkgName}} \n\n") @@ -23,59 +34,64 @@ func init() { pkgTpl = t } -func (v *vis) gen() error { - // open the file - f, err := os.OpenFile(v.genFileName(), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777) +func parseTemplates(tpls templateList, pfx string) { + for k, t := range tpls { + tpl, err := template.New(pfx + k).Parse(t.strTpl) + if err != nil { + log.Fatal(err) + } + t.tpl = tpl + } +} + +func (g *gen) writePkg(pkg string) error { + err := pkgTpl.Execute(g.f, struct { + PkgName string + }{ + PkgName: pkg, + }) + if err != nil { + return err + } + return nil +} + +func (g *gen) writeGojayImport() error { + _, err := g.f.Write(gojayImport) if err != nil { return err } - defer f.Close() + return nil +} + +func (g *gen) gen(pkg string) error { // write package - err = v.writePkg(f) + err := g.writePkg(pkg) if err != nil { return err } // write import of gojay - err = v.writeGojayImport(f) + err = g.writeGojayImport() if err != nil { return err } // range over specs // generate interfaces implementations based on type - for _, s := range v.specs { + for _, s := range g.genTypes { switch t := s.Type.(type) { case *ast.StructType: - err = v.genStruct(f, s.Name.String(), t) + err = g.genStruct(s.Name.String(), t) if err != nil { return err } case *ast.ArrayType: - spew.Println(t, "arr") + err = g.genArray(s.Name.String(), t) + if err != nil { + return err + } + case *ast.MapType: + // TODO: generate for map type } } return nil } - -func (v *vis) genFileName() string { - return v.file[:len(v.file)-3] + genFileSuffix -} - -func (v *vis) writePkg(f *os.File) error { - err := pkgTpl.Execute(f, struct { - PkgName string - }{ - PkgName: v.pkg, - }) - if err != nil { - return err - } - return nil -} - -func (v *vis) writeGojayImport(f *os.File) error { - _, err := f.Write(gojayImport) - if err != nil { - return err - } - return nil -} diff --git a/gojay/gen_array.go b/gojay/gen_array.go @@ -0,0 +1,20 @@ +package main + +import ( + "go/ast" +) + +func (g *gen) genArray(n string, s *ast.ArrayType) error { + err := g.arrGenUnmarshal(n, s) + if err != nil { + return err + } + + err = g.arrGenMarshal(n, s) + if err != nil { + return err + } + err = g.arrGenIsNil(n) + + return nil +} diff --git a/gojay/gen_array_marshal.go b/gojay/gen_array_marshal.go @@ -0,0 +1,220 @@ +package main + +import ( + "errors" + "go/ast" +) + +func init() {} + +func (g *gen) arrGenIsNil(n string) error { + err := arrMarshalTpl["isNil"].tpl.Execute(g.f, struct { + TypeName string + }{ + TypeName: n, + }) + return err +} + +func (g *gen) arrGenMarshal(n string, s *ast.ArrayType) error { + err := arrMarshalTpl["def"].tpl.Execute(g.f, struct { + TypeName string + }{ + TypeName: n, + }) + if err != nil { + return err + } + // determine type of element in array + switch t := s.Elt.(type) { + case *ast.Ident: + err := g.arrGenMarshalIdent(t, false) + if err != nil { + return err + } + case *ast.StarExpr: + switch ptrExp := t.X.(type) { + case *ast.Ident: + err := g.arrGenMarshalIdent(ptrExp, true) + if err != nil { + return err + } + default: + return errors.New("Unknown type") + } + } + _, err = g.f.Write([]byte("}\n")) + if err != nil { + return err + } + return err +} + +func (g *gen) arrGenMarshalIdent(i *ast.Ident, ptr bool) error { + switch i.String() { + case "string": + return g.arrMarshalString(ptr) + case "bool": + return g.arrMarshalBool(ptr) + case "int": + return g.arrMarshalInt("", ptr) + case "int64": + return g.arrMarshalInt("64", ptr) + case "int32": + return g.arrMarshalInt("32", ptr) + case "int16": + return g.arrMarshalInt("16", ptr) + case "int8": + return g.arrMarshalInt("8", ptr) + case "uint64": + return g.arrMarshalUint("64", ptr) + case "uint32": + return g.arrMarshalUint("32", ptr) + case "uint16": + return g.arrMarshalUint("16", ptr) + case "uint8": + return g.arrMarshalUint("8", ptr) + default: + // if ident is already in our spec list + if sp, ok := g.vis.specs[i.Name]; ok { + return g.arrMarshalNonPrim(sp, ptr) + } else if i.Obj != nil { + // else check the obj infos + switch t := i.Obj.Decl.(type) { + case *ast.TypeSpec: + return g.arrMarshalNonPrim(t, ptr) + default: + return errors.New("could not determine what to do with type " + i.String()) + } + } + return errors.New("Unknown type") + } +} + +func (g *gen) arrMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { + switch sp.Type.(type) { + case *ast.StructType: + return g.arrMarshalStruct(sp, ptr) + case *ast.ArrayType: + return g.arrMarshalArr(sp, ptr) + } + return nil +} + +func (g *gen) arrMarshalString(ptr bool) error { + if ptr { + err := arrMarshalTpl["stringPtr"].tpl.Execute(g.f, struct { + Ptr string + }{""}) + if err != nil { + return err + } + } else { + err := arrMarshalTpl["string"].tpl.Execute(g.f, struct { + Ptr string + }{"&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrMarshalBool(ptr bool) error { + if ptr { + err := arrMarshalTpl["bool"].tpl.Execute(g.f, struct { + Ptr string + }{""}) + if err != nil { + return err + } + } else { + err := arrMarshalTpl["bool"].tpl.Execute(g.f, struct { + Ptr string + }{"&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrMarshalInt(intLen string, ptr bool) error { + if ptr { + err := arrMarshalTpl["int"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, ""}) + if err != nil { + return err + } + } else { + err := arrMarshalTpl["int"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrMarshalUint(intLen string, ptr bool) error { + if ptr { + err := arrMarshalTpl["uint"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, ""}) + if err != nil { + return err + } + } else { + err := arrMarshalTpl["uint"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrMarshalStruct(st *ast.TypeSpec, ptr bool) error { + if ptr { + err := arrMarshalTpl["structPtr"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } else { + err := arrMarshalTpl["struct"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrMarshalArr(st *ast.TypeSpec, ptr bool) error { + if ptr { + err := arrMarshalTpl["arrPtr"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } else { + err := arrMarshalTpl["arr"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } + return nil +} diff --git a/gojay/gen_array_marshal_tpl.go b/gojay/gen_array_marshal_tpl.go @@ -0,0 +1,58 @@ +package main + +var arrMarshalTpl = templateList{ + "def": &genTpl{ + strTpl: "\n// MarshalJSONArray implements gojay's MarshalerJSONArray" + + "\nfunc (v *{{.TypeName}}) MarshalJSONArray(enc *gojay.Encoder) {\n", + }, + "isNil": &genTpl{ + strTpl: "\n// IsNil implements gojay's MarshalerJSONArray" + + "\nfunc (v *{{.TypeName}}) IsNil() bool {\n" + + "\treturn *v == nil || len(*v) == 0\n" + + "}\n", + }, + "string": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.String(s)\n" + + "\t}\n", + }, + "bool": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.Bool(s)\n" + + "\t}\n", + }, + "int": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.Int{{.IntLen}}(s)\n" + + "\t}\n", + }, + "uint": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.Uint{{.IntLen}}(s)\n" + + "\t}\n", + }, + "struct": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.Object(s)\n" + + "\t}\n", + }, + "structPtr": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.Object(s)\n" + + "\t}\n", + }, + "arr": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.Array(s)\n" + + "\t}\n", + }, + "arrPtr": &genTpl{ + strTpl: "\tfor _, s := range *v {\n" + + "\t\tenc.Array(s)\n" + + "\t}\n", + }, +} + +func init() { + parseTemplates(arrMarshalTpl, "arrMarshal") +} diff --git a/gojay/gen_array_unmarshal.go b/gojay/gen_array_unmarshal.go @@ -0,0 +1,209 @@ +package main + +import ( + "errors" + "go/ast" +) + +func (g *gen) arrGenUnmarshal(n string, s *ast.ArrayType) error { + err := arrUnmarshalTpl["def"].tpl.Execute(g.f, struct { + TypeName string + }{ + TypeName: n, + }) + if err != nil { + return err + } + // determine type of element in array + switch t := s.Elt.(type) { + case *ast.Ident: + err := g.arrGenUnmarshalIdent(t, false) + if err != nil { + return err + } + case *ast.StarExpr: + switch ptrExp := t.X.(type) { + case *ast.Ident: + err := g.arrGenUnmarshalIdent(ptrExp, true) + if err != nil { + return err + } + default: + return errors.New("Unknown type") + } + } + _, err = g.f.Write(structUnmarshalClose) + if err != nil { + return err + } + return err +} + +func (g *gen) arrGenUnmarshalIdent(i *ast.Ident, ptr bool) error { + switch i.String() { + case "string": + return g.arrUnmarshalString(ptr) + case "bool": + return g.arrUnmarshalBool(ptr) + case "int": + return g.arrUnmarshalInt("", ptr) + case "int64": + return g.arrUnmarshalInt("64", ptr) + case "int32": + return g.arrUnmarshalInt("32", ptr) + case "int16": + return g.arrUnmarshalInt("16", ptr) + case "int8": + return g.arrUnmarshalInt("8", ptr) + case "uint64": + return g.arrUnmarshalUint("64", ptr) + case "uint32": + return g.arrUnmarshalUint("32", ptr) + case "uint16": + return g.arrUnmarshalUint("16", ptr) + case "uint8": + return g.arrUnmarshalUint("8", ptr) + default: + // if ident is already in our spec list + if sp, ok := g.vis.specs[i.Name]; ok { + return g.arrUnmarshalNonPrim(sp, ptr) + } else if i.Obj != nil { + // else check the obj infos + switch t := i.Obj.Decl.(type) { + case *ast.TypeSpec: + return g.arrUnmarshalNonPrim(t, ptr) + default: + return errors.New("could not determine what to do with type " + i.String()) + } + } + return errors.New("Unknown type") + } +} + +func (g *gen) arrUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { + switch sp.Type.(type) { + case *ast.StructType: + return g.arrUnmarshalStruct(sp, ptr) + case *ast.ArrayType: + return g.arrUnmarshalArr(sp, ptr) + } + return nil +} + +func (g *gen) arrUnmarshalString(ptr bool) error { + if ptr { + err := arrUnmarshalTpl["stringPtr"].tpl.Execute(g.f, struct { + Ptr string + }{""}) + if err != nil { + return err + } + } else { + err := arrUnmarshalTpl["string"].tpl.Execute(g.f, struct { + Ptr string + }{"&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrUnmarshalBool(ptr bool) error { + if ptr { + err := arrUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + Ptr string + }{""}) + if err != nil { + return err + } + } else { + err := arrUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + Ptr string + }{"&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrUnmarshalInt(intLen string, ptr bool) error { + if ptr { + err := arrUnmarshalTpl["int"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, ""}) + if err != nil { + return err + } + } else { + err := arrUnmarshalTpl["int"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrUnmarshalUint(intLen string, ptr bool) error { + if ptr { + err := arrUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, ""}) + if err != nil { + return err + } + } else { + err := arrUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + IntLen string + Ptr string + }{intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrUnmarshalStruct(st *ast.TypeSpec, ptr bool) error { + if ptr { + err := arrUnmarshalTpl["structPtr"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } else { + err := arrUnmarshalTpl["struct"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) arrUnmarshalArr(st *ast.TypeSpec, ptr bool) error { + if ptr { + err := arrUnmarshalTpl["arrPtr"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } else { + err := arrUnmarshalTpl["arr"].tpl.Execute(g.f, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } + return nil +} diff --git a/gojay/gen_array_unmarshal_tpl.go b/gojay/gen_array_unmarshal_tpl.go @@ -0,0 +1,64 @@ +package main + +var arrUnmarshalTpl = templateList{ + "def": &genTpl{ + strTpl: "\n// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray" + + "\nfunc (v *{{.TypeName}}) UnmarshalJSONArray(dec *gojay.Decoder) error {\n", + }, + "string": &genTpl{ + strTpl: "\tvar str string" + + "\n\tif err := dec.String(&str); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, str)\n", + }, + "stringPtr": &genTpl{ + strTpl: "\n// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray" + + "\nfunc (v *{{.TypeName}}) UnmarshalJSONArray(dec *gojay.Decoder) error {\n", + }, + "int": &genTpl{ + strTpl: "\tvar i int{{.IntLen}}" + + "\n\tif err := dec.Int{{.IntLen}}(&i); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, i)\n", + }, + "uint": &genTpl{ + strTpl: "\tvar i uint{{.IntLen}}" + + "\n\tif err := dec.Uint{{.IntLen}}(&i); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, i)\n", + }, + "bool": &genTpl{ + strTpl: "\tvar b bool" + + "\n\tif err := dec.Bool(&b); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, b)\n", + }, + "struct": &genTpl{ + strTpl: "\tvar s = {{.StructName}}{}" + + "\n\tif err := dec.Object(&s); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, s)\n", + }, + "structPtr": &genTpl{ + strTpl: "\tvar s = &{{.StructName}}{}" + + "\n\tif err := dec.Object(s); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, s)\n", + }, + "arr": &genTpl{ + strTpl: "\tvar s = make({{.StructName}}, 0)" + + "\n\tif err := dec.Array(&s); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, s)\n", + }, + "arrPtr": &genTpl{ + strTpl: "\tvar s = make({{.StructName}}, 0)" + + "\n\tif err := dec.Array(&s); err != nil {\n" + + "\t\treturn err\n\t}\n" + + "\t*v = append(*v, &s)\n", + }, +} + +func init() { + parseTemplates(arrUnmarshalTpl, "arrUnarshal") +} diff --git a/gojay/gen_struct_marshal.go b/gojay/gen_struct_marshal.go @@ -3,84 +3,11 @@ package main import ( "errors" "go/ast" - "html/template" - "log" - "os" - "reflect" "strings" - - "github.com/davecgh/go-spew/spew" ) -var structMarshalDefTpl *template.Template -var structMarshalStringTpl *template.Template -var structMarshalIntTpl *template.Template -var structMarshalUintTpl *template.Template -var structMarshalBoolTpl *template.Template -var structMarshalStructTpl *template.Template - -var structIsNilTpl *template.Template - -var isNilMethod = ` -// IsNil returns wether the structure is nil value or not -func (v *{{.StructName}}) IsNil() bool { return v == nil } -` - -func init() { - t, err := template.New("structUnmarshalDef"). - Parse("\n// MarshalJSONObject implements gojay's MarshalerJSONObject" + - "\nfunc (v *{{.StructName}}) MarshalJSONObject(enc *gojay.Encoder) {\n", - ) - if err != nil { - log.Fatal(err) - } - structMarshalDefTpl = t - - t, err = template.New("structMarshalCaseString"). - Parse("\tenc.StringKey(\"{{.Key}}\", v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structMarshalStringTpl = t - - t, err = template.New("structMarshalCaseInt"). - Parse("\tenc.Int{{.IntLen}}Key(\"{{.Key}}\", v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structMarshalIntTpl = t - - t, err = template.New("structMarshalCaseUint"). - Parse("\tenc.Uint{{.IntLen}}Key(\"{{.Key}}\", v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structMarshalUintTpl = t - - t, err = template.New("structMarshalCaseBool"). - Parse("\tenc.BoolKey(\"{{.Key}}\", v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structMarshalBoolTpl = t - - t, err = template.New("structMarshalCaseStruct"). - Parse("\tenc.ObjectKey(\"{{.Key}}\", v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structMarshalStructTpl = t - - t, err = template.New("structMarhalIsNil"). - Parse(isNilMethod) - if err != nil { - log.Fatal(err) - } - structIsNilTpl = t -} - -func (v *vis) structGenIsNil(f *os.File, n string) error { - err := structIsNilTpl.Execute(f, struct { +func (g *gen) structGenIsNil(n string) error { + err := structMarshalTpl["isNil"].tpl.Execute(g.f, struct { StructName string }{ StructName: n, @@ -88,8 +15,8 @@ func (v *vis) structGenIsNil(f *os.File, n string) error { return err } -func (v *vis) structGenMarshalObj(f *os.File, n string, s *ast.StructType) (int, error) { - err := structMarshalDefTpl.Execute(f, struct { +func (g *gen) structGenMarshalObj(n string, s *ast.StructType) (int, error) { + err := structMarshalTpl["def"].tpl.Execute(g.f, struct { StructName string }{ StructName: n, @@ -104,7 +31,7 @@ func (v *vis) structGenMarshalObj(f *os.File, n string, s *ast.StructType) (int, switch t := field.Type.(type) { case *ast.Ident: var err error - keys, err = v.structGenMarshalIdent(f, field, t, keys) + keys, err = g.structGenMarshalIdent(field, t, keys, false) if err != nil { return 0, err } @@ -112,165 +39,225 @@ func (v *vis) structGenMarshalObj(f *os.File, n string, s *ast.StructType) (int, switch ptrExp := t.X.(type) { case *ast.Ident: var err error - keys, err = v.structGenMarshalIdent(f, field, ptrExp, keys) + keys, err = g.structGenMarshalIdent(field, ptrExp, keys, true) if err != nil { return 0, err } default: - spew.Println(reflect.TypeOf(ptrExp)) - spew.Println(ptrExp) + return 0, errors.New("Unknown type") } - default: - spew.Println(t) } } } - _, err = f.Write([]byte("}\n")) + _, err = g.f.Write([]byte("}\n")) if err != nil { return 0, err } return keys, nil } -func (v *vis) structGenMarshalIdent(f *os.File, field *ast.Field, i *ast.Ident, keys int) (int, error) { +func (g *gen) structGenMarshalIdent(field *ast.Field, i *ast.Ident, keys int, ptr bool) (int, error) { switch i.String() { case "string": - var err = v.structMarshalString(f, field) + var err = g.structMarshalString(field, ptr) if err != nil { return 0, err } keys++ case "bool": - var err = v.structMarshalBool(f, field) + var err = g.structMarshalBool(field, ptr) if err != nil { return 0, err } keys++ case "int": - var err = v.structMarshalInt(f, field, "") + var err = g.structMarshalInt(field, "", ptr) if err != nil { return 0, err } keys++ case "int64": - var err = v.structMarshalInt(f, field, "64") + var err = g.structMarshalInt(field, "64", ptr) if err != nil { return 0, err } keys++ case "int32": - var err = v.structMarshalInt(f, field, "32") + var err = g.structMarshalInt(field, "32", ptr) if err != nil { return 0, err } keys++ case "int16": - var err = v.structMarshalInt(f, field, "16") + var err = g.structMarshalInt(field, "16", ptr) if err != nil { return 0, err } keys++ case "int8": - var err = v.structMarshalInt(f, field, "8") + var err = g.structMarshalInt(field, "8", ptr) if err != nil { return 0, err } keys++ case "uint64": - var err = v.structMarshalUint(f, field, "64") + var err = g.structMarshalUint(field, "64", ptr) if err != nil { return 0, err } keys++ case "uint32": - var err = v.structMarshalUint(f, field, "32") + var err = g.structMarshalUint(field, "32", ptr) if err != nil { return 0, err } keys++ case "uint16": - var err = v.structMarshalUint(f, field, "16") + var err = g.structMarshalUint(field, "16", ptr) if err != nil { return 0, err } keys++ case "uint8": - var err = v.structMarshalUint(f, field, "8") + var err = g.structMarshalUint(field, "8", ptr) if err != nil { return 0, err } keys++ default: - switch t := i.Obj.Decl.(type) { - case *ast.TypeSpec: - var err = v.structMarshalStruct(f, field, t) + // if ident is already in our spec list + if sp, ok := g.vis.specs[i.Name]; ok { + err := g.structMarshalNonPrim(field, sp, ptr) if err != nil { return 0, err } keys++ - default: - return 0, errors.New("could not determine what to do with type " + i.String()) + } else if i.Obj != nil { + switch t := i.Obj.Decl.(type) { + case *ast.TypeSpec: + var err = g.structMarshalNonPrim(field, t, ptr) + if err != nil { + return 0, err + } + keys++ + default: + return 0, errors.New("could not determine what to do with type " + i.String()) + } + } else { + return 0, errors.New("Unknown type") } } return keys, nil } -func (v *vis) structMarshalString(f *os.File, field *ast.Field) error { +func (g *gen) structMarshalNonPrim(field *ast.Field, sp *ast.TypeSpec, ptr bool) error { + switch sp.Type.(type) { + case *ast.StructType: + return g.structMarshalStruct(field, sp, ptr) + case *ast.ArrayType: + return g.structMarshalArr(field, sp, ptr) + } + return nil +} + +func (g *gen) structMarshalString(field *ast.Field, ptr bool) error { key := field.Names[0].String() - err := structMarshalStringTpl.Execute(f, struct { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := structMarshalTpl["string"].tpl.Execute(g.f, struct { Field string Key string - }{key, strings.ToLower(key)}) + Ptr string + }{key, strings.ToLower(key), ptrStr}) if err != nil { return err } return nil } -func (v *vis) structMarshalBool(f *os.File, field *ast.Field) error { +func (g *gen) structMarshalBool(field *ast.Field, ptr bool) error { key := field.Names[0].String() - err := structMarshalBoolTpl.Execute(f, struct { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := structMarshalTpl["bool"].tpl.Execute(g.f, struct { Field string Key string - }{key, strings.ToLower(key)}) + Ptr string + }{key, strings.ToLower(key), ptrStr}) if err != nil { return err } return nil } -func (v *vis) structMarshalInt(f *os.File, field *ast.Field, intLen string) error { +func (g *gen) structMarshalInt(field *ast.Field, intLen string, ptr bool) error { key := field.Names[0].String() - err := structMarshalIntTpl.Execute(f, struct { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := structMarshalTpl["int"].tpl.Execute(g.f, struct { Field string IntLen string Key string - }{key, intLen, strings.ToLower(key)}) + Ptr string + }{key, intLen, strings.ToLower(key), ptrStr}) if err != nil { return err } return nil } -func (v *vis) structMarshalUint(f *os.File, field *ast.Field, intLen string) error { +func (g *gen) structMarshalUint(field *ast.Field, intLen string, ptr bool) error { key := field.Names[0].String() - err := structMarshalUintTpl.Execute(f, struct { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := structMarshalTpl["uint"].tpl.Execute(g.f, struct { Field string IntLen string Key string - }{key, intLen, strings.ToLower(key)}) + Ptr string + }{key, intLen, strings.ToLower(key), ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *gen) structMarshalStruct(field *ast.Field, st *ast.TypeSpec, ptr bool) error { + key := field.Names[0].String() + ptrStr := "" + if ptr { + ptrStr = "*" + } + var err = structMarshalTpl["struct"].tpl.Execute(g.f, struct { + Key string + Field string + Ptr string + }{strings.ToLower(key), key, ptrStr}) if err != nil { return err } return nil } -func (v *vis) structMarshalStruct(f *os.File, field *ast.Field, st *ast.TypeSpec) error { +func (g *gen) structMarshalArr(field *ast.Field, st *ast.TypeSpec, ptr bool) error { key := field.Names[0].String() - var err = structMarshalStructTpl.Execute(f, struct { + ptrStr := "" + if ptr { + ptrStr = "*" + } + var err = structMarshalTpl["arr"].tpl.Execute(g.f, struct { Key string Field string - }{strings.ToLower(key), key}) + Ptr string + }{strings.ToLower(key), key, ptrStr}) if err != nil { return err } diff --git a/gojay/gen_struct_marshal_tpl.go b/gojay/gen_struct_marshal_tpl.go @@ -0,0 +1,36 @@ +package main + +var structMarshalTpl = templateList{ + "def": &genTpl{ + strTpl: "\n// MarshalJSONObject implements gojay's MarshalerJSONObject" + + "\nfunc (v *{{.StructName}}) MarshalJSONObject(enc *gojay.Encoder) {\n", + }, + "isNil": &genTpl{ + strTpl: ` +// IsNil returns wether the structure is nil value or not +func (v *{{.StructName}}) IsNil() bool { return v == nil } +`, + }, + "string": &genTpl{ + strTpl: "\tenc.StringKey(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", + }, + "int": &genTpl{ + strTpl: "\tenc.Int{{.IntLen}}Key(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", + }, + "uint": &genTpl{ + strTpl: "\tenc.Uint{{.IntLen}}Key(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", + }, + "bool": &genTpl{ + strTpl: "\tenc.BoolKey(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", + }, + "struct": &genTpl{ + strTpl: "\tenc.ObjectKey(\"{{.Key}}\", v.{{.Field}})\n", + }, + "arr": &genTpl{ + strTpl: "\tenc.ArrayKey(\"{{.Key}}\", v.{{.Field}})\n", + }, +} + +func init() { + parseTemplates(structMarshalTpl, "structMarshal") +} diff --git a/gojay/gen_struct_unmarshal.go b/gojay/gen_struct_unmarshal.go @@ -3,101 +3,14 @@ package main import ( "errors" "go/ast" - "html/template" - "log" - "os" - "reflect" "strings" - - "github.com/davecgh/go-spew/spew" ) -var structUnmarshalDefTpl *template.Template -var structUnmarshalCaseTpl *template.Template -var structUnmarshalStringTpl *template.Template -var structUnmarshalIntTpl *template.Template -var structUnmarshalUintTpl *template.Template -var structUnmarshalBoolTpl *template.Template -var structUnmarshalStructTpl *template.Template - -var structNKeysTpl *template.Template - -var nKeysMethod = ` -// NKeys returns the number of keys to unmarshal -func (v *{{.StructName}}) NKeys() int { return {{.NKeys}} } -` - var structUnmarshalSwitchOpen = []byte("\tswitch k {\n") var structUnmarshalClose = []byte("\treturn nil\n}\n") -func init() { - t, err := template.New("structUnmarshalDef"). - Parse("\n// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject" + - "\nfunc (v *{{.StructName}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {\n", - ) - if err != nil { - log.Fatal(err) - } - structUnmarshalDefTpl = t - - t, err = template.New("structUnmarshalCase"). - Parse("\tcase \"{{.Key}}\":\n") - if err != nil { - log.Fatal(err) - } - structUnmarshalCaseTpl = t - - t, err = template.New("structUnmarshalCaseString"). - Parse("\t\treturn dec.String(&v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structUnmarshalStringTpl = t - - t, err = template.New("structUnmarshalCaseInt"). - Parse("\t\treturn dec.Int{{.IntLen}}(&v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structUnmarshalIntTpl = t - - t, err = template.New("structUnmarshalCaseUint"). - Parse("\t\treturn dec.Uint{{.IntLen}}(&v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structUnmarshalUintTpl = t - - t, err = template.New("structUnmarshalCaseBool"). - Parse("\t\treturn dec.Bool(&v.{{.Field}})\n") - if err != nil { - log.Fatal(err) - } - structUnmarshalBoolTpl = t - - t, err = template.New("structUnmarshalCaseStruct"). - Parse( - ` if v.{{.Field}} == nil { - v.{{.Field}} = &{{.StructName}}{} - } - dec.Object(v.{{.Field}}) -`) - if err != nil { - log.Fatal(err) - } - structUnmarshalStructTpl = t - - t, err = template.New("structUnmarshalNKeys"). - Parse(nKeysMethod) - if err != nil { - log.Fatal(err) - } - structNKeysTpl = t - -} - -func (v *vis) structGenNKeys(f *os.File, n string, count int) error { - err := structNKeysTpl.Execute(f, struct { +func (g *gen) structGenNKeys(n string, count int) error { + err := structUnmarshalTpl["nKeys"].tpl.Execute(g.f, struct { NKeys int StructName string }{ @@ -107,8 +20,8 @@ func (v *vis) structGenNKeys(f *os.File, n string, count int) error { return err } -func (v *vis) structGenUnmarshalObj(f *os.File, n string, s *ast.StructType) (int, error) { - err := structUnmarshalDefTpl.Execute(f, struct { +func (g *gen) structGenUnmarshalObj(n string, s *ast.StructType) (int, error) { + err := structUnmarshalTpl["def"].tpl.Execute(g.f, struct { StructName string }{ StructName: n, @@ -119,14 +32,14 @@ func (v *vis) structGenUnmarshalObj(f *os.File, n string, s *ast.StructType) (in keys := 0 if len(s.Fields.List) > 0 { // open switch statement - f.Write(structUnmarshalSwitchOpen) + g.f.Write(structUnmarshalSwitchOpen) // TODO: check tags for _, field := range s.Fields.List { switch t := field.Type.(type) { case *ast.Ident: var err error - keys, err = v.structGenUnmarshalIdent(f, field, t, keys) + keys, err = g.structGenUnmarshalIdent(field, t, keys, false) if err != nil { return 0, err } @@ -134,192 +47,285 @@ func (v *vis) structGenUnmarshalObj(f *os.File, n string, s *ast.StructType) (in switch ptrExp := t.X.(type) { case *ast.Ident: var err error - keys, err = v.structGenUnmarshalIdent(f, field, ptrExp, keys) + keys, err = g.structGenUnmarshalIdent(field, ptrExp, keys, true) if err != nil { return 0, err } default: - spew.Println(reflect.TypeOf(ptrExp)) - spew.Println(ptrExp) + return 0, errors.New("Unknown type1") } - default: - spew.Println(t) } } // close switch statement - f.Write([]byte("\t}\n")) + g.f.Write([]byte("\t}\n")) } - _, err = f.Write(structUnmarshalClose) + _, err = g.f.Write(structUnmarshalClose) if err != nil { return 0, err } return keys, nil } -func (v *vis) structGenUnmarshalIdent(f *os.File, field *ast.Field, i *ast.Ident, keys int) (int, error) { +func (g *gen) structGenUnmarshalIdent(field *ast.Field, i *ast.Ident, keys int, ptr bool) (int, error) { switch i.String() { case "string": - var err = v.structUnmarshalString(f, field) + var err = g.structUnmarshalString(field, ptr) if err != nil { return 0, err } keys++ case "bool": - var err = v.structUnmarshalBool(f, field) + var err = g.structUnmarshalBool(field, ptr) if err != nil { return 0, err } keys++ case "int": - var err = v.structUnmarshalInt(f, field, "") + var err = g.structUnmarshalInt(field, "", ptr) if err != nil { return 0, err } keys++ case "int64": - var err = v.structUnmarshalInt(f, field, "64") + var err = g.structUnmarshalInt(field, "64", ptr) if err != nil { return 0, err } keys++ case "int32": - var err = v.structUnmarshalInt(f, field, "32") + var err = g.structUnmarshalInt(field, "32", ptr) if err != nil { return 0, err } keys++ case "int16": - var err = v.structUnmarshalInt(f, field, "16") + var err = g.structUnmarshalInt(field, "16", ptr) if err != nil { return 0, err } keys++ case "int8": - var err = v.structUnmarshalInt(f, field, "8") + var err = g.structUnmarshalInt(field, "8", ptr) if err != nil { return 0, err } keys++ case "uint64": - var err = v.structUnmarshalUint(f, field, "64") + var err = g.structUnmarshalUint(field, "64", ptr) if err != nil { return 0, err } keys++ case "uint32": - var err = v.structUnmarshalUint(f, field, "32") + var err = g.structUnmarshalUint(field, "32", ptr) if err != nil { return 0, err } keys++ case "uint16": - var err = v.structUnmarshalUint(f, field, "16") + var err = g.structUnmarshalUint(field, "16", ptr) if err != nil { return 0, err } keys++ case "uint8": - var err = v.structUnmarshalUint(f, field, "8") + var err = g.structUnmarshalUint(field, "8", ptr) if err != nil { return 0, err } keys++ default: - switch t := i.Obj.Decl.(type) { - case *ast.TypeSpec: - var err = v.structUnmarshalStruct(f, field, t) + // if ident is already in our spec list + if sp, ok := g.vis.specs[i.Name]; ok { + err := g.structUnmarshalNonPrim(field, sp, ptr) if err != nil { return 0, err } keys++ - default: - return 0, errors.New("could not determine what to do with type " + i.String()) + } else if i.Obj != nil { + // else check the obj infos + switch t := i.Obj.Decl.(type) { + case *ast.TypeSpec: + err := g.structUnmarshalNonPrim(field, t, ptr) + if err != nil { + return 0, err + } + keys++ + default: + return 0, errors.New("could not determine what to do with type " + i.String()) + } + } else { + return 0, errors.New("Unknown type") } } return keys, nil } -func (v *vis) structUnmarshalString(f *os.File, field *ast.Field) error { +func (g *gen) structUnmarshalNonPrim(field *ast.Field, sp *ast.TypeSpec, ptr bool) error { + switch sp.Type.(type) { + case *ast.StructType: + return g.structUnmarshalStruct(field, sp, ptr) + case *ast.ArrayType: + return g.structUnmarshalArr(field, sp, ptr) + } + return errors.New("Unknown type") +} + +func (g *gen) structUnmarshalString(field *ast.Field, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalCaseTpl.Execute(f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { Key string }{strings.ToLower(key)}) if err != nil { return err } - err = structUnmarshalStringTpl.Execute(f, struct { - Field string - }{key}) - if err != nil { - return err + if ptr { + err = structUnmarshalTpl["string"].tpl.Execute(g.f, struct { + Field string + Ptr string + }{key, ""}) + if err != nil { + return err + } + } else { + err = structUnmarshalTpl["string"].tpl.Execute(g.f, struct { + Field string + Ptr string + }{key, "&"}) + if err != nil { + return err + } } return nil } -func (v *vis) structUnmarshalBool(f *os.File, field *ast.Field) error { +func (g *gen) structUnmarshalBool(field *ast.Field, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalCaseTpl.Execute(f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { Key string }{strings.ToLower(key)}) if err != nil { return err } - err = structUnmarshalBoolTpl.Execute(f, struct { - Field string - }{key}) - if err != nil { - return err + if ptr { + err = structUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + Field string + Ptr string + }{key, ""}) + if err != nil { + return err + } + } else { + err = structUnmarshalTpl["bool"].tpl.Execute(g.f, struct { + Field string + Ptr string + }{key, "&"}) + if err != nil { + return err + } } return nil } -func (v *vis) structUnmarshalInt(f *os.File, field *ast.Field, intLen string) error { +func (g *gen) structUnmarshalInt(field *ast.Field, intLen string, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalCaseTpl.Execute(f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { Key string }{strings.ToLower(key)}) if err != nil { return err } - err = structUnmarshalIntTpl.Execute(f, struct { - Field string - IntLen string - }{key, intLen}) - if err != nil { - return err + if ptr { + err = structUnmarshalTpl["int"].tpl.Execute(g.f, struct { + Field string + IntLen string + Ptr string + }{key, intLen, ""}) + if err != nil { + return err + } + } else { + err = structUnmarshalTpl["int"].tpl.Execute(g.f, struct { + Field string + IntLen string + Ptr string + }{key, intLen, "&"}) + if err != nil { + return err + } } return nil } -func (v *vis) structUnmarshalUint(f *os.File, field *ast.Field, intLen string) error { +func (g *gen) structUnmarshalUint(field *ast.Field, intLen string, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalCaseTpl.Execute(f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { Key string }{strings.ToLower(key)}) if err != nil { return err } - err = structUnmarshalUintTpl.Execute(f, struct { - Field string - IntLen string - }{key, intLen}) + if ptr { + err = structUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + Field string + IntLen string + Ptr string + }{key, intLen, ""}) + if err != nil { + return err + } + } else { + err = structUnmarshalTpl["uint"].tpl.Execute(g.f, struct { + Field string + IntLen string + Ptr string + }{key, intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *gen) structUnmarshalStruct(field *ast.Field, st *ast.TypeSpec, ptr bool) error { + key := field.Names[0].String() + err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { + Key string + }{strings.ToLower(key)}) if err != nil { return err } + if ptr { + err = structUnmarshalTpl["struct"].tpl.Execute(g.f, struct { + Field string + StructName string + }{key, st.Name.String()}) + if err != nil { + return err + } + } else { + err = structUnmarshalTpl["struct"].tpl.Execute(g.f, struct { + Field string + StructName string + }{key, st.Name.String()}) + if err != nil { + return err + } + } return nil } -func (v *vis) structUnmarshalStruct(f *os.File, field *ast.Field, st *ast.TypeSpec) error { +func (g *gen) structUnmarshalArr(field *ast.Field, st *ast.TypeSpec, ptr bool) error { key := field.Names[0].String() - err := structUnmarshalCaseTpl.Execute(f, struct { + err := structUnmarshalTpl["case"].tpl.Execute(g.f, struct { Key string }{strings.ToLower(key)}) if err != nil { return err } - err = structUnmarshalStructTpl.Execute(f, struct { - Field string - StructName string + err = structUnmarshalTpl["arr"].tpl.Execute(g.f, struct { + Field string + TypeName string }{key, st.Name.String()}) if err != nil { return err diff --git a/gojay/gen_struct_unmarshal_tpl.go b/gojay/gen_struct_unmarshal_tpl.go @@ -0,0 +1,48 @@ +package main + +var structUnmarshalTpl = templateList{ + "def": &genTpl{ + strTpl: "\n// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject" + + "\nfunc (v *{{.StructName}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {\n", + }, + "nKeys": &genTpl{ + strTpl: ` +// NKeys returns the number of keys to unmarshal +func (v *{{.StructName}}) NKeys() int { return {{.NKeys}} } +`, + }, + "case": &genTpl{ + strTpl: "\tcase \"{{.Key}}\":\n", + }, + "string": &genTpl{ + strTpl: "\t\treturn dec.String({{.Ptr}}v.{{.Field}})\n", + }, + "int": &genTpl{ + strTpl: "\t\treturn dec.Int{{.IntLen}}({{.Ptr}}v.{{.Field}})\n", + }, + "uint": &genTpl{ + strTpl: "\t\treturn dec.Uint{{.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}}{} + } + dec.Object(v.{{.Field}}) +`, + }, + "arr": &genTpl{ + strTpl: ` if v.{{.Field}} == nil { + arr := make({{.TypeName}}, 0) + v.{{.Field}} = &arr + } + dec.Array(v.{{.Field}}) +`, + }, +} + +func init() { + parseTemplates(structUnmarshalTpl, "structUnmarshal") +} diff --git a/gojay/gen_stuct.go b/gojay/gen_stuct.go @@ -2,20 +2,19 @@ package main import ( "go/ast" - "os" ) -func (v *vis) genStruct(f *os.File, n string, s *ast.StructType) error { - keys, err := v.structGenUnmarshalObj(f, n, s) +func (g *gen) genStruct(n string, s *ast.StructType) error { + keys, err := g.structGenUnmarshalObj(n, s) if err != nil { return err } - err = v.structGenNKeys(f, n, keys) + err = g.structGenNKeys(n, keys) - keys, err = v.structGenMarshalObj(f, n, s) + keys, err = g.structGenMarshalObj(n, s) if err != nil { return err } - err = v.structGenIsNil(f, n) + err = g.structGenIsNil(n) return nil } diff --git a/gojay/main.go b/gojay/main.go @@ -50,18 +50,25 @@ func getFiles() ([]string, error) { } func main() { - files, err := getFiles() + p, err := getPath() if err != nil { log.Fatal(err) } - for _, f := range files { - fset := token.NewFileSet() - node, err := parser.ParseFile(fset, f, nil, parser.ParseComments) - if err != nil { - log.Fatal(err) + // for _, f := range files { + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, p, nil, parser.ParseComments) + if err != nil { + log.Fatal(err) + } + 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) + } } - v := &vis{pkg: node.Name.String(), specs: make([]*ast.TypeSpec, 0), file: f} - ast.Walk(v, node) err = v.gen() if err != nil { log.Fatal(err) diff --git a/gojay/tests/basic_slices.go b/gojay/tests/basic_slices.go @@ -0,0 +1,16 @@ +package tests + +//gojay:json +type StrSlice []string + +//gojay:json +type IntSlice []int + +//gojay:json +type BoolSlice []bool + +//gojay:json +type StructSlice []*A + +//gojay:json +type SliceSlice []*StrSlice diff --git a/gojay/tests/basic_slices_gojay.go b/gojay/tests/basic_slices_gojay.go @@ -0,0 +1,113 @@ +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 @@ -14,19 +14,31 @@ type A struct { Uint16 uint16 Uint8 uint8 Bval *B + Arrval *StrSlice } //gojay:json type B struct { - Str string - Bool bool - Int int - Int64 int64 - Int32 int32 - Int16 int16 - Int8 int8 - Uint64 uint64 - Uint32 uint32 - Uint16 uint16 - Uint8 uint8 + Str string + Bool bool + Int int + Int64 int64 + Int32 int32 + Int16 int16 + Int8 int8 + Uint64 uint64 + Uint32 uint32 + Uint16 uint16 + Uint8 uint8 + StrPtr *string + BoolPtr *bool + IntPtr *int + Int64Ptr *int64 + Int32Ptr *int32 + Int16Ptr *int16 + Int8Ptr *int8 + Uint64Ptr *uint64 + Uint32Ptr *uint32 + Uint16Ptr *uint16 + Uint8PTr *uint8 } diff --git a/gojay/tests/basic_structs_gojay.go b/gojay/tests/basic_structs_gojay.go @@ -1,4 +1,4 @@ -package tests +package tests import "github.com/francoispqt/gojay" @@ -32,12 +32,18 @@ func (v *A) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { 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 12 } +func (v *A) NKeys() int { return 13 } // MarshalJSONObject implements gojay's MarshalerJSONObject func (v *A) MarshalJSONObject(enc *gojay.Encoder) { @@ -53,6 +59,7 @@ func (v *A) MarshalJSONObject(enc *gojay.Encoder) { 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 @@ -83,12 +90,34 @@ func (v *B) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { 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 11 } +func (v *B) NKeys() int { return 22 } // MarshalJSONObject implements gojay's MarshalerJSONObject func (v *B) MarshalJSONObject(enc *gojay.Encoder) { @@ -103,6 +132,17 @@ func (v *B) MarshalJSONObject(enc *gojay.Encoder) { 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 diff --git a/gojay/visitor.go b/gojay/visitor.go @@ -2,6 +2,7 @@ package main import ( "go/ast" + "os" "strings" ) @@ -16,7 +17,8 @@ func docContains(n *ast.CommentGroup, s string) bool { type vis struct { pkg string - specs []*ast.TypeSpec + specs map[string]*ast.TypeSpec + files map[string]map[string]*ast.TypeSpec file string commentFound bool } @@ -36,13 +38,42 @@ func (v *vis) Visit(n ast.Node) (w ast.Visitor) { return v case *ast.TypeSpec: if v.commentFound { - v.specs = append(v.specs, n) + 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.commentFound = false return v case *ast.StructType: v.commentFound = false - return nil + return v + } + 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 { + return &vis{ + pkg: pkgName, + specs: make(map[string]*ast.TypeSpec), + files: make(map[string]map[string]*ast.TypeSpec), + } +}