commit acc87b92070551fbd05b1ef178851952e8cf2681
parent 9d67e07523a5848a7723c933d46d8514a2c9a30a
Author: francoispqt <francois@parquet.ninja>
Date: Tue, 19 Jun 2018 00:33:44 +0800
add gen for maps
Diffstat:
7 files changed, 792 insertions(+), 0 deletions(-)
diff --git a/gojay/gen.go b/gojay/gen.go
@@ -107,6 +107,10 @@ func (g *Gen) Gen() error {
// is map
case *ast.MapType:
// TODO: generate for map type
+ err = g.genMap(s.Name.String(), t)
+ if err != nil {
+ return err
+ }
}
}
return nil
diff --git a/gojay/gen_map.go b/gojay/gen_map.go
@@ -1 +1,19 @@
package main
+
+import "go/ast"
+
+func (g *Gen) genMap(n string, s *ast.MapType) error {
+ err := g.mapGenUnmarshalObj(n, s)
+ if err != nil {
+ return err
+ }
+ err = g.mapGenNKeys(n, 0)
+ if err != nil {
+ return err
+ }
+ err = g.mapGenMarshalObj(n, s)
+ if err != nil {
+ return err
+ }
+ return g.mapGenIsNil(n)
+}
diff --git a/gojay/gen_map_marshal.go b/gojay/gen_map_marshal.go
@@ -0,0 +1,249 @@
+package main
+
+import (
+ "errors"
+ "go/ast"
+)
+
+func (g *Gen) mapGenIsNil(n string) error {
+ err := mapMarshalTpl["isNil"].tpl.Execute(g.b, struct {
+ StructName string
+ }{
+ StructName: n,
+ })
+ return err
+}
+
+func (g *Gen) mapGenMarshalObj(n string, s *ast.MapType) error {
+ err := mapMarshalTpl["def"].tpl.Execute(g.b, struct {
+ StructName string
+ }{
+ StructName: n,
+ })
+ if err != nil {
+ return err
+ }
+ switch t := s.Value.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenMarshalIdent(t, false)
+ if err != nil {
+ return err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenMarshalIdent(ptrExp, true)
+ if err != nil {
+ return err
+ }
+ default:
+ return errors.New("Unknown type")
+ }
+ }
+ _, err = g.b.Write([]byte("}\n"))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapGenMarshalIdent(i *ast.Ident, ptr bool) error {
+ switch i.String() {
+ case "string":
+ var err = g.mapMarshalString(ptr)
+ if err != nil {
+ return err
+ }
+ case "bool":
+ var err = g.mapMarshalBool(ptr)
+ if err != nil {
+ return err
+ }
+ case "int":
+ var err = g.mapMarshalInt("", ptr)
+ if err != nil {
+ return err
+ }
+ case "int64":
+ var err = g.mapMarshalInt("64", ptr)
+ if err != nil {
+ return err
+ }
+ case "int32":
+ var err = g.mapMarshalInt("32", ptr)
+ if err != nil {
+ return err
+ }
+ case "int16":
+ var err = g.mapMarshalInt("16", ptr)
+ if err != nil {
+ return err
+ }
+ case "int8":
+ var err = g.mapMarshalInt("8", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint64":
+ var err = g.mapMarshalUint("64", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint32":
+ var err = g.mapMarshalUint("32", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint16":
+ var err = g.mapMarshalUint("16", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint8":
+ var err = g.mapMarshalUint("8", ptr)
+ if err != nil {
+ return err
+ }
+ case "float64":
+ var err = g.mapMarshalFloat("", ptr)
+ if err != nil {
+ return err
+ }
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ err := g.mapMarshalNonPrim(sp, ptr)
+ if err != nil {
+ return err
+ }
+
+ } else if i.Obj != nil {
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ var err = g.mapMarshalNonPrim(t, ptr)
+ if err != nil {
+ return err
+ }
+
+ default:
+ return errors.New("could not determine what to do with type " + i.String())
+ }
+ } else {
+ return errors.New("Unknown type")
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ return g.mapMarshalStruct(sp, ptr)
+ case *ast.ArrayType:
+ return g.mapMarshalArr(sp, ptr)
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalString(ptr bool) error {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalBool(ptr bool) error {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalInt(intLen string, ptr bool) error {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ptrStr})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalUint(intLen string, ptr bool) error {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ptrStr})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalFloat(intLen string, ptr bool) error {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ptrStr})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalStruct(st *ast.TypeSpec, ptr bool) error {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ var err = mapMarshalTpl["struct"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalArr(st *ast.TypeSpec, ptr bool) error {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ var err = mapMarshalTpl["arr"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/gojay/gen_map_marshal_tpl.go b/gojay/gen_map_marshal_tpl.go
@@ -0,0 +1,60 @@
+package main
+
+var mapMarshalTpl = 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 || len(v) == 0 }
+`,
+ },
+ "string": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.StringKey(k, s)
+ }
+`,
+ },
+ "int": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.Int{{.IntLen}}Key(k, s)
+ }
+`,
+ },
+ "uint": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.Uint{{.IntLen}}Key(k, s)
+ }
+`,
+ },
+ "float": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.Float{{.IntLen}}Key(k, s)
+ }
+`,
+ },
+ "bool": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.BoolKey(k, s)
+ }
+`,
+ },
+ "struct": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.ObjectKey(k, s)
+ }
+`,
+ },
+ "arr": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.ArrayKey(k, s)
+ }
+`,
+ },
+}
+
+func init() {
+ parseTemplates(mapMarshalTpl, "mapMarshal")
+}
diff --git a/gojay/gen_map_test.go b/gojay/gen_map_test.go
@@ -0,0 +1,100 @@
+package main
+
+import (
+ "io"
+ "log"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenMap(t *testing.T) {
+ testCases := map[string]struct {
+ input io.Reader
+ expectedResult string
+ }{
+ "basicMapStringString": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type StrMap map[string]string
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v StrMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ v[k] = str
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v StrMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v StrMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.StringKey(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v StrMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringInt": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]int
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int
+ if err := dec.Int(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.IntKey(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) 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)
+ }
+ log.Print(g.b.String())
+ assert.Equal(t, testCase.expectedResult, g.b.String())
+ })
+ }
+}
diff --git a/gojay/gen_map_unmarshal.go b/gojay/gen_map_unmarshal.go
@@ -0,0 +1,279 @@
+package main
+
+import (
+ "errors"
+ "go/ast"
+)
+
+func (g *Gen) mapGenNKeys(n string, count int) error {
+ err := mapUnmarshalTpl["nKeys"].tpl.Execute(g.b, struct {
+ NKeys int
+ StructName string
+ }{
+ NKeys: count,
+ StructName: n,
+ })
+ return err
+}
+
+func (g *Gen) mapGenUnmarshalObj(n string, s *ast.MapType) error {
+ err := mapUnmarshalTpl["def"].tpl.Execute(g.b, struct {
+ TypeName string
+ }{
+ TypeName: n,
+ })
+ if err != nil {
+ return err
+ }
+ switch t := s.Value.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenUnmarshalIdent(t, false)
+ if err != nil {
+ return err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenUnmarshalIdent(ptrExp, true)
+ if err != nil {
+ return err
+ }
+ default:
+ return errors.New("Unknown type")
+ }
+ }
+ _, err = g.b.Write(structUnmarshalClose)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapGenUnmarshalIdent(i *ast.Ident, ptr bool) error {
+ switch i.String() {
+ case "string":
+ var err = g.mapUnmarshalString(ptr)
+ if err != nil {
+ return err
+ }
+ case "bool":
+ var err = g.mapUnmarshalBool(ptr)
+ if err != nil {
+ return err
+ }
+ case "int":
+ var err = g.mapUnmarshalInt("", ptr)
+ if err != nil {
+ return err
+ }
+ case "int64":
+ var err = g.mapUnmarshalInt("64", ptr)
+ if err != nil {
+ return err
+ }
+ case "int32":
+ var err = g.mapUnmarshalInt("32", ptr)
+ if err != nil {
+ return err
+ }
+ case "int16":
+ var err = g.mapUnmarshalInt("16", ptr)
+ if err != nil {
+ return err
+ }
+ case "int8":
+ var err = g.mapUnmarshalInt("8", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint64":
+ var err = g.mapUnmarshalUint("64", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint32":
+ var err = g.mapUnmarshalUint("32", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint16":
+ var err = g.mapUnmarshalUint("16", ptr)
+ if err != nil {
+ return err
+ }
+ case "uint8":
+ var err = g.mapUnmarshalUint("8", ptr)
+ if err != nil {
+ return err
+ }
+ case "float64":
+ var err = g.mapUnmarshalFloat("", ptr)
+ if err != nil {
+ return err
+ }
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ err := g.mapUnmarshalNonPrim(sp, ptr)
+ if err != nil {
+ return err
+ }
+ } else if i.Obj != nil {
+ // else check the obj infos
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ err := g.mapUnmarshalNonPrim(t, ptr)
+ if err != nil {
+ return err
+ }
+ default:
+ return errors.New("could not determine what to do with type " + i.String())
+ }
+ } else {
+ return errors.New("Unknown type")
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ return g.mapUnmarshalStruct(sp, ptr)
+ case *ast.ArrayType:
+ return g.mapUnmarshalArr(sp, ptr)
+ }
+ return errors.New("Unknown type")
+}
+
+func (g *Gen) mapUnmarshalString(ptr bool) error {
+ if ptr {
+ err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ return err
+ }
+ } else {
+ err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalBool(ptr bool) error {
+ if ptr {
+ err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ return err
+ }
+ } else {
+ err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalInt(intLen string, ptr bool) error {
+ if ptr {
+ err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ return err
+ }
+ } else {
+ err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalUint(intLen string, ptr bool) error {
+ if ptr {
+ err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ return err
+ }
+ } else {
+ err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalFloat(intLen string, ptr bool) error {
+ if ptr {
+ err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ return err
+ }
+ } else {
+ err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalStruct(st *ast.TypeSpec, ptr bool) error {
+ if ptr {
+ err := mapUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ return err
+ }
+ } else {
+ err := mapUnmarshalTpl["struct"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalArr(st *ast.TypeSpec, ptr bool) error {
+ err := mapUnmarshalTpl["arr"].tpl.Execute(g.b, struct {
+ TypeName string
+ }{st.Name.String()})
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/gojay/gen_map_unmarshal_tpl.go b/gojay/gen_map_unmarshal_tpl.go
@@ -0,0 +1,82 @@
+package main
+
+var mapUnmarshalTpl = templateList{
+ "def": &genTpl{
+ strTpl: "\n// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject" +
+ "\nfunc (v {{.TypeName}}) 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}} }
+`,
+ },
+ "string": &genTpl{
+ strTpl: ` var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ v[k] = str
+`,
+ },
+ "int": &genTpl{
+ strTpl: ` var i int{{.IntLen}}
+ if err := dec.Int{{.IntLen}}(&i); err != nil {
+ return err
+ }
+ v[k] = i
+`,
+ },
+ "uint": &genTpl{
+ strTpl: ` var i uint{{.IntLen}}
+ if err := dec.Uint{{.IntLen}}(&i); err != nil {
+ return err
+ }
+ v[k] = i
+`,
+ },
+ "float": &genTpl{
+ strTpl: ` var i float{{.IntLen}}
+ if err := dec.Float{{.IntLen}}(&i); err != nil {
+ return err
+ }
+ v[k] = i
+`,
+ },
+ "bool": &genTpl{
+ strTpl: ` var b bool
+ if err := dec.Bool(&b); err != nil {
+ return err
+ }
+ v[k] = b
+`,
+ },
+ "struct": &genTpl{
+ strTpl: ` var s = {{.StructName}}{}
+ if err := dec.Object(&s); err != nil {
+ return err
+ }
+ v[k] = s
+`,
+ },
+ "structPtr": &genTpl{
+ strTpl: ` var s = &{{.StructName}}{}
+ if err := dec.Object(s); err != nil {
+ return err
+ }
+ v[k] = s
+`,
+ },
+ "arr": &genTpl{
+ strTpl: ` var s = &{{.StructName}}{}
+ if err := dec.Array(s); err != nil {
+ return err
+ }
+ v[k] = s
+`,
+ },
+}
+
+func init() {
+ parseTemplates(mapUnmarshalTpl, "mapUnmarshal")
+}