gojay

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

commit 64fc2cdddab0ecee09ec942b9405acb17ee7e2dd
parent 7a748baed1f1ad343517effd3bf5a9c9fc64bf85
Author: Francois Parquet <francois.parquet@gmail.com>
Date:   Sun, 17 Mar 2019 23:57:32 +0800

Merge pull request #101 from francoispqt/feature/replace-generator

Feature/replace generator
Diffstat:
M.gitignore | 4++--
M.travis.yml | 8++++----
MGopkg.lock | 63+++++++++++++++++++++++++++++++++++++--------------------------
Dcodegen/README.md | 61-------------------------------------------------------------
Dcodegen/field.go | 161-------------------------------------------------------------------------------
Dcodegen/generator.go | 167-------------------------------------------------------------------------------
Dcodegen/generator_test.go | 72------------------------------------------------------------------------
Dcodegen/helper.go | 88-------------------------------------------------------------------------------
Dcodegen/options.go | 51---------------------------------------------------
Dcodegen/struct.go | 296-------------------------------------------------------------------------------
Dcodegen/template.go | 272-------------------------------------------------------------------------------
Dcodegen/test/annotated_struct/encoding.go | 307-------------------------------------------------------------------------------
Dcodegen/test/annotated_struct/encoding_test.go | 217-------------------------------------------------------------------------------
Dcodegen/test/annotated_struct/message.go | 19-------------------
Dcodegen/test/annotated_struct/sub_message.go | 10----------
Dcodegen/test/basic_struct/encoding.go | 280-------------------------------------------------------------------------------
Dcodegen/test/basic_struct/encoding_test.go | 122-------------------------------------------------------------------------------
Dcodegen/test/basic_struct/message.go | 18------------------
Dcodegen/test/basic_struct/sub_message.go | 10----------
Dcodegen/test/embedded_struct/encoding.go | 336-------------------------------------------------------------------------------
Dcodegen/test/embedded_struct/encoding_test.go | 109-------------------------------------------------------------------------------
Dcodegen/test/embedded_struct/message.go | 22----------------------
Dcodegen/test/embedded_struct/sub_message.go | 9---------
Dcodegen/test/pooled_struct/encoding.go | 326-------------------------------------------------------------------------------
Dcodegen/test/pooled_struct/encoding_test.go | 125-------------------------------------------------------------------------------
Dcodegen/test/pooled_struct/message.go | 18------------------
Dcodegen/test/pooled_struct/sub_message.go | 10----------
Dgojay/Gopkg.lock | 14--------------
Dgojay/Gopkg.toml | 26--------------------------
Dgojay/Makefile | 16----------------
Mgojay/README.md | 54++++++++++++++++++++++++++----------------------------
Agojay/codegen/field.go | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/generator.go | 251+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/generator_test.go | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/helper.go | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/options.go | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/struct.go | 344+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/template.go | 325+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rcodegen/template_test.go -> gojay/codegen/template_test.go | 0
Agojay/codegen/test/annotated_struct/encoding.go | 289++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/annotated_struct/encoding_test.go | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/annotated_struct/message.go | 21+++++++++++++++++++++
Agojay/codegen/test/annotated_struct/sub_message.go | 10++++++++++
Agojay/codegen/test/basic_struct/encoding.go | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/basic_struct/encoding_test.go | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/basic_struct/message.go | 18++++++++++++++++++
Agojay/codegen/test/basic_struct/sub_message.go | 10++++++++++
Agojay/codegen/test/embedded_struct/encoding.go | 309+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/embedded_struct/encoding_test.go | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/embedded_struct/message.go | 20++++++++++++++++++++
Agojay/codegen/test/embedded_struct/sub_message.go | 9+++++++++
Agojay/codegen/test/pooled_struct/encoding.go | 302++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/pooled_struct/encoding_test.go | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/codegen/test/pooled_struct/message.go | 15+++++++++++++++
Agojay/codegen/test/pooled_struct/sub_message.go | 10++++++++++
Dgojay/gen.go | 146-------------------------------------------------------------------------------
Dgojay/gen_array.go | 17-----------------
Dgojay/gen_array_marshal.go | 241-------------------------------------------------------------------------------
Dgojay/gen_array_marshal_tpl.go | 63---------------------------------------------------------------
Dgojay/gen_array_test.go | 436-------------------------------------------------------------------------------
Dgojay/gen_array_unmarshal.go | 230-------------------------------------------------------------------------------
Dgojay/gen_array_unmarshal_tpl.go | 70----------------------------------------------------------------------
Dgojay/gen_map.go | 19-------------------
Dgojay/gen_map_marshal.go | 201-------------------------------------------------------------------------------
Dgojay/gen_map_marshal_tpl.go | 60------------------------------------------------------------
Dgojay/gen_map_test.go | 616-------------------------------------------------------------------------------
Dgojay/gen_map_unmarshal.go | 242-------------------------------------------------------------------------------
Dgojay/gen_map_unmarshal_tpl.go | 82-------------------------------------------------------------------------------
Dgojay/gen_parse.go | 63---------------------------------------------------------------
Dgojay/gen_struct_marshal.go | 290-------------------------------------------------------------------------------
Dgojay/gen_struct_marshal_tpl.go | 42------------------------------------------
Dgojay/gen_struct_test.go | 433-------------------------------------------------------------------------------
Dgojay/gen_struct_unmarshal.go | 378-------------------------------------------------------------------------------
Dgojay/gen_struct_unmarshal_tpl.go | 72------------------------------------------------------------------------
Dgojay/gen_stuct.go | 30------------------------------
Dgojay/gen_tag.go | 65-----------------------------------------------------------------
Dgojay/gen_test.go | 24------------------------
Agojay/gojay.go | 23+++++++++++++++++++++++
Dgojay/main.go | 121-------------------------------------------------------------------------------
Dgojay/tests/maps.go | 4----
Dgojay/tests/slices.go | 16----------------
Dgojay/tests/structs.go | 44--------------------------------------------
Dgojay/vendor/github.com/fatih/structtag/.travis.yml | 4----
Dgojay/vendor/github.com/fatih/structtag/LICENSE | 60------------------------------------------------------------
Dgojay/vendor/github.com/fatih/structtag/README.md | 73-------------------------------------------------------------------------
Dgojay/vendor/github.com/fatih/structtag/tags.go | 303-------------------------------------------------------------------------------
Dgojay/vendor/github.com/fatih/structtag/tags_test.go | 390-------------------------------------------------------------------------------
Dgojay/visitor.go | 54------------------------------------------------------
Dgojaygen/gojaygen.go | 22----------------------
89 files changed, 3149 insertions(+), 8133 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,4 +1,5 @@ +vendor *.out *.log *.test -.vscode -\ No newline at end of file +.vscode diff --git a/.travis.yml b/.travis.yml @@ -6,9 +6,10 @@ go: - master script: - - go get github.com/stretchr/testify - - go test ./gojay -race + - go get github.com/golang/dep/cmd/dep github.com/stretchr/testify + - dep ensure -v -vendor-only + - go test ./gojay/codegen/test/... -race - go test -race -coverprofile=coverage.txt -covermode=atomic after_success: - - bash <(curl -s https://codecov.io/bash) -\ No newline at end of file + - bash <(curl -s https://codecov.io/bash) diff --git a/Gopkg.lock b/Gopkg.lock @@ -2,20 +2,20 @@ [[projects]] - digest = "1:a639b30711f62030ade1432a6bcf135c23c38607d1478d3ce53829ea2a664197" + digest = "1:3de22f0e7208f2e7fb8662fcab0aee360e3cae85d5f3649f3bf0b247f575d183" name = "cloud.google.com/go" packages = ["compute/metadata"] pruneopts = "" - revision = "0ebda48a7f143b1cce9eb37a8c1106ac762a3430" - version = "v0.34.0" + revision = "f52f9bc132541d2aa914f42100c36d10b1ef7e0c" + version = "v0.37.0" [[projects]] - digest = "1:f7ad09e1bd153d7de18080ccd5e7bef5ebdba9403aa3cde487f0daf7ef67820d" - name = "github.com/fatih/structtag" - packages = ["."] + digest = "1:0deddd908b6b4b768cfc272c16ee61e7088a60f7fe2f06c547bd3d8e1f8b8e77" + name = "github.com/davecgh/go-spew" + packages = ["spew"] pruneopts = "" - revision = "76ae1d6d2117609598c7d4e8f3e938145f204e8f" - version = "v1.0.0" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" [[projects]] digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a" @@ -26,16 +26,16 @@ version = "v1.0.1" [[projects]] - digest = "1:3dd078fda7500c341bc26cfbc6c6a34614f295a2457149fc1045cab767cbcf18" + digest = "1:529d738b7976c3848cae5cf3a8036440166835e389c1f617af701eeb12a0518d" name = "github.com/golang/protobuf" packages = ["proto"] pruneopts = "" - revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" - version = "v1.2.0" + revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30" + version = "v1.3.1" [[projects]] branch = "master" - digest = "1:212bebc561f4f654a653225868b2a97353cd5e160dc0b0bbc7232b06608474ec" + digest = "1:b7ac1ad7d98781c0168803a258d7bf8b3544fd59f356a0e2dd65c43312cf6aac" name = "github.com/mailru/easyjson" packages = [ ".", @@ -44,7 +44,7 @@ "jwriter", ] pruneopts = "" - revision = "60711f1a8329503b04e1c88535f419d0bb440bff" + revision = "1de009706dbeb9d05f18586f0735fcdb7c524481" [[projects]] digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d" @@ -55,15 +55,15 @@ version = "v0.8.1" [[projects]] - digest = "1:63af8a601bded721d750a90c29bd3e0f760d78f3e24b4d464ed1390f2b18e8d4" + digest = "1:f6848760d34f35b10268f8aa3862181c19b4855dc0578a7b7144dc30e6f980f1" name = "github.com/viant/assertly" packages = ["."] pruneopts = "" - revision = "43698254226bcf31ac1b1283bc9be5d6db553f8e" - version = "v0.3.0" + revision = "3810e26d53c2543cb1024e6287fc2a66814f6764" + version = "v0.4.4" [[projects]] - digest = "1:3e18fb0fe710202b9c43173c899175dc1bddb044dda2be257a3b961a836a1a14" + digest = "1:d9fb8cbd3cd93ee1d7358a060c0dcd6f737032f5261bf0185eb7e025066eb7c0" name = "github.com/viant/toolbox" packages = [ ".", @@ -73,12 +73,12 @@ "url", ] pruneopts = "" - revision = "a7e3c8c3c49e598d5f2ba1d4ca7e4694a9b7919b" - version = "v0.9.0" + revision = "ead0bb31abed8480acfa8b80792d94593500d34b" + version = "v0.19.1" [[projects]] branch = "master" - digest = "1:59b49c47c11a48f1054529207f65907c014ecf5f9a7c0d9c0f1616dec7b062ed" + digest = "1:ab3e9a81a5ec54c5d1ed41d0d6898ba88c6799fa401b784b06bdee8e432d872b" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -91,11 +91,11 @@ "ssh", ] pruneopts = "" - revision = "ff983b9c42bc9fbf91556e191cc8efb585c16908" + revision = "a1f597ede03a7bef967a422b5b3a5bd08805a01e" [[projects]] branch = "master" - digest = "1:7ec13687f85b25087fe05f6ea8dd116013a8263f8eb7e057da7664bc7599d2d4" + digest = "1:73e6624a8569db94b0d8f9b34052f73ef9598c380edbce36d89dbbc00941e976" name = "golang.org/x/net" packages = [ "context", @@ -103,11 +103,11 @@ "websocket", ] pruneopts = "" - revision = "915654e7eabcea33ae277abbecf52f0d8b7a9fdc" + revision = "9f648a60d9775ef5c977e7669d1673a7a67bef33" [[projects]] branch = "master" - digest = "1:f059b0adae6e4630f111d471f127fe69de1f5689ce35465bab387d646c17eb97" + digest = "1:ffae4a89b63a2c845533a393b3340f8696898b11b71da1b187f82f08135c23a0" name = "golang.org/x/oauth2" packages = [ ".", @@ -117,7 +117,18 @@ "jwt", ] pruneopts = "" - revision = "5dab4167f31cbd76b407f1486c86b40748bc5073" + revision = "e64efc72b421e893cbf63f17ba2221e7d6d0b0f3" + +[[projects]] + branch = "master" + digest = "1:9f5e388a2e01d646e82dc420469e21a6e0e7a526954ef1638009bc87eacf578e" + name = "golang.org/x/sys" + packages = [ + "cpu", + "unix", + ] + pruneopts = "" + revision = "a2f829d7f35f2ed1c3520c553a6226495455cae0" [[projects]] digest = "1:bc09e719c4e2a15d17163f5272d9a3131c45d77542b7fdc53ff518815bc19ab3" @@ -150,7 +161,7 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ - "github.com/fatih/structtag", + "github.com/davecgh/go-spew/spew", "github.com/go-errors/errors", "github.com/mailru/easyjson", "github.com/mailru/easyjson/jlexer", diff --git a/codegen/README.md b/codegen/README.md @@ -1,61 +0,0 @@ -# Gojay code generator -This package provides a command line tool to generate gojay's marshaling and unmarshing interface implementation for custom type(s) - - -## Get started - -```sh -go install github.com/francoispqt/gojay/gojaygen -``` - -## Generate code - -### Basic command -The basic command is straightforward and easy to use: -```sh -cd $GOPATH/src/mycoany/myproject -gojaygen -p myproject -``` -If you just want to the output to stdout, omit the third parameter. - -### Using flags -- s file/dir path, can be a relative or absolute path -- t root types to generate with all its dependencies (comma separated) -- a annotation tag used to read meta data (default: json) -- o output file (relative or absolute path) -- p reuse object witt sync.Pool - -Examples: - -- Specific type in a go package, write to a file: -```sh -gojay -s /tmp/myproj -t SomeType -o output.go -``` - -- Specific types in a file, with custom tag, write to stdout -```sh -gojay -s somegofile.go -a gojay -t SomeType -``` - - -## Generator tags -You can add tags to your structs to control: - -- the JSON key -- skip a struct field -- the use of omitempty methods for marshaling -- timeFormat (java style data format) -- timeLayout (golang time layout) - - -### Example: -```go -type A struct { - Str string `json:"string"` - StrOmitEmpty string `json:"stringOrEmpty,omitempty"` - Skip string `json:"-"` - StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"` - EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"` -} -``` - diff --git a/codegen/field.go b/codegen/field.go @@ -1,161 +0,0 @@ -package codegen - -import ( - "fmt" - "github.com/viant/toolbox" - "strings" -) - -//Field represents a field. -type Field struct { - Key string - Init string - OmitEmpty string - TimeLayout string - Name string - Accessor string - Mutator string - Receiver string //alias and type name - Alias string //object alias name - Var string //variable for this field - Type string - RawType string - HelperType string - ComponentType string - RawComponentType string - IsPointerComponent bool - - PointerModifier string //takes field pointer, "&" if field is not a pointer type - DereferenceModifier string //take pointer value, i.e "*" if field has a pointer type - - ComponentPointerModifier string //takes item pointer if needed,i.e - ComponentDereferenceModifier string //de reference value if needed, i.e - ComponentInitModifier string //takes item pointer if type is not a pointer type - ComponentInit string //initialises component type - - DecodingMethod string - EncodingMethod string - PoolName string //pool name associated with this field - ResetDependency string - Reset string - IsAnonymous bool - IsPointer bool - IsSlice bool -} - -//NewField returns a new field -func NewField(owner *Struct, field *toolbox.FieldInfo, fieldType *toolbox.TypeInfo) (*Field, error) { - typeName := normalizeTypeName(field.TypeName) - var result = &Field{ - IsAnonymous: field.IsAnonymous, - Name: field.Name, - RawType: field.TypeName, - IsPointer: field.IsPointer, - Key: getJSONKey(owner.options, field), - Receiver: owner.Alias + " *" + owner.TypeInfo.Name, - Type: typeName, - Mutator: owner.Alias + "." + field.Name, - Accessor: owner.Alias + "." + field.Name, - ComponentType: field.ComponentType, - IsPointerComponent: field.IsPointerComponent, - Var: firstLetterToLowercase(field.Name), - Init: fmt.Sprintf("%v{}", typeName), - TimeLayout: "time.RFC3339", - IsSlice: field.IsSlice, - PoolName: getPoolName(field.TypeName), - Alias: owner.Alias, - Reset: "nil", - } - var err error - if field.IsPointer { - result.DereferenceModifier = "*" - result.Init = "&" + result.Init - } else { - result.PointerModifier = "&" - - } - if field.IsSlice { - result.HelperType = getSliceHelperTypeName(field.ComponentType, field.IsPointerComponent) - result.PoolName = getPoolName(field.ComponentType) - } else if fieldType != nil { - result.HelperType = getSliceHelperTypeName(fieldType.Name, field.IsPointerComponent) - } - - if options := getTagOptions(field.Tag, "timeLayout"); len(options) > 0 { - result.TimeLayout = wrapperIfNeeded(options[0], `"`) - } else if options := getTagOptions(field.Tag, "timeFormat"); len(options) > 0 { - result.TimeLayout = wrapperIfNeeded(toolbox.DateFormatToLayout(options[0]), `"`) - } - if strings.Contains(field.Tag, "omitEmpty") { - result.OmitEmpty = "OmitEmpty" - } - if owner.options.PoolObjects { - if field.IsPointer && !strings.HasSuffix(field.TypeName, ".Time") { - poolName := getPoolName(field.TypeName) - result.Init = fmt.Sprintf(`%v.Get().(*%v)`, poolName, field.TypeName) - } - } - - encodingMethod := field.ComponentType - if encodingMethod == "" { - encodingMethod = result.Type - } - result.DecodingMethod = firstLetterToUppercase(encodingMethod) - result.EncodingMethod = firstLetterToUppercase(encodingMethod) - - switch typeName { - case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": - result.Reset = "0" - case "float32", "float64": - result.Reset = "0.0" - case "string": - result.Reset = `""` - - case "bool": - result.Reset = "false" - default: - if field.IsSlice && owner.Type(field.ComponentType) != nil { - var itemPointer = "" - if !field.IsPointerComponent { - itemPointer = "&" - } - result.ResetDependency, err = expandFieldTemplate(poolSliceInstanceRelease, struct { - PoolName string - Accessor string - PointerModifier string - }{PoolName: result.PoolName, Accessor: result.Accessor, PointerModifier: itemPointer}) - if err != nil { - return nil, err - } - - } else if field.IsPointer && fieldType != nil { - result.ResetDependency, err = expandFieldTemplate(poolInstanceRelease, struct { - PoolName string - Accessor string - }{PoolName: result.PoolName, Accessor: result.Accessor}) - if err != nil { - return nil, err - } - } - - } - if field.IsSlice || field.IsPointer { - result.Reset = "nil" - } - - if result.IsPointerComponent { - result.ComponentInit = "&" + result.ComponentType + "{}" - result.RawComponentType = "*" + result.ComponentType - - result.ComponentDereferenceModifier = "*" - result.ComponentInitModifier = "&" - - } else { - result.ComponentInit = result.ComponentType + "{}" - result.RawComponentType = result.ComponentType - - result.ComponentPointerModifier = "&" - } - - return result, nil -} diff --git a/codegen/generator.go b/codegen/generator.go @@ -1,167 +0,0 @@ -package codegen - -import ( - "fmt" - "github.com/viant/toolbox" - "io/ioutil" - "strings" -) - -type Generator struct { - fileInfo *toolbox.FileSetInfo - types map[string]string - structTypes map[string]string - sliceTypes map[string]string - pooledObjects map[string]string - poolInit map[string]string - imports map[string]bool - filedInit []string - Pkg string - Code string - Init string - Imports string - options *Options -} - -func (g *Generator) Type(typeName string) *toolbox.TypeInfo { - return g.fileInfo.Type(typeName) -} - -func (g *Generator) addImport(pkg string) { - g.imports[`"`+pkg+`"`] = true -} - -func (g *Generator) init() { - g.filedInit = []string{} - g.imports = map[string]bool{} - g.pooledObjects = map[string]string{} - g.structTypes = map[string]string{} - g.sliceTypes = map[string]string{} - g.poolInit = map[string]string{} - g.addImport("github.com/francoispqt/gojay") -} - -func (g *Generator) Generate(options *Options) error { - if err := options.Validate(); err != nil { - return err - } - g.options = options - g.init() - - if options.PoolObjects { - g.addImport("sync") - } - if err := g.readPackageCode(options.Source); err != nil { - return err - } - for _, rootType := range options.Types { - if err := g.generateStructCode(rootType); err != nil { - return err - } - } - g.Imports = strings.Join(toolbox.MapKeysToStringSlice(g.imports), "\n") - return g.writeCode() -} - -func (g *Generator) writeCode() error { - var generatedCode = []string{} - for _, code := range g.pooledObjects { - generatedCode = append(generatedCode, code) - } - generatedCode = append(generatedCode, "") - for _, code := range g.sliceTypes { - generatedCode = append(generatedCode, code) - } - generatedCode = append(generatedCode, "") - for _, code := range g.structTypes { - generatedCode = append(generatedCode, code) - } - - for _, code := range g.poolInit { - if g.Init != "" { - g.Init += "\n" - } - g.Init += code - } - g.Code = strings.Join(generatedCode, "\n") - - expandedCode, err := expandBlockTemplate(fileCode, g) - if err != nil { - return err - } - if g.options.Dest == "" { - fmt.Print(expandedCode) - return nil - } - return ioutil.WriteFile(g.options.Dest, []byte(expandedCode), 0644) -} - -func (g *Generator) generatePrimitiveArray(field *Field) error { - key := field.ComponentType + toolbox.AsString(field.IsPointerComponent) - if _, ok := g.sliceTypes[key]; ok { - return nil - } - code, err := expandBlockTemplate(baseTypeSlice, field) - g.sliceTypes[key] = code - return err -} - -func (g *Generator) generateObjectArray(field *Field) error { - if _, ok := g.sliceTypes[field.RawComponentType]; ok { - return nil - } - - if err := g.generateStructCode(field.ComponentType); err != nil { - return err - } - code, err := expandBlockTemplate(structTypeSlice, field) - if err != nil { - return err - } - g.sliceTypes[field.RawComponentType] = code - return err -} - -func (g *Generator) generatePool(structType string) error { - if !g.options.PoolObjects { - return nil - } - var err error - if g.pooledObjects[structType], err = expandBlockTemplate(poolVar, struct { - PoolName string - }{getPoolName(structType)}); err == nil { - g.poolInit[structType], err = expandBlockTemplate(poolInit, struct { - PoolName string - Type string - }{getPoolName(structType), structType}) - } - return err - -} - -func (g *Generator) generateStructCode(structType string) error { - structType = normalizeTypeName(structType) - typeInfo := g.Type(structType) - if typeInfo == nil { - return nil - } - if _, hasCode := g.structTypes[structType]; hasCode { - return nil - } - g.generatePool(structType) - aStruct := NewStruct(typeInfo, g) - code, err := aStruct.Generate() - if err != nil { - return err - } - g.structTypes[structType] = code - return nil -} - -func (g *Generator) readPackageCode(pkgPath string) error { - var err error - fragments := strings.Split(pkgPath, "/") - g.Pkg = fragments[len(fragments)-1] - g.fileInfo, err = toolbox.NewFileSetInfo(pkgPath) - return err -} diff --git a/codegen/generator_test.go b/codegen/generator_test.go @@ -1,72 +0,0 @@ -package codegen - -import ( - "github.com/stretchr/testify/assert" - "github.com/viant/toolbox" - "log" - "path" - "testing" -) - -func TestGenerator_Generate(t *testing.T) { - - parent := path.Join(toolbox.CallerDirectory(3), "test") - - var useCases = []struct { - description string - options *Options - hasError bool - }{ - { - description: "basic struct code generation", - options: &Options{ - Source: path.Join(parent, "basic_struct"), - Types: []string{"Message"}, - Dest: path.Join(parent, "basic_struct", "encoding.go"), - }, - }, - - { - description: "struct with pool code generation", - options: &Options{ - Source: path.Join(parent, "pooled_struct"), - Types: []string{"Message"}, - Dest: path.Join(parent, "pooled_struct", "encoding.go"), - PoolObjects: true, - }, - }, - { - description: "struct with embedded type code generation", - options: &Options{ - Source: path.Join(parent, "embedded_struct"), - Types: []string{"Message"}, - Dest: path.Join(parent, "embedded_struct", "encoding.go"), - PoolObjects: false, - }, - }, - { - description: "struct with json annotation and time/foarmat|layouat generation", - options: &Options{ - Source: path.Join(parent, "annotated_struct"), - Types: []string{"Message"}, - Dest: path.Join(parent, "annotated_struct", "encoding.go"), - PoolObjects: false, - TagName: "json", - }, - }, - } - - for _, useCase := range useCases { - gen := &Generator{} - err := gen.Generate(useCase.options) - if useCase.hasError { - assert.NotNil(t, err, useCase.description) - continue - } - if !assert.Nil(t, err, useCase.description) { - log.Fatal(err) - continue - } - } - -} diff --git a/codegen/helper.go b/codegen/helper.go @@ -1,88 +0,0 @@ -package codegen - -import ( - "github.com/viant/toolbox" - "reflect" - "strings" -) - -func firstLetterToUppercase(text string) string { - return strings.ToUpper(string(text[0:1])) + string(text[1:]) -} - -func firstLetterToLowercase(text string) string { - return strings.ToLower(string(text[0:1])) + string(text[1:]) -} - -func extractReceiverAlias(structType string) string { - var result = string(structType[0]) - for i := len(structType) - 1; i > 0; i-- { - aChar := string(structType[i]) - lowerChar := strings.ToLower(aChar) - if lowerChar != aChar { - result = lowerChar - break - } - } - return strings.ToLower(result) -} - -func getTagOptions(tag, key string) []string { - if tag == "" { - return nil - } - var structTag = reflect.StructTag(strings.Replace(tag, "`", "", len(tag))) - options, ok := structTag.Lookup(key) - if !ok { - return nil - } - return strings.Split(options, ",") -} - -func getSliceHelperTypeName(typeName string, isPointer bool) string { - if typeName == "" { - return "" - } - var pluralName = firstLetterToUppercase(typeName) + "s" - if isPointer { - pluralName += "Ptr" - } - return pluralName -} - -func isSkipable(options *Options, field *toolbox.FieldInfo) bool { - if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 { - for _, candidate := range options { - if candidate == "-" { - return true - } - } - } - return false -} - -func wrapperIfNeeded(text, wrappingChar string) string { - if strings.HasPrefix(text, wrappingChar) { - return text - } - return wrappingChar + text + wrappingChar -} - -func getPoolName(typeName string) string { - typeName = strings.Replace(typeName, "*", "", 1) - return typeName + "Pool" -} - -func getJSONKey(options *Options, field *toolbox.FieldInfo) string { - var key = field.Name - if field.Tag != "" { - if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 { - key = options[0] - } - } - return key -} - -func normalizeTypeName(typeName string) string { - return strings.Replace(typeName, "*", "", strings.Count(typeName, "*")) -} diff --git a/codegen/options.go b/codegen/options.go @@ -1,51 +0,0 @@ -package codegen - -import ( - "flag" - "github.com/go-errors/errors" - "github.com/viant/toolbox" - "github.com/viant/toolbox/url" - "strings" -) - -type Options struct { - Source string - Dest string - Types []string - PoolObjects bool - TagName string -} - -func (o *Options) Validate() error { - if o.Source == "" { - return errors.New("Source was empty") - } - if len(o.Types) == 0 { - return errors.New("Types was empty") - } - return nil -} - -const ( - optionKeySource = "s" - optionKeyDest = "o" - optionKeyTypes = "t" - optionKeyTagName = "a" - optionKeyPoolObjects = "p" -) - -//NewOptionsWithFlagSet creates a new options for the supplide flagset -func NewOptionsWithFlagSet(set *flag.FlagSet) *Options { - toolbox.Dump(set) - - var result = &Options{} - result.Dest = set.Lookup(optionKeyDest).Value.String() - result.Source = set.Lookup(optionKeySource).Value.String() - result.PoolObjects = toolbox.AsBoolean(set.Lookup(optionKeyPoolObjects).Value.String()) - result.TagName = set.Lookup(optionKeyTagName).Value.String() - result.Types = strings.Split(set.Lookup(optionKeyTypes).Value.String(), ",") - if result.Source == "" { - result.Source = url.NewResource(".").ParsedURL.Path - } - return result -} diff --git a/codegen/struct.go b/codegen/struct.go @@ -1,296 +0,0 @@ -package codegen - -import ( - "fmt" - "github.com/viant/toolbox" - "strings" -) - -type Struct struct { - *toolbox.TypeInfo - referenced *toolbox.TypeInfo - *Generator - Alias string - Init string - Body string -} - -//Generate generates decoderCode + structRelease + encoderCode -func (s *Struct) Generate() (string, error) { - return s.generateEncoding(s.TypeInfo) -} - -func (s *Struct) generateEncoding(structInfo *toolbox.TypeInfo) (string, error) { - var initEmbedded, decodingCases, err = s.generateFieldDecoding(structInfo.Fields()) - if err != nil { - return "", err - } - - encodingCases, err := s.generateFieldEncoding(structInfo.Fields()) - if err != nil { - return "", err - } - var resetCode = "" - if s.options.PoolObjects { - resetCode, err = s.generateReset(structInfo.Fields()) - if err != nil { - return "", err - } - } - var data = struct { - Receiver string - Alias string - InitEmbedded string - EncodingCases string - DecodingCases string - Reset string - FieldCount int - }{ - Receiver: s.Alias + " *" + s.Name, - DecodingCases: strings.Join(decodingCases, "\n"), - EncodingCases: strings.Join(encodingCases, "\n"), - FieldCount: len(decodingCases), - InitEmbedded: initEmbedded, - Reset: resetCode, - Alias: s.Alias, - } - return expandBlockTemplate(encodingStructType, data) -} - -func (s *Struct) generateReset(fields []*toolbox.FieldInfo) (string, error) { - fieldReset, err := s.generateFieldReset(fields) - if err != nil { - return "", nil - } - return expandBlockTemplate(resetStruct, struct { - Reset string - Receiver string - }{ - Reset: strings.Join(fieldReset, "\n"), - Receiver: s.Alias + " *" + s.Name, - }) -} - -func (s *Struct) generateFieldReset(fields []*toolbox.FieldInfo) ([]string, error) { - fieldReset := []string{} - for i := range fields { - var templateKey = -1 - fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) - field, err := NewField(s, fields[i], fieldTypeInfo) - if err != nil { - return nil, err - } - if field.IsPointer || field.IsSlice || (fieldTypeInfo != nil && fieldTypeInfo.IsSlice) { - templateKey = resetFieldValue - } else { - switch field.Type { - case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "string", "bool", "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64", "[]byte": - templateKey = resetFieldValue - } - } - if templateKey != -1 { - code, err := expandFieldTemplate(templateKey, field) - if err != nil { - return nil, err - } - fieldReset = append(fieldReset, code) - } - } - return fieldReset, nil -} - -func (s *Struct) generateFieldDecoding(fields []*toolbox.FieldInfo) (string, []string, error) { - fieldCases := []string{} - var initCode = "" - for i := range fields { - if isSkipable(s.options, fields[i]) { - continue - } - var templateKey = -1 - fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) - field, err := NewField(s, fields[i], fieldTypeInfo) - if err != nil { - return "", nil, err - } - if fieldTypeInfo != nil { - if err = s.generateStructCode(fieldTypeInfo.Name); err != nil { - return "", nil, err - } - } - - if field.IsAnonymous { - if fieldTypeInfo != nil { - if field.IsPointer { - init, err := expandBlockTemplate(embeddedStructInit, field) - if err != nil { - return "", nil, err - } - initCode += init - } - init, embeddedCases, err := s.generateFieldDecoding(fieldTypeInfo.Fields()) - if err != nil { - return "", nil, err - } - initCode += init - fieldCases = append(fieldCases, embeddedCases...) - } - continue - } - - main: - switch field.Type { - case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": - templateKey = decodeBaseType - case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64": - templateKey = decodeBaseTypeSlice - s.generatePrimitiveArray(field) - case "[]byte": - templateKey = decodeRawType - default: - - if fieldTypeInfo != nil { - if !(field.IsSlice || fieldTypeInfo.IsSlice) { - - templateKey = decodeStruct - break main - } - - switch fieldTypeInfo.ComponentType { - case "byte": - templateKey = decodeRawType - break main - - case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": - s.generatePrimitiveArray(field) - templateKey = decodeBaseTypeSlice - break main - - } - - if err = s.generateStructCode(field.ComponentType); err != nil { - return "", nil, err - } - - templateKey = decodeStructSlice - if err = s.generateObjectArray(field); err != nil { - return "", nil, err - } - - break main - } else if field.IsSlice { - templateKey = decodeStructSlice - if err = s.generateObjectArray(field); err != nil { - return "", nil, err - } - - } else if strings.HasSuffix(field.Type, "time.Time") { - templateKey = decodeTime - s.addImport("time") - } else { - templateKey = decodeUnknown - } - } - if templateKey != -1 { - decodingCase, err := expandFieldTemplate(templateKey, field) - if err != nil { - return "", nil, err - } - fieldCases = append(fieldCases, decodingCase) - } - - } - return initCode, fieldCases, nil -} - -func (s *Struct) generateEmbeddedFieldEncoding(field *Field, fieldTypeInfo *toolbox.TypeInfo) ([]string, error) { - var result = []string{} - if fieldTypeInfo != nil { - embeddedCases, err := s.generateFieldEncoding(fieldTypeInfo.Fields()) - if err != nil { - return nil, err - } - if field.IsPointer { - result = append(result, fmt.Sprintf(" if %v != nil {", field.Accessor)) - for _, code := range embeddedCases { - result = append(result, " "+code) - } - result = append(result, " }") - } else { - result = append(result, embeddedCases...) - } - } - return result, nil -} - -func (s *Struct) generateFieldEncoding(fields []*toolbox.FieldInfo) ([]string, error) { - fieldCases := []string{} - for i := range fields { - if isSkipable(s.options, fields[i]) { - continue - } - var templateKey = -1 - fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) - field, err := NewField(s, fields[i], fieldTypeInfo) - if err != nil { - return nil, err - } - if field.IsAnonymous { - embedded, err := s.generateEmbeddedFieldEncoding(field, fieldTypeInfo) - if err != nil { - return nil, err - } - fieldCases = append(fieldCases, embedded...) - continue - } - main: - switch field.Type { - case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": - templateKey = encodeBaseType - case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64": - templateKey = encodeBaseTypeSlice - s.generatePrimitiveArray(field) - case "[]byte": - templateKey = encodeRawType - default: - if fieldTypeInfo != nil { - if !(field.IsSlice || fieldTypeInfo.IsSlice) { - templateKey = encodeStruct - break main - } - switch fieldTypeInfo.ComponentType { - case "byte": - templateKey = encodeRawType - break main - case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": - templateKey = decodeBaseTypeSlice - break main - } - templateKey = encodeStructSlice - break main - } else if field.IsSlice { - templateKey = encodeStructSlice - } else if strings.HasSuffix(field.Type, "time.Time") { - templateKey = encodeTime - } else { - templateKey = encodeUnknown - } - } - if templateKey != -1 { - decodingCase, err := expandFieldTemplate(templateKey, field) - if err != nil { - return nil, err - } - fieldCases = append(fieldCases, decodingCase) - } - - } - return fieldCases, nil -} - -func NewStruct(info *toolbox.TypeInfo, generator *Generator) *Struct { - return &Struct{ - TypeInfo: info, - Generator: generator, - Alias: extractReceiverAlias(info.Name), - } -} diff --git a/codegen/template.go b/codegen/template.go @@ -1,272 +0,0 @@ -package codegen - -import ( - "bytes" - "fmt" - "text/template" -) - -const ( - decodeBaseType = iota - encodeBaseType - decodeBaseTypeSlice - encodeBaseTypeSlice - decodeRawType - encodeRawType - decodeStruct - encodeStruct - - decodeStructSlice - encodeStructSlice - decodeTime - encodeTime - - decodeUnknown - encodeUnknown - - resetFieldValue - poolInstanceRelease - poolSliceInstanceRelease -) - -var fieldTemplate = map[int]string{ - decodeBaseType: ` case "{{.Key}}": -{{if .IsPointer}} var value {{.Type}} - err := dec.{{.DecodingMethod}}(&value) - if err == nil { - {{.Accessor}} = &value - } - return err -{{else}} return dec.{{.DecodingMethod}}(&{{.Accessor}}){{end}} -`, - encodeBaseType: ` enc.{{.EncodingMethod}}Key{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Accessor}})`, - - decodeBaseTypeSlice: ` case "{{.Key}}": - var aSlice = {{.HelperType}}{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - {{.Mutator}} = {{.RawType}}(aSlice) - } - return err -`, - encodeBaseTypeSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}}) - enc.ArrayKey{{.OmitEmpty}}("{{.Key}}",{{.Var}}Slice)`, - - decodeRawType: ` case "{{.Key}}": - var value = gojay.EmbeddedJSON{} - err := dec.AddEmbeddedJSON(&value) - if err == nil && len(value) > 0 { - {{.Mutator}} = {{.Type}}(value) - } - return err -`, - - encodeRawType: ` var {{.Var}}Slice = gojay.EmbeddedJSON({{.Accessor}}) - enc.AddEmbeddedJSONKey{{.OmitEmpty}}("{{.Key}}", &{{.Var}}Slice)`, - decodeStruct: ` case "{{.Key}}":{{if .IsPointer}} - var value = {{.Init}} - err := dec.Object(value) - if err == nil { - {{.Mutator}} = value - } -{{else}} - err := dec.Object(&{{.Mutator}}) -{{end}} - return err -`, - encodeStruct: ` enc.ObjectKey{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}})`, - - decodeStructSlice: ` case "{{.Key}}": - var aSlice = {{.HelperType}}{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - {{.Mutator}} = {{.RawType}}(aSlice) - } - return err - `, - - encodeStructSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}}) - enc.ArrayKey{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Var}}Slice)`, - - decodeTime: ` case "{{.Key}}": - var format = {{.TimeLayout}} - var value = {{.Init}} - err := dec.DecodeTime({{.PointerModifier}}value, format){{if .IsPointer}} - if err == nil { - {{.Mutator}} = value - }{{end}} - return err -`, - - encodeTime: `{{if .IsPointer}} if {{.Accessor}} != nil { - enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}}) - }{{else}} enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}}){{end}}`, - decodeUnknown: ` case "{{.Key}}": - //TODO - //dec.Any({{.Accessor}}) - return nil -`, - encodeUnknown: ` - //TODO - //enc.Any({{.Accessor}}) - -`, - resetFieldValue: `{{if .ResetDependency}}{{.ResetDependency}} -{{end}} {{.Mutator}} = {{.Reset}}`, - poolInstanceRelease: ` {{.PoolName}}.Put({{.Accessor}})`, - - poolSliceInstanceRelease: ` for i := range {{.Accessor}} { - {{.Accessor}}[i].Reset() - {{.PoolName}}.Put({{.PointerModifier}}{{.Accessor}}[i]) - }`, -} - -const ( - fileCode = iota - encodingStructType - baseTypeSlice - structTypeSlice - resetStruct - poolVar - poolInit - embeddedStructInit -) - -var blockTemplate = map[int]string{ - fileCode: `// Code generated by GoJayGen. DO NOT EDIT.\n\n -package {{.Pkg}} - -import ( -{{.Imports}} -) - -{{if .Init}} -func init() { -{{.Init}} -} -{{end}} - -{{.Code}} - -`, - encodingStructType: ` - -//MarshalJSONObject implements MarshalerJSONObject -func ({{.Receiver}}) MarshalJSONObject(enc *gojay.Encoder) { -{{.EncodingCases}} -} - -//IsNil checks if instance is nil -func ({{.Receiver}}) IsNil() bool { - return {{.Alias}} == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func ({{.Receiver}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { -{{.InitEmbedded}} - switch k { -{{.DecodingCases}} - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func ({{.Receiver}}) NKeys() int { return {{.FieldCount}} } - -{{.Reset}} - -`, - - baseTypeSlice: ` - -type {{.HelperType}} {{.RawType}} - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value {{.ComponentType}} - if err := dec.{{.DecodingMethod}}(&value); err != nil { - return err - } - *a = append(*a, {{.ComponentInitModifier}}value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.{{.EncodingMethod}}({{.ComponentDereferenceModifier}}item) - } -} - -//IsNil checks if array is nil -func (a {{.HelperType}}) IsNil() bool { - return len(a) == 0 -} -`, - - structTypeSlice: ` -type {{.HelperType}} {{.RawType}} - -func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = {{.ComponentInit}} - if err := dec.Object({{.ComponentPointerModifier}}value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object({{.ComponentPointerModifier}}s[i]) - } -} - - -func (s {{.HelperType}}) IsNil() bool { - return len(s) == 0 -} - - -`, - - resetStruct: ` -// Reset reset fields -func ({{.Receiver}}) Reset() { -{{.Reset}} -} -`, - - poolVar: `var {{.PoolName}} *sync.Pool`, - poolInit: ` {{.PoolName}} = &sync.Pool { - New: func()interface{} { - return &{{.Type}}{} - }, - }`, - embeddedStructInit: `if {{.Accessor}} == nil { - {{.Accessor}} = {{.Init}} - }`, -} - -func expandTemplate(namespace string, dictionary map[int]string, key int, data interface{}) (string, error) { - var id = fmt.Sprintf("%v_%v", namespace, key) - textTemplate, ok := dictionary[key] - if !ok { - return "", fmt.Errorf("failed to lookup template for %v.%v", namespace, key) - } - temlate, err := template.New(id).Parse(textTemplate) - if err != nil { - return "", fmt.Errorf("fiailed to parse template %v %v, due to %v", namespace, key, err) - } - writer := new(bytes.Buffer) - err = temlate.Execute(writer, data) - return writer.String(), err -} - -func expandFieldTemplate(key int, data interface{}) (string, error) { - return expandTemplate("fieldTemplate", fieldTemplate, key, data) -} - -func expandBlockTemplate(key int, data interface{}) (string, error) { - return expandTemplate("blockTemplate", blockTemplate, key, data) -} diff --git a/codegen/test/annotated_struct/encoding.go b/codegen/test/annotated_struct/encoding.go @@ -1,307 +0,0 @@ -// Code generated by GoJayGen. DO NOT EDIT.\n\n -package annotated_struct - -import ( -"github.com/francoispqt/gojay" -"time" -) - - - - - - -type Ints []int - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value int - if err := dec.Int(&value); err != nil { - return err - } - *a = append(*a, value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Int(item) - } -} - -//IsNil checks if array is nil -func (a Ints) IsNil() bool { - return len(a) == 0 -} - - - -type Float32sPtr []*float32 - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value float32 - if err := dec.Float32(&value); err != nil { - return err - } - *a = append(*a, &value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Float32(*item) - } -} - -//IsNil checks if array is nil -func (a Float32sPtr) IsNil() bool { - return len(a) == 0 -} - - -type SubMessagesPtr []*SubMessage - -func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = &SubMessage{} - if err := dec.Object(value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(s[i]) - } -} - - -func (s SubMessagesPtr) IsNil() bool { - return len(s) == 0 -} - - - - -type SubMessages []SubMessage - -func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = SubMessage{} - if err := dec.Object(&value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(&s[i]) - } -} - - -func (s SubMessages) IsNil() bool { - return len(s) == 0 -} - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", m.Id) - enc.StringKey("description", m.Description) - enc.TimeKey("startDate", &m.StartTime, "2006-01-02 15:04:05") - if m.EndTime != nil { - enc.TimeKey("endDate", m.EndTime, "2006-01-02 15:04:05") - } -} - -//IsNil checks if instance is nil -func (m *SubMessage) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "id": - return dec.Int(&m.Id) - - case "description": - return dec.String(&m.Description) - - case "startDate": - var format = "2006-01-02 15:04:05" - var value = time.Time{} - err := dec.DecodeTime(&value, format) - return err - - case "endDate": - var format = "2006-01-02 15:04:05" - var value = &time.Time{} - err := dec.DecodeTime(value, format) - if err == nil { - m.EndTime = value - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *SubMessage) NKeys() int { return 4 } - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (p *Paylod) MarshalJSONObject(enc *gojay.Encoder) { - -} - -//IsNil checks if instance is nil -func (p *Paylod) IsNil() bool { - return p == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (p *Paylod) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (p *Paylod) NKeys() int { return 0 } - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("id", m.Id) - enc.StringKey("name", m.Name) - enc.Float64Key("price", m.Price) - var intsSlice = Ints(m.Ints) - enc.ArrayKey("ints",intsSlice) - var floatsSlice = Float32sPtr(m.Floats) - enc.ArrayKey("floats",floatsSlice) - enc.ObjectKey("subMessageX", m.SubMessageX) - var messagesXSlice = SubMessagesPtr(m.MessagesX) - enc.ArrayKey("messagesX", messagesXSlice) - enc.ObjectKey("SubMessageY", &m.SubMessageY) - var messagesYSlice = SubMessages(m.MessagesY) - enc.ArrayKey("MessagesY", messagesYSlice) - enc.BoolKey("enabled", *m.IsTrue) - var payloadSlice = gojay.EmbeddedJSON(m.Payload) - enc.AddEmbeddedJSONKey("data", &payloadSlice) -} - -//IsNil checks if instance is nil -func (m *Message) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "id": - return dec.Int(&m.Id) - - case "name": - return dec.String(&m.Name) - - case "price": - return dec.Float64(&m.Price) - - case "ints": - var aSlice = Ints{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Ints = []int(aSlice) - } - return err - - case "floats": - var aSlice = Float32sPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Floats = []*float32(aSlice) - } - return err - - case "subMessageX": - var value = &SubMessage{} - err := dec.Object(value) - if err == nil { - m.SubMessageX = value - } - - return err - - case "messagesX": - var aSlice = SubMessagesPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesX = []*SubMessage(aSlice) - } - return err - - case "SubMessageY": - err := dec.Object(&m.SubMessageY) - - return err - - case "MessagesY": - var aSlice = SubMessages{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesY = []SubMessage(aSlice) - } - return err - - case "enabled": - var value bool - err := dec.Bool(&value) - if err == nil { - m.IsTrue = &value - } - return err - - - case "data": - var value = gojay.EmbeddedJSON{} - err := dec.AddEmbeddedJSON(&value) - if err == nil && len(value) > 0 { - m.Payload = Paylod(value) - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *Message) NKeys() int { return 11 } - - - - - diff --git a/codegen/test/annotated_struct/encoding_test.go b/codegen/test/annotated_struct/encoding_test.go @@ -1,216 +0,0 @@ -package annotated_struct - -import ( - "testing" - "github.com/stretchr/testify/assert" - "github.com/francoispqt/gojay" - "github.com/viant/assertly" - "log" - "bytes" -) - -func TestMessage_Unmarshal(t *testing.T) { - - - - input := `{ - "id": 1022, - "name": "name acc", - "price": 13.3, - "ints": [ - 1, - 2, - 5 - ], - "floats": [ - 2.3, - 4.6, - 7.4 - ], - "subMessageX": { - "id": 102, - "description": "abcd" - }, - "messagesX": [ - { - "id": 2102, - "description": "abce" - } - ], - "SubMessageY": { - "id": 3102, - "description": "abcf" - }, - "MessagesY": [ - { - "id": 5102, - "description": "abcg" - }, - { - "id": 5106, - "description": "abcgg" - } - ], - "enabled": true, - "data": "123" -}` - - - expacted := `{ - "Id": 1022, - "Name": "name acc", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "SubMessageX": { - "Id": 102, - "Description": "abcd" - }, - "MessagesX": [ - { - "Id": 2102, - "Description": "abce" - } - ], - "SubMessageY": { - "Id": 3102, - "Description": "abcf" - }, - "MessagesY": [ - { - "Id": 5102, - "Description": "abcg" - }, - { - "Id": 5106, - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "\"123\"" -}` - - var err error - var data = []byte(input) - message := &Message{} - err = gojay.UnmarshalJSONObject(data, message) - if !assert.Nil(t, err) { - log.Fatal(err) - } - assertly.AssertValues(t, expacted, message) -} - - - - -func TestMessage_Marshal(t *testing.T) { - - input := `{ - "id": 1022, - "name": "name acc", - "price": 13.3, - "ints": [ - 1, - 2, - 5 - ], - "floats": [ - 2.3, - 4.6, - 7.4 - ], - "subMessageX": { - "id": 102, - "description": "abcd" - }, - "messagesX": [ - { - "id": 2102, - "description": "abce" - } - ], - "SubMessageY": { - "id": 3102, - "description": "abcf" - }, - "MessagesY": [ - { - "id": 5102, - "description": "abcg" - }, - { - "id": 5106, - "description": "abcgg" - } - ], - "enabled": true, - "data": "123" -}` - - - expacted := `{ - "Id": 1022, - "Name": "name acc", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "SubMessageX": { - "Id": 102, - "Description": "abcd" - }, - "MessagesX": [ - { - "Id": 2102, - "Description": "abce" - } - ], - "SubMessageY": { - "Id": 3102, - "Description": "abcf" - }, - "MessagesY": [ - { - "Id": 5102, - "Description": "abcg" - }, - { - "Id": 5106, - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "\"123\"" -}` - - var err error - var data = []byte(input) - message := &Message{} - err = gojay.UnmarshalJSONObject(data, message) - if !assert.Nil(t, err) { - log.Fatal(err) - } - assertly.AssertValues(t, expacted, message) - var writer = new(bytes.Buffer) - encoder := gojay.NewEncoder(writer) - err = encoder.Encode(message) - assert.Nil(t, err) - var JSON = writer.String() - assertly.AssertValues(t, input, JSON) - -} -\ No newline at end of file diff --git a/codegen/test/annotated_struct/message.go b/codegen/test/annotated_struct/message.go @@ -1,19 +0,0 @@ -package annotated_struct - - -type Paylod []byte - -type Message struct { - Id int `json:"id"` - Name string `json:"name"` - Price float64 `json:"price"` - Ints []int `json:"ints"` - Floats []*float32 `json:"floats"` - SubMessageX *SubMessage `json:"subMessageX"` - MessagesX []*SubMessage `json:"messagesX"` - SubMessageY SubMessage - MessagesY []SubMessage - IsTrue *bool `json:"enabled"` - Payload Paylod `json:"data"` - Ignore string `json:"-"` -} diff --git a/codegen/test/annotated_struct/sub_message.go b/codegen/test/annotated_struct/sub_message.go @@ -1,10 +0,0 @@ -package annotated_struct - -import "time" - -type SubMessage struct { - Id int `json:"id"` - Description string `json:"description"` - StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"` - EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"` -} diff --git a/codegen/test/basic_struct/encoding.go b/codegen/test/basic_struct/encoding.go @@ -1,280 +0,0 @@ -// Code generated by GoJayGen. DO NOT EDIT.\n\n -package basic_struct - -import ( -"github.com/francoispqt/gojay" -"time" -) - - - - - - -type Ints []int - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value int - if err := dec.Int(&value); err != nil { - return err - } - *a = append(*a, value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Int(item) - } -} - -//IsNil checks if array is nil -func (a Ints) IsNil() bool { - return len(a) == 0 -} - - - -type Float32sPtr []*float32 - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value float32 - if err := dec.Float32(&value); err != nil { - return err - } - *a = append(*a, &value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Float32(*item) - } -} - -//IsNil checks if array is nil -func (a Float32sPtr) IsNil() bool { - return len(a) == 0 -} - - -type SubMessagesPtr []*SubMessage - -func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = &SubMessage{} - if err := dec.Object(value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(s[i]) - } -} - - -func (s SubMessagesPtr) IsNil() bool { - return len(s) == 0 -} - - - - -type SubMessages []SubMessage - -func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = SubMessage{} - if err := dec.Object(&value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(&s[i]) - } -} - - -func (s SubMessages) IsNil() bool { - return len(s) == 0 -} - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("Id", m.Id) - enc.StringKey("Description", m.Description) - enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) - if m.EndTime != nil { - enc.TimeKey("EndTime", m.EndTime, time.RFC3339) - } -} - -//IsNil checks if instance is nil -func (m *SubMessage) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "Id": - return dec.Int(&m.Id) - - case "Description": - return dec.String(&m.Description) - - case "StartTime": - var format = time.RFC3339 - var value = time.Time{} - err := dec.DecodeTime(&value, format) - return err - - case "EndTime": - var format = time.RFC3339 - var value = &time.Time{} - err := dec.DecodeTime(value, format) - if err == nil { - m.EndTime = value - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *SubMessage) NKeys() int { return 4 } - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("Id", m.Id) - enc.StringKey("Name", m.Name) - enc.Float64Key("Price", m.Price) - var intsSlice = Ints(m.Ints) - enc.ArrayKey("Ints",intsSlice) - var floatsSlice = Float32sPtr(m.Floats) - enc.ArrayKey("Floats",floatsSlice) - enc.ObjectKey("SubMessageX", m.SubMessageX) - var messagesXSlice = SubMessagesPtr(m.MessagesX) - enc.ArrayKey("MessagesX", messagesXSlice) - enc.ObjectKey("SubMessageY", &m.SubMessageY) - var messagesYSlice = SubMessages(m.MessagesY) - enc.ArrayKey("MessagesY", messagesYSlice) - enc.BoolKey("IsTrue", *m.IsTrue) - var payloadSlice = gojay.EmbeddedJSON(m.Payload) - enc.AddEmbeddedJSONKey("Payload", &payloadSlice) -} - -//IsNil checks if instance is nil -func (m *Message) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "Id": - return dec.Int(&m.Id) - - case "Name": - return dec.String(&m.Name) - - case "Price": - return dec.Float64(&m.Price) - - case "Ints": - var aSlice = Ints{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Ints = []int(aSlice) - } - return err - - case "Floats": - var aSlice = Float32sPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Floats = []*float32(aSlice) - } - return err - - case "SubMessageX": - var value = &SubMessage{} - err := dec.Object(value) - if err == nil { - m.SubMessageX = value - } - - return err - - case "MessagesX": - var aSlice = SubMessagesPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesX = []*SubMessage(aSlice) - } - return err - - case "SubMessageY": - err := dec.Object(&m.SubMessageY) - - return err - - case "MessagesY": - var aSlice = SubMessages{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesY = []SubMessage(aSlice) - } - return err - - case "IsTrue": - var value bool - err := dec.Bool(&value) - if err == nil { - m.IsTrue = &value - } - return err - - - case "Payload": - var value = gojay.EmbeddedJSON{} - err := dec.AddEmbeddedJSON(&value) - if err == nil && len(value) > 0 { - m.Payload = []byte(value) - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *Message) NKeys() int { return 11 } - - - - - diff --git a/codegen/test/basic_struct/encoding_test.go b/codegen/test/basic_struct/encoding_test.go @@ -1,122 +0,0 @@ -package basic_struct - -import ( - "testing" - "github.com/stretchr/testify/assert" - "github.com/francoispqt/gojay" - "github.com/viant/assertly" - "bytes" -) - -func TestMessage_Unmarshal(t *testing.T) { - - input := `{ - "Id": 1022, - "Name": "name acc", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "SubMessageX": { - "Id": 102, - "Description": "abcd" - }, - "MessagesX": [ - { - "Id": 2102, - "Description": "abce" - } - ], - "SubMessageY": { - "Id": 3102, - "Description": "abcf" - }, - "MessagesY": [ - { - "Id": 5102, - "Description": "abcg" - }, - { - "Id": 5106, - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "" -}` - - var err error - var data = []byte(input) - message := &Message{} - err = gojay.UnmarshalJSONObject(data, message) - assert.Nil(t, err) - assertly.AssertValues(t, input, message) -} - - - -func TestMessage_Marshal(t *testing.T) { - - input := `{ - "Id": 1022, - "Name": "name acc", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "SubMessageX": { - "Id": 102, - "Description": "abcd" - }, - "MessagesX": [ - { - "Id": 2102, - "Description": "abce" - } - ], - "SubMessageY": { - "Id": 3102, - "Description": "abcf" - }, - "MessagesY": [ - { - "Id": 5102, - "Description": "abcg" - }, - { - "Id": 5106, - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "" -}` - - var err error - var data = []byte(input) - message := &Message{} - err = gojay.UnmarshalJSONObject(data, message) - assert.Nil(t, err) - - var writer = new(bytes.Buffer) - - encoder := gojay.NewEncoder(writer) - err = encoder.Encode(message) - assert.Nil(t, err) - var JSON = writer.String() - assertly.AssertValues(t, input, JSON) -} diff --git a/codegen/test/basic_struct/message.go b/codegen/test/basic_struct/message.go @@ -1,18 +0,0 @@ -package basic_struct - - - - -type Message struct { - Id int - Name string - Price float64 - Ints []int - Floats []*float32 - SubMessageX *SubMessage - MessagesX []*SubMessage - SubMessageY SubMessage - MessagesY []SubMessage - IsTrue *bool - Payload []byte -} diff --git a/codegen/test/basic_struct/sub_message.go b/codegen/test/basic_struct/sub_message.go @@ -1,10 +0,0 @@ -package basic_struct - -import "time" - -type SubMessage struct { - Id int - Description string - StartTime time.Time - EndTime *time.Time -} diff --git a/codegen/test/embedded_struct/encoding.go b/codegen/test/embedded_struct/encoding.go @@ -1,336 +0,0 @@ -// Code generated by GoJayGen. DO NOT EDIT.\n\n -package embedded_struct - -import ( -"github.com/francoispqt/gojay" -"time" -) - - - - - -type SubMessagesPtr []*SubMessage - -func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = &SubMessage{} - if err := dec.Object(value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(s[i]) - } -} - - -func (s SubMessagesPtr) IsNil() bool { - return len(s) == 0 -} - - - - -type SubMessages []SubMessage - -func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = SubMessage{} - if err := dec.Object(&value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(&s[i]) - } -} - - -func (s SubMessages) IsNil() bool { - return len(s) == 0 -} - - - - - -type Ints []int - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value int - if err := dec.Int(&value); err != nil { - return err - } - *a = append(*a, value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Int(item) - } -} - -//IsNil checks if array is nil -func (a Ints) IsNil() bool { - return len(a) == 0 -} - - - -type Float32sPtr []*float32 - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value float32 - if err := dec.Float32(&value); err != nil { - return err - } - *a = append(*a, &value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Float32(*item) - } -} - -//IsNil checks if array is nil -func (a Float32sPtr) IsNil() bool { - return len(a) == 0 -} - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (i *BaseId) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("Id", i.Id) - enc.StringKey("Name", i.Name) -} - -//IsNil checks if instance is nil -func (i *BaseId) IsNil() bool { - return i == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (i *BaseId) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "Id": - return dec.Int(&i.Id) - - case "Name": - return dec.String(&i.Name) - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (i *BaseId) NKeys() int { return 2 } - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { - enc.StringKey("Description", m.Description) - enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) - if m.EndTime != nil { - enc.TimeKey("EndTime", m.EndTime, time.RFC3339) - } -} - -//IsNil checks if instance is nil -func (m *SubMessage) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "Description": - return dec.String(&m.Description) - - case "StartTime": - var format = time.RFC3339 - var value = time.Time{} - err := dec.DecodeTime(&value, format) - return err - - case "EndTime": - var format = time.RFC3339 - var value = &time.Time{} - err := dec.DecodeTime(value, format) - if err == nil { - m.EndTime = value - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *SubMessage) NKeys() int { return 3 } - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { - if m.BaseId != nil { - enc.IntKey("Id", m.Id) - enc.StringKey("Name", m.Name) - } - enc.StringKey("Description", m.Description) - enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) - if m.EndTime != nil { - enc.TimeKey("EndTime", m.EndTime, time.RFC3339) - } - enc.Float64Key("Price", m.Price) - var intsSlice = Ints(m.Ints) - enc.ArrayKey("Ints",intsSlice) - var floatsSlice = Float32sPtr(m.Floats) - enc.ArrayKey("Floats",floatsSlice) - enc.ObjectKey("SubMessageX", m.SubMessageX) - var messagesXSlice = SubMessagesPtr(m.MessagesX) - enc.ArrayKey("MessagesX", messagesXSlice) - enc.ObjectKey("SubMessageY", &m.SubMessageY) - var messagesYSlice = SubMessages(m.MessagesY) - enc.ArrayKey("MessagesY", messagesYSlice) - enc.BoolKey("IsTrue", *m.IsTrue) - var payloadSlice = gojay.EmbeddedJSON(m.Payload) - enc.AddEmbeddedJSONKey("Payload", &payloadSlice) -} - -//IsNil checks if instance is nil -func (m *Message) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { -if m.BaseId == nil { - m.BaseId = &BaseId{} - } - switch k { - case "Id": - return dec.Int(&m.Id) - - case "Name": - return dec.String(&m.Name) - - case "Description": - return dec.String(&m.Description) - - case "StartTime": - var format = time.RFC3339 - var value = time.Time{} - err := dec.DecodeTime(&value, format) - return err - - case "EndTime": - var format = time.RFC3339 - var value = &time.Time{} - err := dec.DecodeTime(value, format) - if err == nil { - m.EndTime = value - } - return err - - case "Price": - return dec.Float64(&m.Price) - - case "Ints": - var aSlice = Ints{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Ints = []int(aSlice) - } - return err - - case "Floats": - var aSlice = Float32sPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Floats = []*float32(aSlice) - } - return err - - case "SubMessageX": - var value = &SubMessage{} - err := dec.Object(value) - if err == nil { - m.SubMessageX = value - } - - return err - - case "MessagesX": - var aSlice = SubMessagesPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesX = []*SubMessage(aSlice) - } - return err - - case "SubMessageY": - err := dec.Object(&m.SubMessageY) - - return err - - case "MessagesY": - var aSlice = SubMessages{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesY = []SubMessage(aSlice) - } - return err - - case "IsTrue": - var value bool - err := dec.Bool(&value) - if err == nil { - m.IsTrue = &value - } - return err - - - case "Payload": - var value = gojay.EmbeddedJSON{} - err := dec.AddEmbeddedJSON(&value) - if err == nil && len(value) > 0 { - m.Payload = []byte(value) - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *Message) NKeys() int { return 14 } - - - - - diff --git a/codegen/test/embedded_struct/encoding_test.go b/codegen/test/embedded_struct/encoding_test.go @@ -1,108 +0,0 @@ -package embedded_struct - -import ( - "testing" - "github.com/stretchr/testify/assert" - "github.com/francoispqt/gojay" - "github.com/viant/assertly" - "bytes" -) - -func TestMessage_Unmarshal(t *testing.T) { - - input := `{ - "Id": 1022, - "Name": "name acc", - "Description": "abcd", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "MessagesX": [ - { - "Description": "abce" - } - ], - "SubMessageY": { - "Description": "abcf" - }, - "MessagesY": [ - { - "Description": "abcg" - }, - { - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "" -}` - - var err error - var data = []byte(input) - message := &Message{} - err = gojay.UnmarshalJSONObject(data, message) - assert.Nil(t, err) - assertly.AssertValues(t, input, message) -} - - - -func TestMessage_Marshal(t *testing.T) { - - input := `{ - "Id": 1022, - "Name": "name acc", - "Description": "abcd", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "MessagesX": [ - { - "Description": "abce" - } - ], - "SubMessageY": { - "Description": "abcf" - }, - "MessagesY": [ - { - "Description": "abcg" - }, - { - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "" -}` - - var err error - var data = []byte(input) - message := &Message{} - err = gojay.UnmarshalJSONObject(data, message) - assert.Nil(t, err) - assertly.AssertValues(t, input, message) - var writer = new(bytes.Buffer) - - encoder := gojay.NewEncoder(writer) - err = encoder.Encode(message) - assert.Nil(t, err) - var JSON = writer.String() - assertly.AssertValues(t, input, JSON) -} -\ No newline at end of file diff --git a/codegen/test/embedded_struct/message.go b/codegen/test/embedded_struct/message.go @@ -1,22 +0,0 @@ -package embedded_struct - - -type BaseId struct { - Id int - Name string - -} - -type Message struct { - *BaseId - SubMessage - Price float64 - Ints []int - Floats []*float32 - SubMessageX *SubMessage - MessagesX []*SubMessage - SubMessageY SubMessage - MessagesY []SubMessage - IsTrue *bool - Payload []byte -} diff --git a/codegen/test/embedded_struct/sub_message.go b/codegen/test/embedded_struct/sub_message.go @@ -1,9 +0,0 @@ -package embedded_struct - -import "time" - -type SubMessage struct { - Description string - StartTime time.Time - EndTime *time.Time -} diff --git a/codegen/test/pooled_struct/encoding.go b/codegen/test/pooled_struct/encoding.go @@ -1,326 +0,0 @@ -// Code generated by GoJayGen. DO NOT EDIT.\n\n -package pooled_struct - -import ( -"sync" -"time" -"github.com/francoispqt/gojay" -) - - -func init() { - MessagePool = &sync.Pool { - New: func()interface{} { - return &Message{} - }, - } - SubMessagePool = &sync.Pool { - New: func()interface{} { - return &SubMessage{} - }, - } -} - - -var MessagePool *sync.Pool -var SubMessagePool *sync.Pool - - - -type Ints []int - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value int - if err := dec.Int(&value); err != nil { - return err - } - *a = append(*a, value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Int(item) - } -} - -//IsNil checks if array is nil -func (a Ints) IsNil() bool { - return len(a) == 0 -} - - - -type Float32sPtr []*float32 - -//UnmarshalJSONArray decodes JSON array elements into slice -func (a *Float32sPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value float32 - if err := dec.Float32(&value); err != nil { - return err - } - *a = append(*a, &value) - return nil -} - -//MarshalJSONArray encodes arrays into JSON -func (a Float32sPtr) MarshalJSONArray(enc *gojay.Encoder) { - for _, item := range a { - enc.Float32(*item) - } -} - -//IsNil checks if array is nil -func (a Float32sPtr) IsNil() bool { - return len(a) == 0 -} - - -type SubMessagesPtr []*SubMessage - -func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = &SubMessage{} - if err := dec.Object(value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(s[i]) - } -} - - -func (s SubMessagesPtr) IsNil() bool { - return len(s) == 0 -} - - - - -type SubMessages []SubMessage - -func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { - var value = SubMessage{} - if err := dec.Object(&value); err != nil { - return err - } - *s = append(*s, value) - return nil -} - -func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { - for i := range s { - enc.Object(&s[i]) - } -} - - -func (s SubMessages) IsNil() bool { - return len(s) == 0 -} - - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("Id", m.Id) - enc.StringKey("Description", m.Description) - enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) - if m.EndTime != nil { - enc.TimeKey("EndTime", m.EndTime, time.RFC3339) - } -} - -//IsNil checks if instance is nil -func (m *SubMessage) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "Id": - return dec.Int(&m.Id) - - case "Description": - return dec.String(&m.Description) - - case "StartTime": - var format = time.RFC3339 - var value = time.Time{} - err := dec.DecodeTime(&value, format) - return err - - case "EndTime": - var format = time.RFC3339 - var value = &time.Time{} - err := dec.DecodeTime(value, format) - if err == nil { - m.EndTime = value - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *SubMessage) NKeys() int { return 4 } - - -// Reset reset fields -func (m *SubMessage) Reset() { - m.Id = 0 - m.Description = "" - m.EndTime = nil -} - - - - - -//MarshalJSONObject implements MarshalerJSONObject -func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { - enc.IntKey("Id", m.Id) - enc.StringKey("Name", m.Name) - enc.Float64Key("Price", m.Price) - var intsSlice = Ints(m.Ints) - enc.ArrayKey("Ints",intsSlice) - var floatsSlice = Float32sPtr(m.Floats) - enc.ArrayKey("Floats",floatsSlice) - enc.ObjectKey("SubMessageX", m.SubMessageX) - var messagesXSlice = SubMessagesPtr(m.MessagesX) - enc.ArrayKey("MessagesX", messagesXSlice) - enc.ObjectKey("SubMessageY", &m.SubMessageY) - var messagesYSlice = SubMessages(m.MessagesY) - enc.ArrayKey("MessagesY", messagesYSlice) - enc.BoolKey("IsTrue", *m.IsTrue) - var payloadSlice = gojay.EmbeddedJSON(m.Payload) - enc.AddEmbeddedJSONKey("Payload", &payloadSlice) -} - -//IsNil checks if instance is nil -func (m *Message) IsNil() bool { - return m == nil -} - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - - switch k { - case "Id": - return dec.Int(&m.Id) - - case "Name": - return dec.String(&m.Name) - - case "Price": - return dec.Float64(&m.Price) - - case "Ints": - var aSlice = Ints{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Ints = []int(aSlice) - } - return err - - case "Floats": - var aSlice = Float32sPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.Floats = []*float32(aSlice) - } - return err - - case "SubMessageX": - var value = SubMessagePool.Get().(*SubMessage) - err := dec.Object(value) - if err == nil { - m.SubMessageX = value - } - - return err - - case "MessagesX": - var aSlice = SubMessagesPtr{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesX = []*SubMessage(aSlice) - } - return err - - case "SubMessageY": - err := dec.Object(&m.SubMessageY) - - return err - - case "MessagesY": - var aSlice = SubMessages{} - err := dec.Array(&aSlice) - if err == nil && len(aSlice) > 0 { - m.MessagesY = []SubMessage(aSlice) - } - return err - - case "IsTrue": - var value bool - err := dec.Bool(&value) - if err == nil { - m.IsTrue = &value - } - return err - - - case "Payload": - var value = gojay.EmbeddedJSON{} - err := dec.AddEmbeddedJSON(&value) - if err == nil && len(value) > 0 { - m.Payload = []byte(value) - } - return err - - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (m *Message) NKeys() int { return 11 } - - -// Reset reset fields -func (m *Message) Reset() { - m.Id = 0 - m.Name = "" - m.Price = 0.0 - m.Ints = nil - m.Floats = nil - SubMessagePool.Put(m.SubMessageX) - m.SubMessageX = nil - for i := range m.MessagesX { - m.MessagesX[i].Reset() - SubMessagePool.Put(m.MessagesX[i]) - } - m.MessagesX = nil - for i := range m.MessagesY { - m.MessagesY[i].Reset() - SubMessagePool.Put(&m.MessagesY[i]) - } - m.MessagesY = nil - m.IsTrue = nil - m.Payload = nil -} - - - - diff --git a/codegen/test/pooled_struct/encoding_test.go b/codegen/test/pooled_struct/encoding_test.go @@ -1,124 +0,0 @@ -package pooled_struct - -import ( - "testing" - "github.com/francoispqt/gojay" - "github.com/stretchr/testify/assert" - "github.com/viant/assertly" - "bytes" -) - -func TestMessage_Unmarshal(t *testing.T) { - - input := `{ - "Id": 1022, - "Name": "name acc", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "SubMessageX": { - "Id": 102, - "Description": "abcd" - }, - "MessagesX": [ - { - "Id": 2102, - "Description": "abce" - } - ], - "SubMessageY": { - "Id": 3102, - "Description": "abcf" - }, - "MessagesY": [ - { - "Id": 5102, - "Description": "abcg" - }, - { - "Id": 5106, - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "" -}` - - var data = []byte(input) - message := MessagePool.Get().(*Message) - err :=gojay.UnmarshalJSONObject(data, message) - assert.Nil(t, err) - message.Reset() - MessagePool.Put(message) - -} - - -func TestMessage_Marshal(t *testing.T) { - - input := `{ - "Id": 1022, - "Name": "name acc", - "Price": 13.3, - "Ints": [ - 1, - 2, - 5 - ], - "Floats": [ - 2.3, - 4.6, - 7.4 - ], - "SubMessageX": { - "Id": 102, - "Description": "abcd" - }, - "MessagesX": [ - { - "Id": 2102, - "Description": "abce" - } - ], - "SubMessageY": { - "Id": 3102, - "Description": "abcf" - }, - "MessagesY": [ - { - "Id": 5102, - "Description": "abcg" - }, - { - "Id": 5106, - "Description": "abcgg" - } - ], - "IsTrue": true, - "Payload": "" -}` - - var data = []byte(input) - message := MessagePool.Get().(*Message) - err :=gojay.UnmarshalJSONObject(data, message) - assert.Nil(t, err) - defer func() { - message.Reset() - MessagePool.Put(message) - - }() - var writer = new(bytes.Buffer) - encoder := gojay.NewEncoder(writer) - err = encoder.Encode(message) - assert.Nil(t, err) - var JSON = writer.String() - assertly.AssertValues(t, input, JSON) -} -\ No newline at end of file diff --git a/codegen/test/pooled_struct/message.go b/codegen/test/pooled_struct/message.go @@ -1,18 +0,0 @@ -package pooled_struct - - - - -type Message struct { - Id int - Name string - Price float64 - Ints []int - Floats []*float32 - SubMessageX *SubMessage - MessagesX []*SubMessage - SubMessageY SubMessage - MessagesY []SubMessage - IsTrue *bool - Payload []byte -} diff --git a/codegen/test/pooled_struct/sub_message.go b/codegen/test/pooled_struct/sub_message.go @@ -1,10 +0,0 @@ -package pooled_struct - -import "time" - -type SubMessage struct { - Id int - Description string - StartTime time.Time - EndTime *time.Time -} diff --git a/gojay/Gopkg.lock b/gojay/Gopkg.lock @@ -1,14 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - -[[projects]] - name = "github.com/fatih/structtag" - packages = ["."] - revision = "da3d9ab5b78fdc25d3a7614853b085200bd10da9" - version = "v0.1.0" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "859116499e74f6c88afba2065cef9e2ee1caa81d532e01ed144541f9d36faae8" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/gojay/Gopkg.toml b/gojay/Gopkg.toml @@ -1,26 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - name = "github.com/fatih/structtag" - version = "0.1.0" diff --git a/gojay/Makefile b/gojay/Makefile @@ -1,15 +0,0 @@ -.PHONY: build -build: - go build ./ - -.PHONY: test -test: - go test -race -v . - -.PHONY: cover -cover: - go test -tags test -coverprofile=coverage.out - -.PHONY: coverhtml -coverhtml: - go tool cover -html=coverage.out -\ No newline at end of file diff --git a/gojay/README.md b/gojay/README.md @@ -1,63 +1,61 @@ # Gojay code generator +This package provides a command line tool to generate gojay's marshaling and unmarshing interface implementation for custom struct type(s) -This package provides a command line tool to generate gojay's marshaling and unmarshing interface implementation for struct, slice and map types. ## Get started ```sh -go install github.com/francoispqt/gojay/gojay +go install github.com/francoispqt/gojay/gojaygen ``` ## Generate code ### Basic command - The basic command is straightforward and easy to use: ```sh -gojay github.com/some/package TypeA,TypeB,TypeC output.go +cd $GOPATH/src/github.com/user/project +gojaygen -s . -p true -t MyType -o output.go ``` -If you just want to the output to stdout, omit the third parameter. +If you just want to the output to stdout, omit the -o flag. ### Using flags - -- p package to parse, relative path to $GOPATH/src -- s file/dir to path, can be a relative or absolute path -- t types to generate (comma separated) -- o output file (relative or absolute path) +- s Source file/dir path, can be a relative or absolute path +- t Types to generate with all its dependencies (comma separated) +- a Annotation tag used to read metadata (default: json) +- o Output file (relative or absolute path) +- p Pool to reuse object (using sync.Pool) Examples: -- Specific types in a go package, to stdout: -```sh -gojay -p github.com/francoispqt/gojay/gojay/tests -t A,B,StrSlice -``` -- Specific types in a go package, write to a file: +- Generate `SomeType` type in `/tmp/myproj` go package, write to file `output.go`: ```sh -gojay -p github.com/francoispqt/gojay/gojay/tests -t A,B,StrSlice -o output.go +gojay -s /tmp/myproj -t SomeType -o output.go ``` -- Specific types in a go file, to stdout: +- Generate type `SomeType` in file `somegofile.go`, with custom tag `gojay`, write to stdout: ```sh -gojay -s somegofile.go -t SomeType +gojay -s somegofile.go -a gojay -t SomeType ``` -## Gojay tags - +## Generator tags You can add tags to your structs to control: + - the JSON key -- skip a struct field only for unmarshaling -- skip a struct field only for marshaling - skip a struct field -- the use of omit empty methods for marshaling +- the use of omitempty methods for marshaling +- timeFormat (java style data format) +- timeLayout (golang time layout) + ### Example: ```go type A struct { - Str string `gojay:"string"` - StrOmitEmpty string `gojay:"string,omitempty"` - SkipUnmarshal string `gojay:"skipUnmarshal,-u"` - SkipMarshal string `gojay:"skipMarshal,-m"` - Skip string `gojay:"-"` + Str string `json:"string"` + StrOmitEmpty string `json:"stringOrEmpty,omitempty"` + Skip string `json:"-"` + StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"` + EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"` } ``` + diff --git a/gojay/codegen/field.go b/gojay/codegen/field.go @@ -0,0 +1,168 @@ +package codegen + +import ( + "fmt" + "github.com/viant/toolbox" + "strings" +) + +//Field represents a field. +type Field struct { + Key string + Init string + OmitEmpty string + TimeLayout string + NullType string + Name string + Accessor string + Mutator string + Receiver string //alias and type name + Alias string //object alias name + Var string //variable for this field + Type string + RawType string + HelperType string + ComponentType string + RawComponentType string + IsPointerComponent bool + + PointerModifier string //takes field pointer, "&" if field is not a pointer type + DereferenceModifier string //take pointer value, i.e "*" if field has a pointer type + + ComponentPointerModifier string //takes item pointer if needed,i.e + ComponentDereferenceModifier string //de reference value if needed, i.e + ComponentInitModifier string //takes item pointer if type is not a pointer type + ComponentInit string //initialises component type + + DecodingMethod string + EncodingMethod string + PoolName string //pool name associated with this field + ResetDependency string + Reset string + IsAnonymous bool + IsPointer bool + IsSlice bool + + GojayMethod string +} + +//NewField returns a new field +func NewField(owner *Struct, field *toolbox.FieldInfo, fieldType *toolbox.TypeInfo) (*Field, error) { + typeName := normalizeTypeName(field.TypeName) + var result = &Field{ + IsAnonymous: field.IsAnonymous, + Name: field.Name, + RawType: field.TypeName, + IsPointer: field.IsPointer, + Key: getJSONKey(owner.options, field), + Receiver: owner.Alias + " *" + owner.TypeInfo.Name, + Type: typeName, + Mutator: owner.Alias + "." + field.Name, + Accessor: owner.Alias + "." + field.Name, + ComponentType: field.ComponentType, + IsPointerComponent: field.IsPointerComponent, + Var: firstLetterToLowercase(field.Name), + Init: fmt.Sprintf("%v{}", typeName), + TimeLayout: "time.RFC3339", + IsSlice: field.IsSlice, + PoolName: getPoolName(field.TypeName), + Alias: owner.Alias, + Reset: "nil", + } + var err error + if field.IsPointer { + result.DereferenceModifier = "*" + result.Init = "&" + result.Init + } else { + result.PointerModifier = "&" + + } + if field.IsSlice { + result.HelperType = getSliceHelperTypeName(field.ComponentType, field.IsPointerComponent) + result.PoolName = getPoolName(field.ComponentType) + } else if fieldType != nil { + result.HelperType = getSliceHelperTypeName(fieldType.Name, field.IsPointerComponent) + } + + if options := getTagOptions(field.Tag, "timeLayout"); len(options) > 0 { + result.TimeLayout = wrapperIfNeeded(options[0], `"`) + } else if options := getTagOptions(field.Tag, "timeFormat"); len(options) > 0 { + result.TimeLayout = wrapperIfNeeded(toolbox.DateFormatToLayout(options[0]), `"`) + } + if strings.Contains(field.Tag, "omitempty") { + result.OmitEmpty = "OmitEmpty" + } + if strings.Contains(field.Tag, "nullempty") { + result.OmitEmpty = "NullEmpty" + } + + if owner.options.PoolObjects { + if field.IsPointer && !strings.HasSuffix(field.TypeName, ".Time") && !strings.Contains(field.TypeName, "sql.Null") { + poolName := getPoolName(field.TypeName) + result.Init = fmt.Sprintf(`%v.Get().(*%v)`, poolName, field.TypeName) + } + } + + encodingMethod := field.ComponentType + if encodingMethod == "" { + encodingMethod = result.Type + } + result.DecodingMethod = firstLetterToUppercase(encodingMethod) + result.EncodingMethod = firstLetterToUppercase(encodingMethod) + + switch typeName { + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + result.Reset = "0" + case "float32", "float64": + result.Reset = "0.0" + case "string": + result.Reset = `""` + + case "bool": + result.Reset = "false" + default: + if field.IsSlice && owner.Type(field.ComponentType) != nil { + var itemPointer = "" + if !field.IsPointerComponent { + itemPointer = "&" + } + result.ResetDependency, err = expandFieldTemplate(poolSliceInstanceRelease, struct { + PoolName string + Accessor string + PointerModifier string + }{PoolName: result.PoolName, Accessor: result.Accessor, PointerModifier: itemPointer}) + if err != nil { + return nil, err + } + + } else if field.IsPointer && fieldType != nil { + result.ResetDependency, err = expandFieldTemplate(poolInstanceRelease, struct { + PoolName string + Accessor string + }{PoolName: result.PoolName, Accessor: result.Accessor}) + if err != nil { + return nil, err + } + } + + } + if field.IsSlice || field.IsPointer { + result.Reset = "nil" + } + + if result.IsPointerComponent { + result.ComponentInit = "&" + result.ComponentType + "{}" + result.RawComponentType = "*" + result.ComponentType + + result.ComponentDereferenceModifier = "*" + result.ComponentInitModifier = "&" + + } else { + result.ComponentInit = result.ComponentType + "{}" + result.RawComponentType = result.ComponentType + + result.ComponentPointerModifier = "&" + } + + return result, nil +} diff --git a/gojay/codegen/generator.go b/gojay/codegen/generator.go @@ -0,0 +1,251 @@ +package codegen + +import ( + "fmt" + "go/format" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/viant/toolbox" +) + +const gojayPackage = "github.com/francoispqt/gojay" + +// Generator holds the content to generate the gojay code +type Generator struct { + fileInfo *toolbox.FileSetInfo + types map[string]string + structTypes map[string]string + sliceTypes map[string]string + pooledObjects map[string]string + poolInit map[string]string + imports map[string]bool + filedInit []string + Pkg string + Code string + Init string + Imports string + options *Options +} + +// Returns the type from the the fileInfo +func (g *Generator) Type(typeName string) *toolbox.TypeInfo { + return g.fileInfo.Type(typeName) +} + +// addImport adds an import package to be printed on the generated code +func (g *Generator) addImport(pkg string) { + g.imports[`"`+pkg+`"`] = true +} + +// we initiate the variables containing the code to be generated +func (g *Generator) init() { + g.filedInit = []string{} + g.imports = map[string]bool{} + g.pooledObjects = map[string]string{} + g.structTypes = map[string]string{} + g.sliceTypes = map[string]string{} + g.poolInit = map[string]string{} + g.addImport(gojayPackage) + // if we want pools, add the sync package right away + if g.options.PoolObjects { + g.addImport("sync") + } +} + +// NewGenerator creates a new generator with the given options +func NewGenerator(options *Options) *Generator { + var g = &Generator{} + // first we validate the flags + if err := options.Validate(); err != nil { + panic(err) + } + g.options = options + // we initiate the values on the generator + g.init() + return g +} + +// Generate generates the gojay implementation code +func (g *Generator) Generate() error { + // first we read the code from which we should find the types + if err := g.readPackageCode(g.options.Source); err != nil { + return err + } + + // then we generate code for the types given + for _, rootType := range g.options.Types { + if err := g.generateStructCode(rootType); err != nil { + return err + } + } + + // + g.Imports = strings.Join(toolbox.MapKeysToStringSlice(g.imports), "\n") + return g.writeCode() +} + +func (g *Generator) writeCode() error { + var generatedCode = []string{} + for _, code := range g.pooledObjects { + generatedCode = append(generatedCode, code) + } + generatedCode = append(generatedCode, "") + for _, code := range g.sliceTypes { + generatedCode = append(generatedCode, code) + } + generatedCode = append(generatedCode, "") + for _, code := range g.structTypes { + generatedCode = append(generatedCode, code) + } + + for _, code := range g.poolInit { + if g.Init != "" { + g.Init += "\n" + } + g.Init += code + } + g.Code = strings.Join(generatedCode, "\n") + + expandedCode, err := expandBlockTemplate(fileCode, g) + if err != nil { + return err + } + + code, err := format.Source([]byte(expandedCode)) + + if err != nil { + return err + } + + // code destination is empty, we just print to stdout + if g.options.Dest == "" { + fmt.Print(string(code)) + return nil + } + + return ioutil.WriteFile(g.options.Dest, code, 0644) +} + +func (g *Generator) generatePrimitiveArray(field *Field) error { + key := field.ComponentType + toolbox.AsString(field.IsPointerComponent) + if _, ok := g.sliceTypes[key]; ok { + return nil + } + code, err := expandBlockTemplate(baseTypeSlice, field) + g.sliceTypes[key] = code + return err +} + +func (g *Generator) generateObjectArray(field *Field) error { + if _, ok := g.sliceTypes[field.RawComponentType]; ok { + return nil + } + + if err := g.generateStructCode(field.ComponentType); err != nil { + return err + } + code, err := expandBlockTemplate(structTypeSlice, field) + if err != nil { + return err + } + g.sliceTypes[field.RawComponentType] = code + return err +} + +func (g *Generator) generateTimeArray(field *Field) error { + if _, ok := g.sliceTypes[field.RawComponentType]; ok { + return nil + } + + code, err := expandBlockTemplate(timeSlice, field) + if err != nil { + return err + } + g.sliceTypes[field.RawComponentType] = code + return err +} + +func (g *Generator) generateTypedArray(field *Field) error { + if _, ok := g.sliceTypes[field.RawComponentType]; ok { + return nil + } + + code, err := expandBlockTemplate(typeSlice, field) + if err != nil { + return err + } + g.sliceTypes[field.RawComponentType] = code + return err +} + +func (g *Generator) generatePool(structType string) error { + if !g.options.PoolObjects { + return nil + } + var err error + if g.pooledObjects[structType], err = expandBlockTemplate(poolVar, struct { + PoolName string + }{getPoolName(structType)}); err == nil { + g.poolInit[structType], err = expandBlockTemplate(poolInit, struct { + PoolName string + Type string + }{getPoolName(structType), structType}) + } + return err + +} + +func (g *Generator) generateStructCode(structType string) error { + structType = normalizeTypeName(structType) + typeInfo := g.Type(structType) + if typeInfo == nil { + return nil + } + if _, hasCode := g.structTypes[structType]; hasCode { + return nil + } + g.generatePool(structType) + + aStruct := NewStruct(typeInfo, g) + code, err := aStruct.Generate() + + if err != nil { + return err + } + + g.structTypes[structType] = code + return nil +} + +func (g *Generator) readPackageCode(pkgPath string) error { + p, err := filepath.Abs(pkgPath) + if err != nil { + return err + } + + var f os.FileInfo + if f, err = os.Stat(p); err != nil { + // path/to/whatever does not exist + return err + } + + if !f.IsDir() { + g.Pkg = filepath.Dir(p) + dir, _ := filepath.Split(p) + g.fileInfo, err = toolbox.NewFileSetInfo(dir) + + } else { + g.Pkg = filepath.Base(p) + g.fileInfo, err = toolbox.NewFileSetInfo(p) + } + + // if Pkg flag is set use it + if g.options.Pkg != "" { + g.Pkg = g.options.Pkg + } + + return err +} diff --git a/gojay/codegen/generator_test.go b/gojay/codegen/generator_test.go @@ -0,0 +1,72 @@ +package codegen + +import ( + "github.com/stretchr/testify/assert" + "github.com/viant/toolbox" + "log" + "path" + "testing" +) + +func TestGenerator_Generate(t *testing.T) { + + parent := path.Join(toolbox.CallerDirectory(3), "test") + + var useCases = []struct { + description string + options *Options + hasError bool + }{ + { + description: "basic struct code generation", + options: &Options{ + Source: path.Join(parent, "basic_struct"), + Types: []string{"Message"}, + Dest: path.Join(parent, "basic_struct", "encoding.go"), + }, + }, + + { + description: "struct with pool code generation", + options: &Options{ + Source: path.Join(parent, "pooled_struct"), + Types: []string{"Message"}, + Dest: path.Join(parent, "pooled_struct", "encoding.go"), + PoolObjects: true, + }, + }, + { + description: "struct with embedded type code generation", + options: &Options{ + Source: path.Join(parent, "embedded_struct"), + Types: []string{"Message"}, + Dest: path.Join(parent, "embedded_struct", "encoding.go"), + PoolObjects: false, + }, + }, + { + description: "struct with json annotation and time/foarmat|layouat generation", + options: &Options{ + Source: path.Join(parent, "annotated_struct"), + Types: []string{"Message"}, + Dest: path.Join(parent, "annotated_struct", "encoding.go"), + PoolObjects: false, + TagName: "json", + }, + }, + } + + for _, useCase := range useCases { + gen := NewGenerator(useCase.options) + err := gen.Generate() + if useCase.hasError { + assert.NotNil(t, err, useCase.description) + continue + } + if !assert.Nil(t, err, useCase.description) { + log.Fatal(err) + continue + } + } + +} diff --git a/gojay/codegen/helper.go b/gojay/codegen/helper.go @@ -0,0 +1,89 @@ +package codegen + +import ( + "github.com/viant/toolbox" + "reflect" + "strings" +) + +func firstLetterToUppercase(text string) string { + return strings.ToUpper(string(text[0:1])) + string(text[1:]) +} + +func firstLetterToLowercase(text string) string { + return strings.ToLower(string(text[0:1])) + string(text[1:]) +} + +func extractReceiverAlias(structType string) string { + var result = string(structType[0]) + for i := len(structType) - 1; i > 0; i-- { + aChar := string(structType[i]) + lowerChar := strings.ToLower(aChar) + if lowerChar != aChar { + result = lowerChar + break + } + } + return strings.ToLower(result) +} + +func getTagOptions(tag, key string) []string { + if tag == "" { + return nil + } + var structTag = reflect.StructTag(strings.Replace(tag, "`", "", len(tag))) + options, ok := structTag.Lookup(key) + if !ok { + return nil + } + return strings.Split(options, ",") +} + +func getSliceHelperTypeName(typeName string, isPointer bool) string { + if typeName == "" { + return "" + } + + var pluralName = firstLetterToUppercase(typeName) + "s" + if isPointer { + pluralName += "Ptr" + } + return strings.Replace(pluralName, ".", "", -1) +} + +func isSkipable(options *Options, field *toolbox.FieldInfo) bool { + if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 { + for _, candidate := range options { + if candidate == "-" { + return true + } + } + } + return false +} + +func wrapperIfNeeded(text, wrappingChar string) string { + if strings.HasPrefix(text, wrappingChar) { + return text + } + return wrappingChar + text + wrappingChar +} + +func getPoolName(typeName string) string { + typeName = strings.Replace(typeName, "*", "", 1) + return strings.Replace(typeName+"Pool", ".", "", -1) +} + +func getJSONKey(options *Options, field *toolbox.FieldInfo) string { + var key = field.Name + if field.Tag != "" { + if options := getTagOptions(field.Tag, options.TagName); len(options) > 0 { + key = options[0] + } + } + return key +} + +func normalizeTypeName(typeName string) string { + return strings.Replace(typeName, "*", "", strings.Count(typeName, "*")) +} diff --git a/gojay/codegen/options.go b/gojay/codegen/options.go @@ -0,0 +1,55 @@ +package codegen + +import ( + "flag" + "strings" + + "github.com/go-errors/errors" + "github.com/viant/toolbox" + "github.com/viant/toolbox/url" +) + +type Options struct { + Source string + Dest string + Types []string + PoolObjects bool + TagName string + Pkg string +} + +func (o *Options) Validate() error { + if o.Source == "" { + return errors.New("Source was empty") + } + if len(o.Types) == 0 { + return errors.New("Types was empty") + } + return nil +} + +const ( + optionKeySource = "s" + optionKeyDest = "o" + optionKeyTypes = "t" + optionKeyTagName = "a" + optionKeyPoolObjects = "p" + optionKeyPkg = "pkg" +) + +//NewOptionsWithFlagSet creates a new options for the supplide flagset +func NewOptionsWithFlagSet(set *flag.FlagSet) *Options { + toolbox.Dump(set) + + var result = &Options{} + result.Dest = set.Lookup(optionKeyDest).Value.String() + result.Source = set.Lookup(optionKeySource).Value.String() + result.PoolObjects = toolbox.AsBoolean(set.Lookup(optionKeyPoolObjects).Value.String()) + result.TagName = set.Lookup(optionKeyTagName).Value.String() + result.Types = strings.Split(set.Lookup(optionKeyTypes).Value.String(), ",") + result.Pkg = set.Lookup(optionKeyPkg).Value.String() + if result.Source == "" { + result.Source = url.NewResource(".").ParsedURL.Path + } + return result +} diff --git a/gojay/codegen/struct.go b/gojay/codegen/struct.go @@ -0,0 +1,344 @@ +package codegen + +import ( + "fmt" + "strings" + + "github.com/viant/toolbox" +) + +type Struct struct { + *toolbox.TypeInfo + referenced *toolbox.TypeInfo + *Generator + Alias string + Init string + Body string +} + +//Generate generates decoderCode + structRelease + encoderCode +func (s *Struct) Generate() (string, error) { + return s.generateEncoding(s.TypeInfo) +} + +func (s *Struct) generateEncoding(structInfo *toolbox.TypeInfo) (string, error) { + var initEmbedded, decodingCases, err = s.generateFieldDecoding(structInfo.Fields()) + if err != nil { + return "", err + } + + encodingCases, err := s.generateFieldEncoding(structInfo.Fields()) + if err != nil { + return "", err + } + var resetCode = "" + if s.options.PoolObjects { + resetCode, err = s.generateReset(structInfo.Fields()) + if err != nil { + return "", err + } + } + var data = struct { + Receiver string + Alias string + InitEmbedded string + EncodingCases string + DecodingCases string + Reset string + FieldCount int + }{ + Receiver: s.Alias + " *" + s.Name, + DecodingCases: strings.Join(decodingCases, "\n"), + EncodingCases: strings.Join(encodingCases, "\n"), + FieldCount: len(decodingCases), + InitEmbedded: initEmbedded, + Reset: resetCode, + Alias: s.Alias, + } + return expandBlockTemplate(encodingStructType, data) +} + +func (s *Struct) generateReset(fields []*toolbox.FieldInfo) (string, error) { + fieldReset, err := s.generateFieldReset(fields) + if err != nil { + return "", nil + } + return expandBlockTemplate(resetStruct, struct { + Reset string + Receiver string + }{ + Reset: strings.Join(fieldReset, "\n"), + Receiver: s.Alias + " *" + s.Name, + }) +} + +func (s *Struct) generateFieldReset(fields []*toolbox.FieldInfo) ([]string, error) { + fieldReset := []string{} + for i := range fields { + var templateKey = -1 + fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) + field, err := NewField(s, fields[i], fieldTypeInfo) + if err != nil { + return nil, err + } + if field.IsPointer || field.IsSlice || (fieldTypeInfo != nil && fieldTypeInfo.IsSlice) { + templateKey = resetFieldValue + } else { + switch field.Type { + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "string", "bool", "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64", "[]byte": + templateKey = resetFieldValue + } + } + if templateKey != -1 { + code, err := expandFieldTemplate(templateKey, field) + if err != nil { + return nil, err + } + fieldReset = append(fieldReset, code) + } + } + return fieldReset, nil +} + +func (s *Struct) generateFieldDecoding(fields []*toolbox.FieldInfo) (string, []string, error) { + + fieldCases := []string{} + var initCode = "" + for i := range fields { + if isSkipable(s.options, fields[i]) { + continue + } + var templateKey = -1 + fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) + field, err := NewField(s, fields[i], fieldTypeInfo) + if err != nil { + return "", nil, err + } + if fieldTypeInfo != nil { + if err = s.generateStructCode(fieldTypeInfo.Name); err != nil { + return "", nil, err + } + } + + if field.IsAnonymous { + if fieldTypeInfo != nil { + if field.IsPointer { + init, err := expandBlockTemplate(embeddedStructInit, field) + if err != nil { + return "", nil, err + } + initCode += init + } + init, embeddedCases, err := s.generateFieldDecoding(fieldTypeInfo.Fields()) + if err != nil { + return "", nil, err + } + initCode += init + fieldCases = append(fieldCases, embeddedCases...) + } + continue + } + + main: + switch field.Type { + case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": + templateKey = decodeBaseType + case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64": + templateKey = decodeBaseTypeSlice + s.generatePrimitiveArray(field) + case "[]byte": + templateKey = decodeRawType + default: + + if fieldTypeInfo != nil { + if !(field.IsSlice || fieldTypeInfo.IsSlice) { + + templateKey = decodeStruct + break main + } + + switch fieldTypeInfo.ComponentType { + case "byte": + templateKey = decodeRawType + break main + + case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": + s.generatePrimitiveArray(field) + templateKey = decodeBaseTypeSlice + break main + + } + + if err = s.generateStructCode(field.ComponentType); err != nil { + return "", nil, err + } + + templateKey = decodeStructSlice + if err = s.generateObjectArray(field); err != nil { + return "", nil, err + } + + break main + } else if field.IsSlice { + if f, _, ok := s.typedFieldDecode(field, field.ComponentType); ok { + templateKey = decodeStructSlice + if err = f(field); err != nil { + return "", nil, err + } + } else { + templateKey = decodeStructSlice + if err = s.generateObjectArray(field); err != nil { + return "", nil, err + } + } + } else if _, k, ok := s.typedFieldDecode(field, field.Type); ok { + templateKey = k + } else { + templateKey = decodeUnknown + } + } + if templateKey != -1 { + decodingCase, err := expandFieldTemplate(templateKey, field) + if err != nil { + return "", nil, err + } + fieldCases = append(fieldCases, decodingCase) + } + + } + return initCode, fieldCases, nil +} + +func (s *Struct) generateEmbeddedFieldEncoding(field *Field, fieldTypeInfo *toolbox.TypeInfo) ([]string, error) { + var result = []string{} + if fieldTypeInfo != nil { + embeddedCases, err := s.generateFieldEncoding(fieldTypeInfo.Fields()) + if err != nil { + return nil, err + } + if field.IsPointer { + result = append(result, fmt.Sprintf(" if %v != nil {", field.Accessor)) + for _, code := range embeddedCases { + result = append(result, " "+code) + } + result = append(result, " }") + } else { + result = append(result, embeddedCases...) + } + } + return result, nil +} + +func (s *Struct) generateFieldEncoding(fields []*toolbox.FieldInfo) ([]string, error) { + fieldCases := []string{} + for i := range fields { + if isSkipable(s.options, fields[i]) { + continue + } + var templateKey = -1 + fieldTypeInfo := s.Type(normalizeTypeName(fields[i].TypeName)) + field, err := NewField(s, fields[i], fieldTypeInfo) + if err != nil { + return nil, err + } + if field.IsAnonymous { + embedded, err := s.generateEmbeddedFieldEncoding(field, fieldTypeInfo) + if err != nil { + return nil, err + } + fieldCases = append(fieldCases, embedded...) + continue + } + main: + switch field.Type { + case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": + templateKey = encodeBaseType + case "[]string", "[]bool", "[]int", "[]int8", "[]int16", "[]int32", "[]int64", "[]uint", "[]uint8", "[]uint16", "[]uint32", "[]uint64", "[]float32", "[]float64": + templateKey = encodeBaseTypeSlice + s.generatePrimitiveArray(field) + case "[]byte": + templateKey = encodeRawType + default: + if fieldTypeInfo != nil { + if !(field.IsSlice || fieldTypeInfo.IsSlice) { + templateKey = encodeStruct + break main + } + switch fieldTypeInfo.ComponentType { + case "byte": + templateKey = encodeRawType + break main + case "string", "bool", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64": + templateKey = decodeBaseTypeSlice + break main + } + templateKey = encodeStructSlice + break main + } else if field.IsSlice { + s.typedFieldDecode(field, field.ComponentType) + templateKey = encodeStructSlice + } else if _, k, ok := s.typedFieldEncode(field, field.Type); ok { + templateKey = k + } else { + templateKey = encodeUnknown + } + } + if templateKey != -1 { + decodingCase, err := expandFieldTemplate(templateKey, field) + if err != nil { + return nil, err + } + fieldCases = append(fieldCases, decodingCase) + } + + } + return fieldCases, nil +} + +var sqlNullTypes = []string{ + "Bool", + "Float64", + "Int64", + "String", + "Time", +} + +func (s *Struct) typedFieldEncode(field *Field, typeName string) (func(*Field) error, int, bool) { + if strings.Contains(typeName, "time.Time") { + return s.generateTimeArray, encodeTime, true + } else if strings.Contains(typeName, "sql.Null") { + for _, nullType := range sqlNullTypes { + if strings.Contains(typeName, nullType) { + field.NullType = nullType + field.GojayMethod = "SQLNull" + nullType + } + } + return s.generateTypedArray, encodeSQLNull, true + } + return nil, 0, false +} + +func (s *Struct) typedFieldDecode(field *Field, typeName string) (func(*Field) error, int, bool) { + if strings.Contains(typeName, "time.Time") { + s.addImport("time") + return s.generateTimeArray, decodeTime, true + } else if strings.Contains(typeName, "sql.Null") { + for _, nullType := range sqlNullTypes { + if strings.Contains(typeName, nullType) { + field.NullType = nullType + field.GojayMethod = "SQLNull" + nullType + } + } + s.addImport("database/sql") + return s.generateTypedArray, decodeSQLNull, true + } + return nil, 0, false +} + +func NewStruct(info *toolbox.TypeInfo, generator *Generator) *Struct { + return &Struct{ + TypeInfo: info, + Generator: generator, + Alias: extractReceiverAlias(info.Name), + } +} diff --git a/gojay/codegen/template.go b/gojay/codegen/template.go @@ -0,0 +1,325 @@ +package codegen + +import ( + "bytes" + "fmt" + "text/template" +) + +const ( + decodeBaseType = iota + encodeBaseType + decodeBaseTypeSlice + encodeBaseTypeSlice + decodeRawType + encodeRawType + decodeStruct + encodeStruct + + decodeStructSlice + encodeStructSlice + decodeTime + encodeTime + + decodeSQLNull + encodeSQLNull + + decodeUnknown + encodeUnknown + + resetFieldValue + poolInstanceRelease + poolSliceInstanceRelease +) + +var fieldTemplate = map[int]string{ + decodeBaseType: ` case "{{.Key}}": +{{if .IsPointer}} var value {{.Type}} + err := dec.{{.DecodingMethod}}(&value) + if err == nil { + {{.Accessor}} = &value + } + return err +{{else}} return dec.{{.DecodingMethod}}(&{{.Accessor}}){{end}} +`, + encodeBaseType: ` enc.{{.EncodingMethod}}Key{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Accessor}})`, + + decodeBaseTypeSlice: ` case "{{.Key}}": + var aSlice = {{.HelperType}}{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + {{.Mutator}} = {{.RawType}}(aSlice) + } + return err +`, + encodeBaseTypeSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}}) + enc.ArrayKey{{.OmitEmpty}}("{{.Key}}",{{.Var}}Slice)`, + + decodeRawType: ` case "{{.Key}}": + var value = gojay.EmbeddedJSON{} + err := dec.AddEmbeddedJSON(&value) + if err == nil && len(value) > 0 { + {{.Mutator}} = {{.Type}}(value) + } + return err +`, + + encodeRawType: ` var {{.Var}}Slice = gojay.EmbeddedJSON({{.Accessor}}) + enc.AddEmbeddedJSONKey{{.OmitEmpty}}("{{.Key}}", &{{.Var}}Slice)`, + decodeStruct: ` case "{{.Key}}":{{if .IsPointer}} + var value = {{.Init}} + err := dec.Object(value) + if err == nil { + {{.Mutator}} = value + } +{{else}} + err := dec.Object(&{{.Mutator}}) +{{end}} + return err +`, + encodeStruct: ` enc.ObjectKey{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}})`, + + decodeStructSlice: ` case "{{.Key}}": + var aSlice = {{.HelperType}}{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + {{.Mutator}} = {{.RawType}}(aSlice) + } + return err + `, + + encodeStructSlice: ` var {{.Var}}Slice = {{.HelperType}}({{.Accessor}}) + enc.ArrayKey{{.OmitEmpty}}("{{.Key}}", {{.DereferenceModifier}}{{.Var}}Slice)`, + + decodeTime: ` case "{{.Key}}": + var format = {{.TimeLayout}} + var value = {{.Init}} + err := dec.Time({{.PointerModifier}}value, format) + if err == nil { + {{.Mutator}} = value + } + return err +`, + + encodeTime: `{{if .IsPointer}} if {{.Accessor}} != nil { + enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}}) + }{{else}} enc.TimeKey("{{.Key}}", {{.PointerModifier}}{{.Accessor}}, {{.TimeLayout}}){{end}}`, + decodeSQLNull: ` case "{{.Key}}": + var value = {{.Init}} + err := dec.SQLNull{{.NullType}}({{.PointerModifier}}value) + if err == nil { + {{.Mutator}} = value + } + return err +`, + encodeSQLNull: `{{if .IsPointer}} if {{.Accessor}} != nil { + enc.SQLNull{{.NullType}}Key{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}}) + }{{else}} enc.SQLNull{{.NullType}}Key{{.OmitEmpty}}("{{.Key}}", {{.PointerModifier}}{{.Accessor}}){{end}}`, + decodeUnknown: ` case "{{.Key}}": + //TODO + //dec.Any({{.Accessor}}) + return nil +`, + encodeUnknown: ` + //TODO + //enc.Any({{.Accessor}}) + +`, + resetFieldValue: `{{if .ResetDependency}}{{.ResetDependency}} +{{end}} {{.Mutator}} = {{.Reset}}`, + poolInstanceRelease: ` {{.PoolName}}.Put({{.Accessor}})`, + + poolSliceInstanceRelease: ` for i := range {{.Accessor}} { + {{.Accessor}}[i].Reset() + {{.PoolName}}.Put({{.PointerModifier}}{{.Accessor}}[i]) + }`, +} + +const ( + fileCode = iota + encodingStructType + baseTypeSlice + structTypeSlice + resetStruct + poolVar + poolInit + embeddedStructInit + timeSlice + typeSlice +) + +var blockTemplate = map[int]string{ + fileCode: `// Code generated by Gojay. DO NOT EDIT. + + +package {{.Pkg}} + +import ( + {{.Imports}} +) +{{if .Init}} +func init() { +{{.Init}} +} +{{end}} +{{.Code}} +`, + encodingStructType: `// MarshalJSONObject implements MarshalerJSONObject +func ({{.Receiver}}) MarshalJSONObject(enc *gojay.Encoder) { +{{.EncodingCases}} +} + +// IsNil checks if instance is nil +func ({{.Receiver}}) IsNil() bool { + return {{.Alias}} == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func ({{.Receiver}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { +{{.InitEmbedded}} + switch k { +{{.DecodingCases}} + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func ({{.Receiver}}) NKeys() int { return {{.FieldCount}} } + +{{.Reset}} + +`, + + baseTypeSlice: ` + +type {{.HelperType}} {{.RawType}} + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value {{.ComponentType}} + if err := dec.{{.DecodingMethod}}(&value); err != nil { + return err + } + *a = append(*a, {{.ComponentInitModifier}}value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.{{.EncodingMethod}}({{.ComponentDereferenceModifier}}item) + } +} + +// IsNil checks if array is nil +func (a {{.HelperType}}) IsNil() bool { + return len(a) == 0 +} +`, + + structTypeSlice: ` +type {{.HelperType}} {{.RawType}} + +func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = {{.ComponentInit}} + if err := dec.Object({{.ComponentPointerModifier}}value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object({{.ComponentPointerModifier}}s[i]) + } +} + +func (s {{.HelperType}}) IsNil() bool { + return len(s) == 0 +} +`, + typeSlice: ` +type {{.HelperType}} {{.RawType}} + +func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = {{.ComponentInit}} + if err := dec.{{.GojayMethod}}({{.ComponentPointerModifier}}value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.{{.GojayMethod}}({{.ComponentPointerModifier}}s[i]) + } +} + +func (s {{.HelperType}}) IsNil() bool { + return len(s) == 0 +} +`, + timeSlice: ` +type {{.HelperType}} {{.RawType}} + +func (s *{{.HelperType}}) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = {{.ComponentInit}} + if err := dec.Time({{.ComponentPointerModifier}}value, {{.TimeLayout}}); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s {{.HelperType}}) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Time({{.ComponentPointerModifier}}s[i], {{.TimeLayout}}) + } +} + +func (s {{.HelperType}}) IsNil() bool { + return len(s) == 0 +} +`, + resetStruct: ` +// Reset reset fields +func ({{.Receiver}}) Reset() { +{{.Reset}} +} +`, + + poolVar: `var {{.PoolName}} *sync.Pool`, + poolInit: ` {{.PoolName}} = &sync.Pool { + New: func()interface{} { + return &{{.Type}}{} + }, + }`, + embeddedStructInit: `if {{.Accessor}} == nil { + {{.Accessor}} = {{.Init}} + }`, +} + +func expandTemplate(namespace string, dictionary map[int]string, key int, data interface{}) (string, error) { + var id = fmt.Sprintf("%v_%v", namespace, key) + textTemplate, ok := dictionary[key] + if !ok { + return "", fmt.Errorf("failed to lookup template for %v.%v", namespace, key) + } + temlate, err := template.New(id).Parse(textTemplate) + if err != nil { + return "", fmt.Errorf("fiailed to parse template %v %v, due to %v", namespace, key, err) + } + writer := new(bytes.Buffer) + err = temlate.Execute(writer, data) + return writer.String(), err +} + +func expandFieldTemplate(key int, data interface{}) (string, error) { + return expandTemplate("fieldTemplate", fieldTemplate, key, data) +} + +func expandBlockTemplate(key int, data interface{}) (string, error) { + return expandTemplate("blockTemplate", blockTemplate, key, data) +} diff --git a/codegen/template_test.go b/gojay/codegen/template_test.go diff --git a/gojay/codegen/test/annotated_struct/encoding.go b/gojay/codegen/test/annotated_struct/encoding.go @@ -0,0 +1,289 @@ +// Code generated by Gojay. DO NOT EDIT. + +package annotated_struct + +import ( + "database/sql" + "github.com/francoispqt/gojay" + "time" +) + +type Ints []int + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value int + if err := dec.Int(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Int(item) + } +} + +// IsNil checks if array is nil +func (a Ints) IsNil() bool { + return len(a) == 0 +} + +type Float32s []float32 + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Float32s) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value float32 + if err := dec.Float32(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Float32s) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Float32(item) + } +} + +// IsNil checks if array is nil +func (a Float32s) IsNil() bool { + return len(a) == 0 +} + +type SubMessagesPtr []*SubMessage + +func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = &SubMessage{} + if err := dec.Object(value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(s[i]) + } +} + +func (s SubMessagesPtr) IsNil() bool { + return len(s) == 0 +} + +type SubMessages []SubMessage + +func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = SubMessage{} + if err := dec.Object(&value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(&s[i]) + } +} + +func (s SubMessages) IsNil() bool { + return len(s) == 0 +} + +// MarshalJSONObject implements MarshalerJSONObject +func (p *Payload) MarshalJSONObject(enc *gojay.Encoder) { + +} + +// IsNil checks if instance is nil +func (p *Payload) IsNil() bool { + return p == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (p *Payload) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (p *Payload) NKeys() int { return 0 } + +// MarshalJSONObject implements MarshalerJSONObject +func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("id", m.Id) + enc.StringKey("name", m.Name) + enc.Float64Key("price", m.Price) + var intsSlice = Ints(m.Ints) + enc.ArrayKey("ints", intsSlice) + var floatsSlice = Float32s(m.Floats) + enc.ArrayKey("floats", floatsSlice) + enc.ObjectKey("subMessageX", m.SubMessageX) + var messagesXSlice = SubMessagesPtr(m.MessagesX) + enc.ArrayKey("messagesX", messagesXSlice) + enc.ObjectKey("SubMessageY", &m.SubMessageY) + var messagesYSlice = SubMessages(m.MessagesY) + enc.ArrayKey("MessagesY", messagesYSlice) + enc.BoolKey("enabled", *m.IsTrue) + var payloadSlice = gojay.EmbeddedJSON(m.Payload) + enc.AddEmbeddedJSONKey("data", &payloadSlice) + if m.SQLNullString != nil { + enc.SQLNullStringKey("sqlNullString", m.SQLNullString) + } +} + +// IsNil checks if instance is nil +func (m *Message) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "id": + return dec.Int(&m.Id) + + case "name": + return dec.String(&m.Name) + + case "price": + return dec.Float64(&m.Price) + + case "ints": + var aSlice = Ints{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Ints = []int(aSlice) + } + return err + + case "floats": + var aSlice = Float32s{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Floats = []float32(aSlice) + } + return err + + case "subMessageX": + var value = &SubMessage{} + err := dec.Object(value) + if err == nil { + m.SubMessageX = value + } + + return err + + case "messagesX": + var aSlice = SubMessagesPtr{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesX = []*SubMessage(aSlice) + } + return err + + case "SubMessageY": + err := dec.Object(&m.SubMessageY) + + return err + + case "MessagesY": + var aSlice = SubMessages{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesY = []SubMessage(aSlice) + } + return err + + case "enabled": + var value bool + err := dec.Bool(&value) + if err == nil { + m.IsTrue = &value + } + return err + + case "data": + var value = gojay.EmbeddedJSON{} + err := dec.AddEmbeddedJSON(&value) + if err == nil && len(value) > 0 { + m.Payload = Payload(value) + } + return err + + case "sqlNullString": + var value = &sql.NullString{} + err := dec.SQLNullString(value) + if err == nil { + m.SQLNullString = value + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *Message) NKeys() int { return 12 } + +// MarshalJSONObject implements MarshalerJSONObject +func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("id", m.Id) + enc.StringKey("description", m.Description) + enc.TimeKey("startDate", &m.StartTime, "2006-01-02 15:04:05") + if m.EndTime != nil { + enc.TimeKey("endDate", m.EndTime, "2006-01-02 15:04:05") + } +} + +// IsNil checks if instance is nil +func (m *SubMessage) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "id": + return dec.Int(&m.Id) + + case "description": + return dec.String(&m.Description) + + case "startDate": + var format = "2006-01-02 15:04:05" + var value = time.Time{} + err := dec.Time(&value, format) + if err == nil { + m.StartTime = value + } + return err + + case "endDate": + var format = "2006-01-02 15:04:05" + var value = &time.Time{} + err := dec.Time(value, format) + if err == nil { + m.EndTime = value + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *SubMessage) NKeys() int { return 4 } diff --git a/gojay/codegen/test/annotated_struct/encoding_test.go b/gojay/codegen/test/annotated_struct/encoding_test.go @@ -0,0 +1,126 @@ +package annotated_struct + +import ( + "bytes" + "database/sql" + "log" + "testing" + + "github.com/francoispqt/gojay" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var isTrue = true +var msg = &Message{ + Id: 1022, + Name: "name acc", + Price: 13.3, + Ints: []int{1, 2, 5}, + Floats: []float32{2.3, 4.6, 7.4}, + SubMessageX: &SubMessage{ + Id: 102, + Description: "abcd", + }, + MessagesX: []*SubMessage{ + &SubMessage{ + Id: 2102, + Description: "abce", + }, + }, + SubMessageY: SubMessage{ + Id: 3102, + Description: "abcf", + }, + MessagesY: []SubMessage{ + SubMessage{ + Id: 5102, + Description: "abcg", + }, + SubMessage{ + Id: 5106, + Description: "abcgg", + }, + }, + IsTrue: &isTrue, + Payload: []byte(`"123"`), + SQLNullString: &sql.NullString{ + String: "test", + Valid: true, + }, +} + +var jsonData = `{ + "id": 1022, + "name": "name acc", + "price": 13.3, + "ints": [ + 1, + 2, + 5 + ], + "floats": [ + 2.3, + 4.6, + 7.4 + ], + "subMessageX": { + "id": 102, + "description": "abcd", + "startDate": "0001-01-01 00:00:00" + }, + "messagesX": [ + { + "id": 2102, + "description": "abce", + "startDate": "0001-01-01 00:00:00" + } + ], + "SubMessageY": { + "id": 3102, + "description": "abcf", + "startDate": "0001-01-01 00:00:00" + }, + "MessagesY": [ + { + "id": 5102, + "description": "abcg", + "startDate": "0001-01-01 00:00:00" + }, + { + "id": 5106, + "description": "abcgg", + "startDate": "0001-01-01 00:00:00" + } + ], + "enabled": true, + "data": "123", + "sqlNullString": "test" +}` + +func TestMessage_Unmarshal(t *testing.T) { + + var err error + var data = []byte(jsonData) + message := &Message{} + err = gojay.UnmarshalJSONObject(data, message) + if !assert.Nil(t, err) { + log.Fatal(err) + } + require.Equal( + t, + msg, + message, + ) +} + +func TestMessage_Marshal(t *testing.T) { + var err error + var writer = new(bytes.Buffer) + encoder := gojay.NewEncoder(writer) + err = encoder.Encode(msg) + assert.Nil(t, err) + var JSON = writer.String() + require.JSONEq(t, jsonData, JSON) + +} diff --git a/gojay/codegen/test/annotated_struct/message.go b/gojay/codegen/test/annotated_struct/message.go @@ -0,0 +1,21 @@ +package annotated_struct + +import "database/sql" + +type Payload []byte + +type Message struct { + Id int `json:"id"` + Name string `json:"name"` + Price float64 `json:"price"` + Ints []int `json:"ints"` + Floats []float32 `json:"floats"` + SubMessageX *SubMessage `json:"subMessageX"` + MessagesX []*SubMessage `json:"messagesX"` + SubMessageY SubMessage + MessagesY []SubMessage + IsTrue *bool `json:"enabled"` + Payload Payload `json:"data"` + Ignore string `json:"-"` + SQLNullString *sql.NullString `json:"sqlNullString"` +} diff --git a/gojay/codegen/test/annotated_struct/sub_message.go b/gojay/codegen/test/annotated_struct/sub_message.go @@ -0,0 +1,10 @@ +package annotated_struct + +import "time" + +type SubMessage struct { + Id int `json:"id"` + Description string `json:"description"` + StartTime time.Time `json:"startDate" timeFormat:"yyyy-MM-dd HH:mm:ss"` + EndTime *time.Time `json:"endDate" timeLayout:"2006-01-02 15:04:05"` +} diff --git a/gojay/codegen/test/basic_struct/encoding.go b/gojay/codegen/test/basic_struct/encoding.go @@ -0,0 +1,267 @@ +// Code generated by Gojay. DO NOT EDIT. + +package basic_struct + +import ( + "database/sql" + "github.com/francoispqt/gojay" + "time" +) + +type SubMessagesPtr []*SubMessage + +func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = &SubMessage{} + if err := dec.Object(value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(s[i]) + } +} + +func (s SubMessagesPtr) IsNil() bool { + return len(s) == 0 +} + +type SubMessages []SubMessage + +func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = SubMessage{} + if err := dec.Object(&value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(&s[i]) + } +} + +func (s SubMessages) IsNil() bool { + return len(s) == 0 +} + +type Ints []int + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value int + if err := dec.Int(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Int(item) + } +} + +// IsNil checks if array is nil +func (a Ints) IsNil() bool { + return len(a) == 0 +} + +type Float32s []float32 + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Float32s) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value float32 + if err := dec.Float32(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Float32s) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Float32(item) + } +} + +// IsNil checks if array is nil +func (a Float32s) IsNil() bool { + return len(a) == 0 +} + +// MarshalJSONObject implements MarshalerJSONObject +func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("Id", m.Id) + enc.StringKey("Description", m.Description) + enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) + if m.EndTime != nil { + enc.TimeKey("EndTime", m.EndTime, time.RFC3339) + } +} + +// IsNil checks if instance is nil +func (m *SubMessage) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "Id": + return dec.Int(&m.Id) + + case "Description": + return dec.String(&m.Description) + + case "StartTime": + var format = time.RFC3339 + var value = time.Time{} + err := dec.Time(&value, format) + if err == nil { + m.StartTime = value + } + return err + + case "EndTime": + var format = time.RFC3339 + var value = &time.Time{} + err := dec.Time(value, format) + if err == nil { + m.EndTime = value + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *SubMessage) NKeys() int { return 4 } + +// MarshalJSONObject implements MarshalerJSONObject +func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("Id", m.Id) + enc.StringKey("Name", m.Name) + enc.Float64Key("Price", m.Price) + var intsSlice = Ints(m.Ints) + enc.ArrayKey("Ints", intsSlice) + var floatsSlice = Float32s(m.Floats) + enc.ArrayKey("Floats", floatsSlice) + enc.ObjectKey("SubMessageX", m.SubMessageX) + var messagesXSlice = SubMessagesPtr(m.MessagesX) + enc.ArrayKey("MessagesX", messagesXSlice) + enc.ObjectKey("SubMessageY", &m.SubMessageY) + var messagesYSlice = SubMessages(m.MessagesY) + enc.ArrayKey("MessagesY", messagesYSlice) + enc.BoolKey("IsTrue", *m.IsTrue) + var payloadSlice = gojay.EmbeddedJSON(m.Payload) + enc.AddEmbeddedJSONKey("Payload", &payloadSlice) + if m.SQLNullString != nil { + enc.SQLNullStringKey("SQLNullString", m.SQLNullString) + } +} + +// IsNil checks if instance is nil +func (m *Message) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "Id": + return dec.Int(&m.Id) + + case "Name": + return dec.String(&m.Name) + + case "Price": + return dec.Float64(&m.Price) + + case "Ints": + var aSlice = Ints{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Ints = []int(aSlice) + } + return err + + case "Floats": + var aSlice = Float32s{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Floats = []float32(aSlice) + } + return err + + case "SubMessageX": + var value = &SubMessage{} + err := dec.Object(value) + if err == nil { + m.SubMessageX = value + } + + return err + + case "MessagesX": + var aSlice = SubMessagesPtr{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesX = []*SubMessage(aSlice) + } + return err + + case "SubMessageY": + err := dec.Object(&m.SubMessageY) + + return err + + case "MessagesY": + var aSlice = SubMessages{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesY = []SubMessage(aSlice) + } + return err + + case "IsTrue": + var value bool + err := dec.Bool(&value) + if err == nil { + m.IsTrue = &value + } + return err + + case "Payload": + var value = gojay.EmbeddedJSON{} + err := dec.AddEmbeddedJSON(&value) + if err == nil && len(value) > 0 { + m.Payload = []byte(value) + } + return err + + case "SQLNullString": + var value = &sql.NullString{} + err := dec.SQLNullString(value) + if err == nil { + m.SQLNullString = value + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *Message) NKeys() int { return 12 } diff --git a/gojay/codegen/test/basic_struct/encoding_test.go b/gojay/codegen/test/basic_struct/encoding_test.go @@ -0,0 +1,118 @@ +package basic_struct + +import ( + "bytes" + "database/sql" + "testing" + + "github.com/francoispqt/gojay" + "github.com/stretchr/testify/require" +) + +var isTrue = true +var msg = &Message{ + Id: 1022, + Name: "name acc", + Price: 13.3, + Ints: []int{1, 2, 5}, + Floats: []float32{2.3, 4.6, 7.4}, + SubMessageX: &SubMessage{ + Id: 102, + Description: "abcd", + }, + MessagesX: []*SubMessage{ + &SubMessage{ + Id: 2102, + Description: "abce", + }, + }, + SubMessageY: SubMessage{ + Id: 3102, + Description: "abcf", + }, + MessagesY: []SubMessage{ + SubMessage{ + Id: 5102, + Description: "abcg", + }, + SubMessage{ + Id: 5106, + Description: "abcgg", + }, + }, + IsTrue: &isTrue, + Payload: []byte(`"123"`), + SQLNullString: &sql.NullString{ + String: "test", + Valid: true, + }, +} + +var jsonData = `{ + "Id": 1022, + "Name": "name acc", + "Price": 13.3, + "Ints": [ + 1, + 2, + 5 + ], + "Floats": [ + 2.3, + 4.6, + 7.4 + ], + "SubMessageX": { + "Id": 102, + "Description": "abcd", + "StartTime": "0001-01-01T00:00:00Z" + }, + "MessagesX": [ + { + "Id": 2102, + "Description": "abce", + "StartTime": "0001-01-01T00:00:00Z" + } + ], + "SubMessageY": { + "Id": 3102, + "Description": "abcf", + "StartTime": "0001-01-01T00:00:00Z" + }, + "MessagesY": [ + { + "Id": 5102, + "Description": "abcg", + "StartTime": "0001-01-01T00:00:00Z" + }, + { + "Id": 5106, + "Description": "abcgg", + "StartTime": "0001-01-01T00:00:00Z" + } + ], + "IsTrue": true, + "Payload": "123", + "SQLNullString": "test" +}` + +func TestMessage_Unmarshal(t *testing.T) { + var err error + var data = []byte(jsonData) + message := &Message{} + err = gojay.UnmarshalJSONObject(data, message) + require.Nil(t, err) + require.Equal(t, msg, message) +} + +func TestMessage_Marshal(t *testing.T) { + var writer = new(bytes.Buffer) + + encoder := gojay.NewEncoder(writer) + var err = encoder.Encode(msg) + + require.Nil(t, err) + var JSON = writer.String() + + require.JSONEq(t, jsonData, JSON) +} diff --git a/gojay/codegen/test/basic_struct/message.go b/gojay/codegen/test/basic_struct/message.go @@ -0,0 +1,18 @@ +package basic_struct + +import "database/sql" + +type Message struct { + Id int + Name string + Price float64 + Ints []int + Floats []float32 + SubMessageX *SubMessage + MessagesX []*SubMessage + SubMessageY SubMessage + MessagesY []SubMessage + IsTrue *bool + Payload []byte + SQLNullString *sql.NullString +} diff --git a/gojay/codegen/test/basic_struct/sub_message.go b/gojay/codegen/test/basic_struct/sub_message.go @@ -0,0 +1,10 @@ +package basic_struct + +import "time" + +type SubMessage struct { + Id int + Description string + StartTime time.Time + EndTime *time.Time +} diff --git a/gojay/codegen/test/embedded_struct/encoding.go b/gojay/codegen/test/embedded_struct/encoding.go @@ -0,0 +1,309 @@ +// Code generated by Gojay. DO NOT EDIT. + +package embedded_struct + +import ( + "github.com/francoispqt/gojay" + "time" +) + +type Ints []int + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value int + if err := dec.Int(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Int(item) + } +} + +// IsNil checks if array is nil +func (a Ints) IsNil() bool { + return len(a) == 0 +} + +type Float64s []float64 + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Float64s) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value float64 + if err := dec.Float64(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Float64s) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Float64(item) + } +} + +// IsNil checks if array is nil +func (a Float64s) IsNil() bool { + return len(a) == 0 +} + +type SubMessagesPtr []*SubMessage + +func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = &SubMessage{} + if err := dec.Object(value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(s[i]) + } +} + +func (s SubMessagesPtr) IsNil() bool { + return len(s) == 0 +} + +type SubMessages []SubMessage + +func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = SubMessage{} + if err := dec.Object(&value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(&s[i]) + } +} + +func (s SubMessages) IsNil() bool { + return len(s) == 0 +} + +// MarshalJSONObject implements MarshalerJSONObject +func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { + enc.StringKey("Description", m.Description) + enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) + if m.EndTime != nil { + enc.TimeKey("EndTime", m.EndTime, time.RFC3339) + } +} + +// IsNil checks if instance is nil +func (m *SubMessage) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "Description": + return dec.String(&m.Description) + + case "StartTime": + var format = time.RFC3339 + var value = time.Time{} + err := dec.Time(&value, format) + if err == nil { + m.StartTime = value + } + return err + + case "EndTime": + var format = time.RFC3339 + var value = &time.Time{} + err := dec.Time(value, format) + if err == nil { + m.EndTime = value + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *SubMessage) NKeys() int { return 3 } + +// MarshalJSONObject implements MarshalerJSONObject +func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { + if m.BaseId != nil { + enc.IntKey("Id", m.Id) + enc.StringKey("Name", m.Name) + } + enc.StringKey("Description", m.Description) + enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) + if m.EndTime != nil { + enc.TimeKey("EndTime", m.EndTime, time.RFC3339) + } + enc.Float64Key("Price", m.Price) + var intsSlice = Ints(m.Ints) + enc.ArrayKey("Ints", intsSlice) + var floatsSlice = Float64s(m.Floats) + enc.ArrayKey("Floats", floatsSlice) + enc.ObjectKey("SubMessageX", m.SubMessageX) + var messagesXSlice = SubMessagesPtr(m.MessagesX) + enc.ArrayKey("MessagesX", messagesXSlice) + enc.ObjectKey("SubMessageY", &m.SubMessageY) + var messagesYSlice = SubMessages(m.MessagesY) + enc.ArrayKey("MessagesY", messagesYSlice) + enc.BoolKey("IsTrue", *m.IsTrue) + var payloadSlice = gojay.EmbeddedJSON(m.Payload) + enc.AddEmbeddedJSONKey("Payload", &payloadSlice) +} + +// IsNil checks if instance is nil +func (m *Message) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + if m.BaseId == nil { + m.BaseId = &BaseId{} + } + switch k { + case "Id": + return dec.Int(&m.Id) + + case "Name": + return dec.String(&m.Name) + + case "Description": + return dec.String(&m.Description) + + case "StartTime": + var format = time.RFC3339 + var value = time.Time{} + err := dec.Time(&value, format) + if err == nil { + m.StartTime = value + } + return err + + case "EndTime": + var format = time.RFC3339 + var value = &time.Time{} + err := dec.Time(value, format) + if err == nil { + m.EndTime = value + } + return err + + case "Price": + return dec.Float64(&m.Price) + + case "Ints": + var aSlice = Ints{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Ints = []int(aSlice) + } + return err + + case "Floats": + var aSlice = Float64s{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Floats = []float64(aSlice) + } + return err + + case "SubMessageX": + var value = &SubMessage{} + err := dec.Object(value) + if err == nil { + m.SubMessageX = value + } + + return err + + case "MessagesX": + var aSlice = SubMessagesPtr{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesX = []*SubMessage(aSlice) + } + return err + + case "SubMessageY": + err := dec.Object(&m.SubMessageY) + + return err + + case "MessagesY": + var aSlice = SubMessages{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesY = []SubMessage(aSlice) + } + return err + + case "IsTrue": + var value bool + err := dec.Bool(&value) + if err == nil { + m.IsTrue = &value + } + return err + + case "Payload": + var value = gojay.EmbeddedJSON{} + err := dec.AddEmbeddedJSON(&value) + if err == nil && len(value) > 0 { + m.Payload = []byte(value) + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *Message) NKeys() int { return 14 } + +// MarshalJSONObject implements MarshalerJSONObject +func (i *BaseId) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("Id", i.Id) + enc.StringKey("Name", i.Name) +} + +// IsNil checks if instance is nil +func (i *BaseId) IsNil() bool { + return i == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (i *BaseId) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "Id": + return dec.Int(&i.Id) + + case "Name": + return dec.String(&i.Name) + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (i *BaseId) NKeys() int { return 2 } diff --git a/gojay/codegen/test/embedded_struct/encoding_test.go b/gojay/codegen/test/embedded_struct/encoding_test.go @@ -0,0 +1,106 @@ +package embedded_struct + +import ( + "bytes" + "github.com/francoispqt/gojay" + "github.com/stretchr/testify/assert" + "github.com/viant/assertly" + "testing" +) + +func TestMessage_Unmarshal(t *testing.T) { + + input := `{ + "Id": 1022, + "Name": "name acc", + "Description": "abcd", + "Price": 13.3, + "Ints": [ + 1, + 2, + 5 + ], + "Floats": [ + 2.3, + 4.6, + 7.4 + ], + "MessagesX": [ + { + "Description": "abce" + } + ], + "SubMessageY": { + "Description": "abcf" + }, + "MessagesY": [ + { + "Description": "abcg" + }, + { + "Description": "abcgg" + } + ], + "IsTrue": true, + "Payload": "" +}` + + var err error + var data = []byte(input) + message := &Message{} + err = gojay.UnmarshalJSONObject(data, message) + assert.Nil(t, err) + assertly.AssertValues(t, input, message) +} + +func TestMessage_Marshal(t *testing.T) { + + input := `{ + "Id": 1022, + "Name": "name acc", + "Description": "abcd", + "Price": 13.3, + "Ints": [ + 1, + 2, + 5 + ], + "Floats": [ + 2.3, + 4.6, + 7.4 + ], + "MessagesX": [ + { + "Description": "abce" + } + ], + "SubMessageY": { + "Description": "abcf" + }, + "MessagesY": [ + { + "Description": "abcg" + }, + { + "Description": "abcgg" + } + ], + "IsTrue": true, + "Payload": "" +}` + + var err error + var data = []byte(input) + message := &Message{} + err = gojay.UnmarshalJSONObject(data, message) + assert.Nil(t, err) + assertly.AssertValues(t, input, message) + var writer = new(bytes.Buffer) + + encoder := gojay.NewEncoder(writer) + err = encoder.Encode(message) + assert.Nil(t, err) + var JSON = writer.String() + assertly.AssertValues(t, input, JSON) +} diff --git a/gojay/codegen/test/embedded_struct/message.go b/gojay/codegen/test/embedded_struct/message.go @@ -0,0 +1,20 @@ +package embedded_struct + +type BaseId struct { + Id int + Name string +} + +type Message struct { + *BaseId + SubMessage + Price float64 + Ints []int + Floats []float64 + SubMessageX *SubMessage + MessagesX []*SubMessage + SubMessageY SubMessage + MessagesY []SubMessage + IsTrue *bool + Payload []byte +} diff --git a/gojay/codegen/test/embedded_struct/sub_message.go b/gojay/codegen/test/embedded_struct/sub_message.go @@ -0,0 +1,9 @@ +package embedded_struct + +import "time" + +type SubMessage struct { + Description string + StartTime time.Time + EndTime *time.Time +} diff --git a/gojay/codegen/test/pooled_struct/encoding.go b/gojay/codegen/test/pooled_struct/encoding.go @@ -0,0 +1,302 @@ +// Code generated by Gojay. DO NOT EDIT. + +package pooled_struct + +import ( + "github.com/francoispqt/gojay" + "sync" + "time" +) + +func init() { + MessagePool = &sync.Pool{ + New: func() interface{} { + return &Message{} + }, + } + SubMessagePool = &sync.Pool{ + New: func() interface{} { + return &SubMessage{} + }, + } +} + +var MessagePool *sync.Pool +var SubMessagePool *sync.Pool + +type Ints []int + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Ints) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value int + if err := dec.Int(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Ints) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Int(item) + } +} + +// IsNil checks if array is nil +func (a Ints) IsNil() bool { + return len(a) == 0 +} + +type Float64s []float64 + +// UnmarshalJSONArray decodes JSON array elements into slice +func (a *Float64s) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value float64 + if err := dec.Float64(&value); err != nil { + return err + } + *a = append(*a, value) + return nil +} + +// MarshalJSONArray encodes arrays into JSON +func (a Float64s) MarshalJSONArray(enc *gojay.Encoder) { + for _, item := range a { + enc.Float64(item) + } +} + +// IsNil checks if array is nil +func (a Float64s) IsNil() bool { + return len(a) == 0 +} + +type SubMessagesPtr []*SubMessage + +func (s *SubMessagesPtr) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = &SubMessage{} + if err := dec.Object(value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessagesPtr) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(s[i]) + } +} + +func (s SubMessagesPtr) IsNil() bool { + return len(s) == 0 +} + +type SubMessages []SubMessage + +func (s *SubMessages) UnmarshalJSONArray(dec *gojay.Decoder) error { + var value = SubMessage{} + if err := dec.Object(&value); err != nil { + return err + } + *s = append(*s, value) + return nil +} + +func (s SubMessages) MarshalJSONArray(enc *gojay.Encoder) { + for i := range s { + enc.Object(&s[i]) + } +} + +func (s SubMessages) IsNil() bool { + return len(s) == 0 +} + +// MarshalJSONObject implements MarshalerJSONObject +func (m *SubMessage) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("Id", m.Id) + enc.StringKey("Description", m.Description) + enc.TimeKey("StartTime", &m.StartTime, time.RFC3339) + if m.EndTime != nil { + enc.TimeKey("EndTime", m.EndTime, time.RFC3339) + } +} + +// IsNil checks if instance is nil +func (m *SubMessage) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *SubMessage) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "Id": + return dec.Int(&m.Id) + + case "Description": + return dec.String(&m.Description) + + case "StartTime": + var format = time.RFC3339 + var value = time.Time{} + err := dec.Time(&value, format) + if err == nil { + m.StartTime = value + } + return err + + case "EndTime": + var format = time.RFC3339 + var value = &time.Time{} + err := dec.Time(value, format) + if err == nil { + m.EndTime = value + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *SubMessage) NKeys() int { return 4 } + +// Reset reset fields +func (m *SubMessage) Reset() { + m.Id = 0 + m.Description = "" + m.EndTime = nil +} + +// MarshalJSONObject implements MarshalerJSONObject +func (m *Message) MarshalJSONObject(enc *gojay.Encoder) { + enc.IntKey("Id", m.Id) + enc.StringKey("Name", m.Name) + enc.Float64Key("Price", m.Price) + var intsSlice = Ints(m.Ints) + enc.ArrayKey("Ints", intsSlice) + var floatsSlice = Float64s(m.Floats) + enc.ArrayKey("Floats", floatsSlice) + enc.ObjectKey("SubMessageX", m.SubMessageX) + var messagesXSlice = SubMessagesPtr(m.MessagesX) + enc.ArrayKey("MessagesX", messagesXSlice) + enc.ObjectKey("SubMessageY", &m.SubMessageY) + var messagesYSlice = SubMessages(m.MessagesY) + enc.ArrayKey("MessagesY", messagesYSlice) + enc.BoolKey("IsTrue", *m.IsTrue) + var payloadSlice = gojay.EmbeddedJSON(m.Payload) + enc.AddEmbeddedJSONKey("Payload", &payloadSlice) +} + +// IsNil checks if instance is nil +func (m *Message) IsNil() bool { + return m == nil +} + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (m *Message) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + + switch k { + case "Id": + return dec.Int(&m.Id) + + case "Name": + return dec.String(&m.Name) + + case "Price": + return dec.Float64(&m.Price) + + case "Ints": + var aSlice = Ints{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Ints = []int(aSlice) + } + return err + + case "Floats": + var aSlice = Float64s{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.Floats = []float64(aSlice) + } + return err + + case "SubMessageX": + var value = SubMessagePool.Get().(*SubMessage) + err := dec.Object(value) + if err == nil { + m.SubMessageX = value + } + + return err + + case "MessagesX": + var aSlice = SubMessagesPtr{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesX = []*SubMessage(aSlice) + } + return err + + case "SubMessageY": + err := dec.Object(&m.SubMessageY) + + return err + + case "MessagesY": + var aSlice = SubMessages{} + err := dec.Array(&aSlice) + if err == nil && len(aSlice) > 0 { + m.MessagesY = []SubMessage(aSlice) + } + return err + + case "IsTrue": + var value bool + err := dec.Bool(&value) + if err == nil { + m.IsTrue = &value + } + return err + + case "Payload": + var value = gojay.EmbeddedJSON{} + err := dec.AddEmbeddedJSON(&value) + if err == nil && len(value) > 0 { + m.Payload = []byte(value) + } + return err + + } + return nil +} + +// NKeys returns the number of keys to unmarshal +func (m *Message) NKeys() int { return 11 } + +// Reset reset fields +func (m *Message) Reset() { + m.Id = 0 + m.Name = "" + m.Price = 0.0 + m.Ints = nil + m.Floats = nil + SubMessagePool.Put(m.SubMessageX) + m.SubMessageX = nil + for i := range m.MessagesX { + m.MessagesX[i].Reset() + SubMessagePool.Put(m.MessagesX[i]) + } + m.MessagesX = nil + for i := range m.MessagesY { + m.MessagesY[i].Reset() + SubMessagePool.Put(&m.MessagesY[i]) + } + m.MessagesY = nil + m.IsTrue = nil + m.Payload = nil +} diff --git a/gojay/codegen/test/pooled_struct/encoding_test.go b/gojay/codegen/test/pooled_struct/encoding_test.go @@ -0,0 +1,123 @@ +package pooled_struct + +import ( + "bytes" + "github.com/francoispqt/gojay" + "github.com/stretchr/testify/assert" + "github.com/viant/assertly" + "testing" +) + +func TestMessage_Unmarshal(t *testing.T) { + + input := `{ + "Id": 1022, + "Name": "name acc", + "Price": 13.3, + "Ints": [ + 1, + 2, + 5 + ], + "Floats": [ + 2.3, + 4.6, + 7.4 + ], + "SubMessageX": { + "Id": 102, + "Description": "abcd" + }, + "MessagesX": [ + { + "Id": 2102, + "Description": "abce" + } + ], + "SubMessageY": { + "Id": 3102, + "Description": "abcf" + }, + "MessagesY": [ + { + "Id": 5102, + "Description": "abcg" + }, + { + "Id": 5106, + "Description": "abcgg" + } + ], + "IsTrue": true, + "Payload": "" +}` + + var data = []byte(input) + message := MessagePool.Get().(*Message) + err := gojay.UnmarshalJSONObject(data, message) + assert.Nil(t, err) + message.Reset() + MessagePool.Put(message) + +} + +func TestMessage_Marshal(t *testing.T) { + + input := `{ + "Id": 1022, + "Name": "name acc", + "Price": 13.3, + "Ints": [ + 1, + 2, + 5 + ], + "Floats": [ + 2.3, + 4.6, + 7.4 + ], + "SubMessageX": { + "Id": 102, + "Description": "abcd" + }, + "MessagesX": [ + { + "Id": 2102, + "Description": "abce" + } + ], + "SubMessageY": { + "Id": 3102, + "Description": "abcf" + }, + "MessagesY": [ + { + "Id": 5102, + "Description": "abcg" + }, + { + "Id": 5106, + "Description": "abcgg" + } + ], + "IsTrue": true, + "Payload": "" +}` + + var data = []byte(input) + message := MessagePool.Get().(*Message) + err := gojay.UnmarshalJSONObject(data, message) + assert.Nil(t, err) + defer func() { + message.Reset() + MessagePool.Put(message) + + }() + var writer = new(bytes.Buffer) + encoder := gojay.NewEncoder(writer) + err = encoder.Encode(message) + assert.Nil(t, err) + var JSON = writer.String() + assertly.AssertValues(t, input, JSON) +} diff --git a/gojay/codegen/test/pooled_struct/message.go b/gojay/codegen/test/pooled_struct/message.go @@ -0,0 +1,15 @@ +package pooled_struct + +type Message struct { + Id int + Name string + Price float64 + Ints []int + Floats []float64 + SubMessageX *SubMessage + MessagesX []*SubMessage + SubMessageY SubMessage + MessagesY []SubMessage + IsTrue *bool + Payload []byte +} diff --git a/gojay/codegen/test/pooled_struct/sub_message.go b/gojay/codegen/test/pooled_struct/sub_message.go @@ -0,0 +1,10 @@ +package pooled_struct + +import "time" + +type SubMessage struct { + Id int + Description string + StartTime time.Time + EndTime *time.Time +} diff --git a/gojay/gen.go b/gojay/gen.go @@ -1,146 +0,0 @@ -package main - -import ( - "go/ast" - "log" - "strings" - "text/template" -) - -const gojayAnnotation = "//gojay:json" -const genFileSuffix = "_gojay.go" -const omitEmptyFuncName = "OmitEmpty" - -var pkgTpl *template.Template -var genHeader = []byte("// Code generated by GoJay. DO NOT EDIT.\n\n") -var gojayImport = []byte("import \"github.com/francoispqt/gojay\"\n") - -// Gen is the structure representing a generator -type Gen struct { - b *strings.Builder - pkg string - src string - types []string - 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") - if err != nil { - log.Fatal(err) - } - pkgTpl = t -} - -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 - } -} - -// NewGen returns a new generator -func NewGen(p string, types []string) *Gen { - g := &Gen{ - src: p, - types: types, - 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, - }) - if err != nil { - return err - } - return nil -} - -func (g *Gen) writeGojayImport() error { - _, err := g.b.Write(gojayImport) - if err != nil { - return err - } - return nil -} - -func (g *Gen) writeGenHeader() error { - _, err := g.b.Write(genHeader) - if err != nil { - return err - } - return nil -} - -func (g *Gen) isGenType(typeName string) bool { - // check if type is in types - for _, t := range g.types { - if t == typeName { - return true - } - } - return false -} - -// Gen starts the generation writing to the string builder -func (g *Gen) Gen() error { - err := g.writeGenHeader() - if err != nil { - return err - } - - // write package - err = g.writePkg(g.pkg) - if err != nil { - return err - } - // write import of gojay - err = g.writeGojayImport() - if err != nil { - return err - } - // range over specs - // 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 - err = g.genMap(s.Name.String(), t) - if err != nil { - return err - } - } - } - return nil -} diff --git a/gojay/gen_array.go b/gojay/gen_array.go @@ -1,17 +0,0 @@ -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 - } - return g.arrGenIsNil(n) -} diff --git a/gojay/gen_array_marshal.go b/gojay/gen_array_marshal.go @@ -1,241 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "go/ast" - "log" -) - -func init() {} - -func (g *Gen) arrGenIsNil(n string) error { - err := arrMarshalTpl["isNil"].tpl.Execute(g.b, struct { - TypeName string - }{ - TypeName: n, - }) - return err -} - -func (g *Gen) arrGenMarshal(n string, s *ast.ArrayType) error { - err := arrMarshalTpl["def"].tpl.Execute(g.b, 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 fmt.Errorf("Unknown type %s", n) - } - } - _, err = g.b.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": - g.arrMarshalString(ptr) - case "bool": - g.arrMarshalBool(ptr) - case "int": - g.arrMarshalInt("", ptr) - case "int64": - g.arrMarshalInt("64", ptr) - case "int32": - g.arrMarshalInt("32", ptr) - case "int16": - g.arrMarshalInt("16", ptr) - case "int8": - g.arrMarshalInt("8", ptr) - case "uint64": - g.arrMarshalUint("64", ptr) - case "uint32": - g.arrMarshalUint("32", ptr) - case "uint16": - g.arrMarshalUint("16", ptr) - case "uint8": - g.arrMarshalUint("8", ptr) - case "float64": - g.arrMarshalFloat("64", ptr) - case "float32": - g.arrMarshalFloat("32", ptr) - default: - // if ident is already in our spec list - if sp, ok := g.genTypes[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 fmt.Errorf("Unknown type %s", i.Name) - } - return nil -} - -func (g *Gen) arrMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { - switch sp.Type.(type) { - case *ast.StructType: - g.arrMarshalStruct(sp, ptr) - case *ast.ArrayType: - g.arrMarshalArr(sp, ptr) - } - return nil -} - -func (g *Gen) arrMarshalString(ptr bool) { - if ptr { - err := arrMarshalTpl["stringPtr"].tpl.Execute(g.b, struct { - Ptr string - }{""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrMarshalTpl["string"].tpl.Execute(g.b, struct { - Ptr string - }{"&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrMarshalBool(ptr bool) { - if ptr { - err := arrMarshalTpl["bool"].tpl.Execute(g.b, struct { - Ptr string - }{""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrMarshalTpl["bool"].tpl.Execute(g.b, struct { - Ptr string - }{"&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrMarshalInt(intLen string, ptr bool) { - if ptr { - err := arrMarshalTpl["int"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrMarshalTpl["int"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrMarshalFloat(intLen string, ptr bool) { - if ptr { - err := arrMarshalTpl["float"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrMarshalTpl["float"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrMarshalUint(intLen string, ptr bool) { - if ptr { - err := arrMarshalTpl["uint"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrMarshalTpl["uint"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrMarshalStruct(st *ast.TypeSpec, ptr bool) { - if ptr { - err := arrMarshalTpl["structPtr"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrMarshalTpl["struct"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrMarshalArr(st *ast.TypeSpec, ptr bool) { - if ptr { - err := arrMarshalTpl["arrPtr"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrMarshalTpl["arr"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } -} diff --git a/gojay/gen_array_marshal_tpl.go b/gojay/gen_array_marshal_tpl.go @@ -1,63 +0,0 @@ -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", - }, - "float": &genTpl{ - strTpl: "\tfor _, s := range *v {\n" + - "\t\tenc.Float{{.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_test.go b/gojay/gen_array_test.go @@ -1,436 +0,0 @@ -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 -} -`, - }, - "basicFloatSlice": { - input: strings.NewReader(`package test - -//gojay:json -type IntSlice []float64 - `), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray -func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - var i float64 - if err := dec.Float64(&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.Float64(s) - } -} - -// IsNil implements gojay's MarshalerJSONArray -func (v *IntSlice) IsNil() bool { - return *v == nil || len(*v) == 0 -} -`, - }, - "basicFloat32Slice": { - input: strings.NewReader(`package test - -//gojay:json -type IntSlice []float32 - `), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray -func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error { - var i float32 - if err := dec.Float32(&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.Float32(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, - string(genHeader)+testCase.expectedResult, - g.b.String(), - ) - }) - } -} diff --git a/gojay/gen_array_unmarshal.go b/gojay/gen_array_unmarshal.go @@ -1,230 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "go/ast" - "log" -) - -func (g *Gen) arrGenUnmarshal(n string, s *ast.ArrayType) error { - err := arrUnmarshalTpl["def"].tpl.Execute(g.b, 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 fmt.Errorf("Unknown type %s", n) - } - } - _, err = g.b.Write(structUnmarshalClose) - if err != nil { - return err - } - return err -} - -func (g *Gen) arrGenUnmarshalIdent(i *ast.Ident, ptr bool) error { - switch i.String() { - case "string": - g.arrUnmarshalString(ptr) - case "bool": - g.arrUnmarshalBool(ptr) - case "int": - g.arrUnmarshalInt("", ptr) - case "int64": - g.arrUnmarshalInt("64", ptr) - case "int32": - g.arrUnmarshalInt("32", ptr) - case "int16": - g.arrUnmarshalInt("16", ptr) - case "int8": - g.arrUnmarshalInt("8", ptr) - case "uint64": - g.arrUnmarshalUint("64", ptr) - case "uint32": - g.arrUnmarshalUint("32", ptr) - case "uint16": - g.arrUnmarshalUint("16", ptr) - case "uint8": - g.arrUnmarshalUint("8", ptr) - case "float64": - g.arrUnmarshalFloat("64", ptr) - case "float32": - g.arrUnmarshalFloat("32", ptr) - default: - // if ident is already in our spec list - if sp, ok := g.genTypes[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 fmt.Errorf("Unknown type %s", i.Name) - } - return nil -} - -func (g *Gen) arrUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { - switch sp.Type.(type) { - case *ast.StructType: - g.arrUnmarshalStruct(sp, ptr) - case *ast.ArrayType: - g.arrUnmarshalArr(sp, ptr) - } - return nil -} - -func (g *Gen) arrUnmarshalString(ptr bool) { - if ptr { - err := arrUnmarshalTpl["stringPtr"].tpl.Execute(g.b, struct { - Ptr string - }{""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrUnmarshalTpl["string"].tpl.Execute(g.b, struct { - Ptr string - }{"&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrUnmarshalBool(ptr bool) { - if ptr { - err := arrUnmarshalTpl["bool"].tpl.Execute(g.b, struct { - Ptr string - }{""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrUnmarshalTpl["bool"].tpl.Execute(g.b, struct { - Ptr string - }{"&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrUnmarshalInt(intLen string, ptr bool) { - if ptr { - err := arrUnmarshalTpl["int"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrUnmarshalTpl["int"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrUnmarshalUint(intLen string, ptr bool) { - if ptr { - err := arrUnmarshalTpl["uint"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrUnmarshalTpl["uint"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrUnmarshalFloat(intLen string, ptr bool) { - if ptr { - err := arrUnmarshalTpl["float"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrUnmarshalTpl["float"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrUnmarshalStruct(st *ast.TypeSpec, ptr bool) { - if ptr { - err := arrUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrUnmarshalTpl["struct"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) arrUnmarshalArr(st *ast.TypeSpec, ptr bool) { - if ptr { - err := arrUnmarshalTpl["arrPtr"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } else { - err := arrUnmarshalTpl["arr"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } -} diff --git a/gojay/gen_array_unmarshal_tpl.go b/gojay/gen_array_unmarshal_tpl.go @@ -1,70 +0,0 @@ -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", - }, - "float": &genTpl{ - strTpl: "\tvar i float{{.IntLen}}" + - "\n\tif err := dec.Float{{.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, "arrUnmarshal") -} diff --git a/gojay/gen_map.go b/gojay/gen_map.go @@ -1,19 +0,0 @@ -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 @@ -1,201 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "go/ast" - "log" -) - -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 fmt.Errorf("Unknown type %s", n) - } - } - _, 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": - g.mapMarshalString(ptr) - case "bool": - g.mapMarshalBool(ptr) - case "int": - g.mapMarshalInt("", ptr) - case "int64": - g.mapMarshalInt("64", ptr) - case "int32": - g.mapMarshalInt("32", ptr) - case "int16": - g.mapMarshalInt("16", ptr) - case "int8": - g.mapMarshalInt("8", ptr) - case "uint64": - g.mapMarshalUint("64", ptr) - case "uint32": - g.mapMarshalUint("32", ptr) - case "uint16": - g.mapMarshalUint("16", ptr) - case "uint8": - g.mapMarshalUint("8", ptr) - case "float64": - g.mapMarshalFloat("64", ptr) - case "float32": - g.mapMarshalFloat("32", ptr) - default: - // if ident is already in our spec list - if sp, ok := g.genTypes[i.Name]; ok { - g.mapMarshalNonPrim(sp, ptr) - } else if i.Obj != nil { - switch t := i.Obj.Decl.(type) { - case *ast.TypeSpec: - g.mapMarshalNonPrim(t, ptr) - default: - return errors.New("could not determine what to do with type " + i.String()) - } - } else { - return fmt.Errorf("Unknown type %s", i.Name) - } - } - return nil -} - -func (g *Gen) mapMarshalNonPrim(sp *ast.TypeSpec, ptr bool) { - switch sp.Type.(type) { - case *ast.StructType: - g.mapMarshalStruct(sp, ptr) - case *ast.ArrayType: - g.mapMarshalArr(sp, ptr) - } -} - -func (g *Gen) mapMarshalString(ptr bool) { - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := mapMarshalTpl["string"].tpl.Execute(g.b, struct { - Ptr string - }{ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) mapMarshalBool(ptr bool) { - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := mapMarshalTpl["bool"].tpl.Execute(g.b, struct { - Ptr string - }{ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) mapMarshalInt(intLen string, ptr bool) { - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := mapMarshalTpl["int"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) mapMarshalUint(intLen string, ptr bool) { - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := mapMarshalTpl["uint"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) mapMarshalFloat(intLen string, ptr bool) { - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := mapMarshalTpl["float"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) mapMarshalStruct(st *ast.TypeSpec, ptr bool) { - ptrStr := "" - if ptr { - ptrStr = "*" - } - var err = mapMarshalTpl["struct"].tpl.Execute(g.b, struct { - Ptr string - }{ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) mapMarshalArr(st *ast.TypeSpec, ptr bool) { - ptrStr := "" - if ptr { - ptrStr = "*" - } - var err = mapMarshalTpl["arr"].tpl.Execute(g.b, struct { - Ptr string - }{ptrStr}) - if err != nil { - log.Fatal(err) - } -} diff --git a/gojay/gen_map_marshal_tpl.go b/gojay/gen_map_marshal_tpl.go @@ -1,60 +0,0 @@ -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, {{.Ptr}}s) - } -`, - }, - "int": &genTpl{ - strTpl: ` for k, s := range v { - enc.Int{{.IntLen}}Key(k, {{.Ptr}}s) - } -`, - }, - "uint": &genTpl{ - strTpl: ` for k, s := range v { - enc.Uint{{.IntLen}}Key(k, {{.Ptr}}s) - } -`, - }, - "float": &genTpl{ - strTpl: ` for k, s := range v { - enc.Float{{.IntLen}}Key(k, {{.Ptr}}s) - } -`, - }, - "bool": &genTpl{ - strTpl: ` for k, s := range v { - enc.BoolKey(k, {{.Ptr}}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 @@ -1,616 +0,0 @@ -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 } -`, - }, - "basicMapStringStringPtr": { - 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 } -`, - }, - "basicMapStringIntPtr": { - 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 } -`, - }, - "basicMapStringInt64": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]int64 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i int64 - if err := dec.Int64(&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.Int64Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringInt64Ptr": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]*int64 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i int64 - if err := dec.Int64(&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.Int64Key(k, *s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringInt32": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]int32 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i int32 - if err := dec.Int32(&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.Int32Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringInt16": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]int16 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i int16 - if err := dec.Int16(&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.Int16Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringInt8": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]int8 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i int8 - if err := dec.Int8(&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.Int8Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringUint64": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]uint64 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i uint64 - if err := dec.Uint64(&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.Uint64Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringUint64Ptr": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]*uint64 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i uint64 - if err := dec.Uint64(&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.Uint64Key(k, *s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringUint32": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]uint32 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i uint32 - if err := dec.Uint32(&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.Uint32Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringUint16": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]uint16 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i uint16 - if err := dec.Uint16(&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.Uint16Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringUint8": { - input: strings.NewReader(`package test - -//gojay:json -type IntMap map[string]uint8 -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var i uint8 - if err := dec.Uint8(&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.Uint8Key(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringBool": { - input: strings.NewReader(`package test - -//gojay:json -type BoolMap map[string]bool -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v BoolMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var b bool - if err := dec.Bool(&b); err != nil { - return err - } - v[k] = b - return nil -} - -// NKeys returns the number of keys to unmarshal -func (v BoolMap) NKeys() int { return 0 } - -// MarshalJSONObject implements gojay's MarshalerJSONObject -func (v BoolMap) MarshalJSONObject(enc *gojay.Encoder) { - for k, s := range v { - enc.BoolKey(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v BoolMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringBoolPtr": { - input: strings.NewReader(`package test - -//gojay:json -type BoolMap map[string]*bool -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v BoolMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var b bool - if err := dec.Bool(&b); err != nil { - return err - } - v[k] = &b - return nil -} - -// NKeys returns the number of keys to unmarshal -func (v BoolMap) NKeys() int { return 0 } - -// MarshalJSONObject implements gojay's MarshalerJSONObject -func (v BoolMap) MarshalJSONObject(enc *gojay.Encoder) { - for k, s := range v { - enc.BoolKey(k, *s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v BoolMap) IsNil() bool { return v == nil || len(v) == 0 } -`, - }, - "basicMapStringStruct": { - input: strings.NewReader(`package test - -//gojay:json -type BoolMap map[string]*Test - -type Test struct{} -`), - expectedResult: `package - -import "github.com/francoispqt/gojay" - -// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject -func (v BoolMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { - var s = &Test{} - if err := dec.Object(s); err != nil { - return err - } - v[k] = s - return nil -} - -// NKeys returns the number of keys to unmarshal -func (v BoolMap) NKeys() int { return 0 } - -// MarshalJSONObject implements gojay's MarshalerJSONObject -func (v BoolMap) MarshalJSONObject(enc *gojay.Encoder) { - for k, s := range v { - enc.ObjectKey(k, s) - } -} - -// IsNil returns wether the structure is nil value or not -func (v BoolMap) 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, - string(genHeader)+testCase.expectedResult, - g.b.String(), - ) - }) - } -} diff --git a/gojay/gen_map_unmarshal.go b/gojay/gen_map_unmarshal.go @@ -1,242 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "go/ast" - "log" -) - -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 fmt.Errorf("Unknown type %s", n) - } - } - _, 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": - g.mapUnmarshalString(ptr) - case "bool": - g.mapUnmarshalBool(ptr) - case "int": - g.mapUnmarshalInt("", ptr) - case "int64": - g.mapUnmarshalInt("64", ptr) - case "int32": - g.mapUnmarshalInt("32", ptr) - case "int16": - g.mapUnmarshalInt("16", ptr) - case "int8": - g.mapUnmarshalInt("8", ptr) - case "uint64": - g.mapUnmarshalUint("64", ptr) - case "uint32": - g.mapUnmarshalUint("32", ptr) - case "uint16": - g.mapUnmarshalUint("16", ptr) - case "uint8": - g.mapUnmarshalUint("8", ptr) - case "float64": - g.mapUnmarshalFloat("64", ptr) - case "float32": - g.mapUnmarshalFloat("32", ptr) - 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 fmt.Errorf("Unknown type %s", i.Name) - } - } - return nil -} - -func (g *Gen) mapUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { - switch sp.Type.(type) { - case *ast.StructType: - g.mapUnmarshalStruct(sp, ptr) - return nil - case *ast.ArrayType: - g.mapUnmarshalArr(sp, ptr) - return nil - } - return errors.New("Unknown type") -} - -func (g *Gen) mapUnmarshalString(ptr bool) { - if ptr { - err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct { - Ptr string - }{"&"}) - if err != nil { - log.Fatal(err) - } - } else { - err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct { - Ptr string - }{""}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) mapUnmarshalBool(ptr bool) { - if ptr { - err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct { - Ptr string - }{"&"}) - if err != nil { - log.Fatal(err) - } - } else { - err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct { - Ptr string - }{""}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) mapUnmarshalInt(intLen string, ptr bool) { - if ptr { - err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } else { - err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) mapUnmarshalUint(intLen string, ptr bool) { - if ptr { - err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } else { - err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) mapUnmarshalFloat(intLen string, ptr bool) { - if ptr { - err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } else { - err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct { - IntLen string - Ptr string - }{intLen, ""}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) mapUnmarshalStruct(st *ast.TypeSpec, ptr bool) { - if ptr { - err := mapUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } else { - err := mapUnmarshalTpl["struct"].tpl.Execute(g.b, struct { - StructName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) mapUnmarshalArr(st *ast.TypeSpec, ptr bool) { - err := mapUnmarshalTpl["arr"].tpl.Execute(g.b, struct { - TypeName string - }{st.Name.String()}) - if err != nil { - log.Fatal(err) - } -} diff --git a/gojay/gen_map_unmarshal_tpl.go b/gojay/gen_map_unmarshal_tpl.go @@ -1,82 +0,0 @@ -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] = {{.Ptr}}str -`, - }, - "int": &genTpl{ - strTpl: ` var i int{{.IntLen}} - if err := dec.Int{{.IntLen}}(&i); err != nil { - return err - } - v[k] = {{.Ptr}}i -`, - }, - "uint": &genTpl{ - strTpl: ` var i uint{{.IntLen}} - if err := dec.Uint{{.IntLen}}(&i); err != nil { - return err - } - v[k] = {{.Ptr}}i -`, - }, - "float": &genTpl{ - strTpl: ` var i float{{.IntLen}} - if err := dec.Float{{.IntLen}}(&i); err != nil { - return err - } - v[k] = {{.Ptr}}i -`, - }, - "bool": &genTpl{ - strTpl: ` var b bool - if err := dec.Bool(&b); err != nil { - return err - } - v[k] = {{.Ptr}}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") -} diff --git a/gojay/gen_parse.go b/gojay/gen_parse.go @@ -1,63 +0,0 @@ -//+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 @@ -1,290 +0,0 @@ -package main - -import ( - "fmt" - "go/ast" - "log" -) - -func (g *Gen) structGenIsNil(n string) error { - err := structMarshalTpl["isNil"].tpl.Execute(g.b, struct { - StructName string - }{ - StructName: n, - }) - return err -} - -func (g *Gen) structGenMarshalObj(n string, s *ast.StructType) (int, error) { - err := structMarshalTpl["def"].tpl.Execute(g.b, struct { - StructName string - }{ - StructName: n, - }) - if err != nil { - return 0, err - } - keys := 0 - if len(s.Fields.List) > 0 { - // TODO: check tags - for _, field := range s.Fields.List { - // check if has hide tag - var omitEmpty string - if field.Tag != nil { - if hasTagMarshalHide(field.Tag) { - continue - } - if hasTagOmitEmpty(field.Tag) { - omitEmpty = omitEmptyFuncName - } - } - switch t := field.Type.(type) { - case *ast.Ident: - var err error - keys, err = g.structGenMarshalIdent(field, t, keys, omitEmpty, false) - if err != nil { - return 0, err - } - case *ast.StarExpr: - switch ptrExp := t.X.(type) { - case *ast.Ident: - var err error - keys, err = g.structGenMarshalIdent(field, ptrExp, keys, omitEmpty, true) - if err != nil { - return 0, err - } - default: - return 0, fmt.Errorf("Unknown type %s", 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, omitEmpty string, ptr bool) (int, error) { - var keyV = getStructFieldJSONKey(field) - - switch i.String() { - case "string": - g.structMarshalString(field, keyV, omitEmpty, ptr) - keys++ - case "bool": - g.structMarshalBool(field, keyV, omitEmpty, ptr) - keys++ - case "int": - g.structMarshalInt(field, keyV, "", omitEmpty, ptr) - keys++ - case "int64": - g.structMarshalInt(field, keyV, "64", omitEmpty, ptr) - keys++ - case "int32": - g.structMarshalInt(field, keyV, "32", omitEmpty, ptr) - keys++ - case "int16": - g.structMarshalInt(field, keyV, "16", omitEmpty, ptr) - keys++ - case "int8": - g.structMarshalInt(field, keyV, "8", omitEmpty, ptr) - keys++ - case "uint64": - g.structMarshalUint(field, keyV, "64", omitEmpty, ptr) - keys++ - case "uint32": - g.structMarshalUint(field, keyV, "32", omitEmpty, ptr) - keys++ - case "uint16": - g.structMarshalUint(field, keyV, "16", omitEmpty, ptr) - keys++ - case "uint8": - g.structMarshalUint(field, keyV, "8", omitEmpty, ptr) - keys++ - case "float64": - g.structMarshalFloat(field, keyV, "64", omitEmpty, ptr) - keys++ - case "float32": - g.structMarshalFloat(field, keyV, "32", omitEmpty, ptr) - keys++ - default: - // if ident is already in our spec list - if sp, ok := g.genTypes[i.Name]; ok { - err := g.structMarshalNonPrim(field, keyV, sp, omitEmpty, ptr) - if err != nil { - return 0, err - } - keys++ - } else if i.Obj != nil { - switch t := i.Obj.Decl.(type) { - case *ast.TypeSpec: - var err = g.structMarshalNonPrim(field, keyV, t, omitEmpty, ptr) - if err != nil { - return 0, err - } - keys++ - default: - g.structMarshalAny(field, keyV, sp, ptr) - keys++ - } - } else { - g.structMarshalAny(field, keyV, sp, ptr) - keys++ - } - } - return keys, nil -} - -func (g *Gen) structMarshalNonPrim(field *ast.Field, keyV string, sp *ast.TypeSpec, omitEmpty string, ptr bool) error { - switch sp.Type.(type) { - case *ast.StructType: - g.structMarshalStruct(field, keyV, sp, omitEmpty, ptr) - return nil - case *ast.ArrayType: - g.structMarshalArr(field, keyV, sp, omitEmpty, ptr) - return nil - default: - g.structMarshalAny(field, keyV, sp, ptr) - } - return nil -} - -func (g *Gen) structMarshalString(field *ast.Field, keyV string, omitEmpty string, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := structMarshalTpl["string"].tpl.Execute(g.b, struct { - Field string - Key string - OmitEmpty string - Ptr string - }{key, keyV, omitEmpty, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) structMarshalBool(field *ast.Field, keyV string, omitEmpty string, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := structMarshalTpl["bool"].tpl.Execute(g.b, struct { - Field string - Key string - OmitEmpty string - Ptr string - }{key, keyV, omitEmpty, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) structMarshalInt(field *ast.Field, keyV string, intLen string, omitEmpty string, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := structMarshalTpl["int"].tpl.Execute(g.b, struct { - Field string - IntLen string - Key string - OmitEmpty string - Ptr string - }{key, intLen, keyV, omitEmpty, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) structMarshalUint(field *ast.Field, keyV string, intLen string, omitEmpty string, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := structMarshalTpl["uint"].tpl.Execute(g.b, struct { - Field string - IntLen string - Key string - OmitEmpty string - Ptr string - }{key, intLen, keyV, omitEmpty, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) structMarshalFloat(field *ast.Field, keyV string, intLen string, omitEmpty string, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - err := structMarshalTpl["float"].tpl.Execute(g.b, struct { - Field string - IntLen string - Key string - OmitEmpty string - Ptr string - }{key, intLen, keyV, omitEmpty, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) structMarshalStruct(field *ast.Field, keyV string, st *ast.TypeSpec, omitEmpty string, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - var err = structMarshalTpl["struct"].tpl.Execute(g.b, struct { - Key string - Field string - OmitEmpty string - Ptr string - }{keyV, key, omitEmpty, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) structMarshalArr(field *ast.Field, keyV string, st *ast.TypeSpec, omitEmpty string, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - var err = structMarshalTpl["arr"].tpl.Execute(g.b, struct { - Key string - Field string - OmitEmpty string - Ptr string - }{keyV, key, omitEmpty, ptrStr}) - if err != nil { - log.Fatal(err) - } -} - -func (g *Gen) structMarshalAny(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) { - key := field.Names[0].String() - ptrStr := "" - if ptr { - ptrStr = "*" - } - var err = structMarshalTpl["any"].tpl.Execute(g.b, struct { - Key string - Field string - Ptr string - }{keyV, key, ptrStr}) - if err != nil { - log.Fatal(err) - } -} diff --git a/gojay/gen_struct_marshal_tpl.go b/gojay/gen_struct_marshal_tpl.go @@ -1,42 +0,0 @@ -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{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", - }, - "int": &genTpl{ - strTpl: "\tenc.Int{{.IntLen}}Key{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", - }, - "uint": &genTpl{ - strTpl: "\tenc.Uint{{.IntLen}}Key{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", - }, - "float": &genTpl{ - strTpl: "\tenc.Float{{.IntLen}}Key{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", - }, - "bool": &genTpl{ - strTpl: "\tenc.BoolKey{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n", - }, - "struct": &genTpl{ - strTpl: "\tenc.ObjectKey{{.OmitEmpty}}(\"{{.Key}}\", v.{{.Field}})\n", - }, - "arr": &genTpl{ - strTpl: "\tenc.ArrayKey{{.OmitEmpty}}(\"{{.Key}}\", v.{{.Field}})\n", - }, - "any": &genTpl{ - strTpl: "\tenc.AnyKey(\"{{.Key}}\", v.{{.Field}})\n", - }, -} - -func init() { - parseTemplates(structMarshalTpl, "structMarshal") -} diff --git a/gojay/gen_struct_test.go b/gojay/gen_struct_test.go @@ -1,433 +0,0 @@ -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 - Uint8 uint8 - Uint16 uint16 - Uint32 uint32 - Uint64 uint64 - Float64 float64 - Float32 float32 - Str string - Bool bool - Unknown UnknownType -} - `), - 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 "uint8": - return dec.Uint8(&v.Uint8) - case "uint16": - return dec.Uint16(&v.Uint16) - case "uint32": - return dec.Uint32(&v.Uint32) - case "uint64": - return dec.Uint64(&v.Uint64) - case "float64": - return dec.Float64(&v.Float64) - case "float32": - return dec.Float32(&v.Float32) - case "str": - return dec.String(&v.Str) - case "bool": - return dec.Bool(&v.Bool) - case "unknown": - return dec.Any(&v.Unknown) - } - return nil -} - -// NKeys returns the number of keys to unmarshal -func (v *Struct) NKeys() int { return 14 } - -// 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.Uint8Key("uint8", v.Uint8) - enc.Uint16Key("uint16", v.Uint16) - enc.Uint32Key("uint32", v.Uint32) - enc.Uint64Key("uint64", v.Uint64) - enc.Float64Key("float64", v.Float64) - enc.Float32Key("float32", v.Float32) - enc.StringKey("str", v.Str) - enc.BoolKey("bool", v.Bool) - enc.AnyKey("unknown", v.Unknown) -} - -// 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 } -`, - }, - "complexStructStructPtrTagOmitEmpty": { - input: strings.NewReader(`package test - -//gojay:json -type Struct struct{ - Struct *Struct ` + "`gojay:\"someStruct,omitempty\"`" + ` -} - `), - 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.ObjectKeyOmitEmpty("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, - string(genHeader)+testCase.expectedResult, - g.b.String(), - ) - }) - } -} diff --git a/gojay/gen_struct_unmarshal.go b/gojay/gen_struct_unmarshal.go @@ -1,378 +0,0 @@ -package main - -import ( - "fmt" - "go/ast" - "log" -) - -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.b, struct { - NKeys int - StructName string - }{ - NKeys: count, - StructName: n, - }) - return err -} - -func (g *Gen) structGenUnmarshalObj(n string, s *ast.StructType) (int, error) { - err := structUnmarshalTpl["def"].tpl.Execute(g.b, struct { - StructName string - }{ - StructName: n, - }) - if err != nil { - return 0, err - } - keys := 0 - if len(s.Fields.List) > 0 { - // open switch statement - 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 - keys, err = g.structGenUnmarshalIdent(field, t, keys, false) - if err != nil { - return 0, err - } - case *ast.StarExpr: - switch ptrExp := t.X.(type) { - case *ast.Ident: - var err error - keys, err = g.structGenUnmarshalIdent(field, ptrExp, keys, true) - if err != nil { - return 0, err - } - default: - return 0, fmt.Errorf("Unknown type %s", n) - } - } - } - // close switch statement - g.b.Write([]byte("\t}\n")) - } - _, 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) { - var keyV = getStructFieldJSONKey(field) - - switch i.String() { - case "string": - g.structUnmarshalString(field, keyV, ptr) - keys++ - case "bool": - g.structUnmarshalBool(field, keyV, ptr) - keys++ - case "int": - g.structUnmarshalInt(field, keyV, "", ptr) - keys++ - case "int64": - g.structUnmarshalInt(field, keyV, "64", ptr) - keys++ - case "int32": - g.structUnmarshalInt(field, keyV, "32", ptr) - keys++ - case "int16": - g.structUnmarshalInt(field, keyV, "16", ptr) - keys++ - case "int8": - g.structUnmarshalInt(field, keyV, "8", ptr) - keys++ - case "uint64": - g.structUnmarshalUint(field, keyV, "64", ptr) - keys++ - case "uint32": - g.structUnmarshalUint(field, keyV, "32", ptr) - keys++ - case "uint16": - g.structUnmarshalUint(field, keyV, "16", ptr) - keys++ - case "uint8": - g.structUnmarshalUint(field, keyV, "8", ptr) - keys++ - case "float64": - g.structUnmarshalFloat(field, keyV, "64", ptr) - keys++ - case "float32": - g.structUnmarshalFloat(field, keyV, "32", ptr) - keys++ - default: - // if ident is already in our spec list - if sp, ok := g.genTypes[i.Name]; ok { - err := g.structUnmarshalNonPrim(field, keyV, sp, ptr) - if err != nil { - return 0, err - } - keys++ - } else if i.Obj != nil { - // else check the obj infos - switch t := i.Obj.Decl.(type) { - case *ast.TypeSpec: - err := g.structUnmarshalNonPrim(field, keyV, t, ptr) - if err != nil { - return 0, err - } - keys++ - default: - g.structUnmarshalAny(field, keyV, sp, ptr) - keys++ - } - } else { - g.structUnmarshalAny(field, keyV, sp, ptr) - keys++ - } - } - return keys, nil -} - -func (g *Gen) structUnmarshalNonPrim(field *ast.Field, keyV string, sp *ast.TypeSpec, ptr bool) error { - switch sp.Type.(type) { - case *ast.StructType: - g.structUnmarshalStruct(field, keyV, sp, ptr) - return nil - case *ast.ArrayType: - g.structUnmarshalArr(field, keyV, sp, ptr) - return nil - default: - g.structUnmarshalAny(field, keyV, sp, ptr) - return nil - } -} - -func (g *Gen) structUnmarshalString(field *ast.Field, keyV string, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["string"].tpl.Execute(g.b, struct { - Field string - Ptr string - }{key, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["string"].tpl.Execute(g.b, struct { - Field string - Ptr string - }{key, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) structUnmarshalBool(field *ast.Field, keyV string, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["bool"].tpl.Execute(g.b, struct { - Field string - Ptr string - }{key, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["bool"].tpl.Execute(g.b, struct { - Field string - Ptr string - }{key, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) structUnmarshalInt(field *ast.Field, keyV string, intLen string, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["int"].tpl.Execute(g.b, struct { - Field string - IntLen string - Ptr string - }{key, intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["int"].tpl.Execute(g.b, struct { - Field string - IntLen string - Ptr string - }{key, intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) structUnmarshalUint(field *ast.Field, keyV string, intLen string, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["uint"].tpl.Execute(g.b, struct { - Field string - IntLen string - Ptr string - }{key, intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["uint"].tpl.Execute(g.b, struct { - Field string - IntLen string - Ptr string - }{key, intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) structUnmarshalFloat(field *ast.Field, keyV string, intLen string, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["float"].tpl.Execute(g.b, struct { - Field string - IntLen string - Ptr string - }{key, intLen, ""}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["float"].tpl.Execute(g.b, struct { - Field string - IntLen string - Ptr string - }{key, intLen, "&"}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) structUnmarshalStruct(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct { - Field string - StructName string - }{key, st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["struct"].tpl.Execute(g.b, struct { - Field string - StructName string - }{key, st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) structUnmarshalArr(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["arrPtr"].tpl.Execute(g.b, struct { - Field string - TypeName string - }{key, st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["arr"].tpl.Execute(g.b, struct { - Field string - TypeName string - }{key, st.Name.String()}) - if err != nil { - log.Fatal(err) - } - } -} - -func (g *Gen) structUnmarshalAny(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) { - key := field.Names[0].String() - err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct { - Key string - }{keyV}) - if err != nil { - log.Fatal(err) - } - if ptr { - err = structUnmarshalTpl["anyPtr"].tpl.Execute(g.b, struct { - Field string - }{key}) - if err != nil { - log.Fatal(err) - } - } else { - err = structUnmarshalTpl["any"].tpl.Execute(g.b, struct { - Field string - }{key}) - if err != nil { - log.Fatal(err) - } - } -} diff --git a/gojay/gen_struct_unmarshal_tpl.go b/gojay/gen_struct_unmarshal_tpl.go @@ -1,72 +0,0 @@ -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", - }, - "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}}{} - } - return dec.Object(v.{{.Field}}) -`, - }, - "arr": &genTpl{ - strTpl: ` if v.{{.Field}} == nil { - arr := make({{.TypeName}}, 0) - v.{{.Field}} = arr - } - return dec.Array(&v.{{.Field}}) -`, - }, - "arrPtr": &genTpl{ - strTpl: ` if v.{{.Field}} == nil { - arr := make({{.TypeName}}, 0) - v.{{.Field}} = &arr - } - return dec.Array(v.{{.Field}}) -`, - }, - "any": &genTpl{ - strTpl: "\t\treturn dec.Any(&v.{{.Field}})\n", - }, - "anyPtr": &genTpl{ - strTpl: "\t\treturn dec.Any(v.{{.Field}})\n", - }, -} - -func init() { - parseTemplates(structUnmarshalTpl, "structUnmarshal") -} diff --git a/gojay/gen_stuct.go b/gojay/gen_stuct.go @@ -1,30 +0,0 @@ -package main - -import ( - "go/ast" - "strings" -) - -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 - } - err = g.structGenNKeys(n, keys) - keys, err = g.structGenMarshalObj(n, s) - if err != nil { - return err - } - return g.structGenIsNil(n) -} diff --git a/gojay/gen_tag.go b/gojay/gen_tag.go @@ -1,65 +0,0 @@ -package main - -import ( - "go/ast" - "log" - - "github.com/fatih/structtag" -) - -const gojayTag = "gojay" -const hideTag = "-" -const unmarshalHideTag = "-u" -const marshalHideTag = "-m" -const omitEmptyTag = "omitempty" - -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 hasTagOmitEmpty(tags *ast.BasicLit) bool { - v, err := getGojayTagValue(tags) - if err != nil { - log.Print(err) - return false - } - return v.Name == omitEmptyTag || v.HasOption(omitEmptyTag) -} - -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 @@ -1,24 +0,0 @@ -package main - -import ( - "go/ast" - "go/parser" - "go/token" - "io" -) - -func MakeGenFromReader(input io.Reader) (*Gen, error) { - g := NewGen("", []string{}) - 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/gojay.go b/gojay/gojay.go @@ -0,0 +1,23 @@ +package main + +import ( + "flag" + "github.com/francoispqt/gojay/gojay/codegen" + "log" +) + +var pkg = flag.String("pkg", "", "the package name of the generated file") +var dst = flag.String("o", "", "destination file to output generated code") +var src = flag.String("s", "", "source dir or file (absolute or relative path)") +var types = flag.String("t", "", "types to generate") +var annotation = flag.String("a", "json", "annotation tag (default json)") +var poolObjects = flag.String("p", "", "generate code to reuse objects using sync.Pool") + +func main() { + flag.Parse() + options := codegen.NewOptionsWithFlagSet(flag.CommandLine) + gen := codegen.NewGenerator(options) + if err := gen.Generate(); err != nil { + log.Fatal(err) + } +} diff --git a/gojay/main.go b/gojay/main.go @@ -1,121 +0,0 @@ -//+build !test - -package main - -import ( - "errors" - "flag" - "io/ioutil" - "log" - "os" - "path/filepath" - "strings" -) - -var dst = flag.String("o", "", "destination file to output generated implementations") -var src = flag.String("s", "", "source dir or file (absolute or relative path)") -var pkg = flag.String("p", "", "go package") -var types = flag.String("t", "", "types to generate") - -var ErrNoPathProvided = errors.New("You must provide a path or a package name") - -type stringWriter interface { - WriteString(string) (int, error) -} - -func hasAnnotation(fP string) bool { - b, err := ioutil.ReadFile(fP) - if err != nil { - log.Fatal(err) - } - return strings.Contains(string(b), gojayAnnotation) -} - -func resolvePath(p string) (string, error) { - if fullPath, err := filepath.Abs(p); err != nil { - return "", err - } else if _, err := os.Stat(fullPath); err != nil { - return "", err - } else { - return fullPath, nil - } -} - -// getPath returns either the path given as argument or current working directory -func getPath() (string, error) { - // if pkg is set, resolve pkg path - if *pkg != "" { - return resolvePath(os.Getenv("GOPATH") + "/src/" + *pkg) - } else if *src != "" { // if src is present parse from src - return resolvePath(*src) - } else if len(os.Args) > 1 { // else if there is a command line arg, use it as path to a package $GOPATH/src/os.Args[1] - return resolvePath(os.Getenv("GOPATH") + "/src/" + os.Args[1]) - } - return "", ErrNoPathProvided -} - -// getTypes returns the types to be parsed -func getTypes() (t []string) { - if *types != "" { // if src is present parse from src - return strings.Split(*types, ",") - } else if *src == "." && *dst == "" && len(os.Args) > 2 { // else if there is a command line arg, use it as path to a package $GOPATH/src/os.Args[1] - return strings.Split(os.Args[2], ",") - } - return t -} - -// getOutput returns the output -func getOutput() (stringWriter, error) { - if *dst != "" { - p, err := filepath.Abs(*dst) - if err != nil { - return nil, err - } - return os.OpenFile(p, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) - } else if len(os.Args) > 3 && *src == "" && *types == "" { - p, err := filepath.Abs(os.Args[3]) - if err != nil { - return nil, err - } - return os.OpenFile(p, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) - } - return os.Stdout, nil -} - -func parseArgs() (p string, t []string, err error) { - flag.Parse() - p, err = getPath() - if err != nil { - return p, t, err - } - t = getTypes() - return p, t, err -} - -func main() { - p, t, err := parseArgs() - if err != nil { - log.Fatal(err) - } - // parse source files - g := NewGen(p, t) - err = g.parse() - if err != nil { - log.Fatal(err) - return - } - // generate output - err = g.Gen() - if err != nil { - log.Fatal(err) - return - } - var o stringWriter - o, err = getOutput() - if err != nil { - log.Fatal(err) - return - } - // write content to output - o.WriteString(g.b.String()) -} diff --git a/gojay/tests/maps.go b/gojay/tests/maps.go @@ -1,4 +0,0 @@ -package tests - -//gojay:json -type MapStringInt map[string]int diff --git a/gojay/tests/slices.go b/gojay/tests/slices.go @@ -1,16 +0,0 @@ -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/structs.go b/gojay/tests/structs.go @@ -1,44 +0,0 @@ -package tests - -//gojay:json -type A struct { - Str string `gojay:"string"` - Bool bool - Int int - Int64 int64 - Int32 int32 - Int16 int16 - Int8 int8 - Uint64 uint64 - Uint32 uint32 - 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 - 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/vendor/github.com/fatih/structtag/.travis.yml b/gojay/vendor/github.com/fatih/structtag/.travis.yml @@ -1,4 +0,0 @@ -language: go -go: - - 1.7.x - - tip diff --git a/gojay/vendor/github.com/fatih/structtag/LICENSE b/gojay/vendor/github.com/fatih/structtag/LICENSE @@ -1,60 +0,0 @@ -Copyright (c) 2017, Fatih Arslan -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of structtag nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -This software includes some portions from Go. Go is used under the terms of the -BSD like license. - -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The Go gopher was designed by Renee French. http://reneefrench.blogspot.com/ The design is licensed under the Creative Commons 3.0 Attributions license. Read this article for more details: https://blog.golang.org/gopher diff --git a/gojay/vendor/github.com/fatih/structtag/README.md b/gojay/vendor/github.com/fatih/structtag/README.md @@ -1,73 +0,0 @@ -# structtag [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/structtag) [![Build Status](https://travis-ci.org/fatih/structtag.svg?branch=master)](https://travis-ci.org/fatih/structtag) - -structtag provides an easy way of parsing and manipulating struct tag fields. -Please vendor the library as it might change in future versions. - -# Install - -```bash -go get github.com/fatih/structtag -``` - -# Example - -```go -package main - -import ( - "fmt" - "reflect" - "sort" - - "github.com/fatih/structtag" -) - -func main() { - type t struct { - t string `json:"foo,omitempty,string" xml:"foo"` - } - - // get field tag - tag := reflect.TypeOf(t{}).Field(0).Tag - - // ... and start using structtag by parsing the tag - tags, err := structtag.Parse(string(tag)) - if err != nil { - panic(err) - } - - // iterate over all tags - for _, t := range tags.Tags() { - fmt.Printf("tag: %+v\n", t) - } - - // get a single tag - jsonTag, err := tags.Get("json") - if err != nil { - panic(err) - } - fmt.Println(jsonTag) // Output: json:"foo,omitempty,string" - fmt.Println(jsonTag.Key) // Output: json - fmt.Println(jsonTag.Name) // Output: foo - fmt.Println(jsonTag.Options) // Output: [omitempty string] - - // change existing tag - jsonTag.Name = "foo_bar" - jsonTag.Options = nil - tags.Set(jsonTag) - - // add new tag - tags.Set(&structtag.Tag{ - Key: "hcl", - Name: "foo", - Options: []string{"squash"}, - }) - - // print the tags - fmt.Println(tags) // Output: json:"foo_bar" xml:"foo" hcl:"foo,squash" - - // sort tags according to keys - sort.Sort(tags) - fmt.Println(tags) // Output: hcl:"foo,squash" json:"foo_bar" xml:"foo" -} -``` diff --git a/gojay/vendor/github.com/fatih/structtag/tags.go b/gojay/vendor/github.com/fatih/structtag/tags.go @@ -1,303 +0,0 @@ -package structtag - -import ( - "bytes" - "errors" - "fmt" - "strconv" - "strings" -) - -var ( - errTagSyntax = errors.New("bad syntax for struct tag pair") - errTagKeySyntax = errors.New("bad syntax for struct tag key") - errTagValueSyntax = errors.New("bad syntax for struct tag value") - - errKeyNotSet = errors.New("tag key does not exist") - errTagNotExist = errors.New("tag does not exist") - errTagKeyMismatch = errors.New("mismatch between key and tag.key") -) - -// Tags represent a set of tags from a single struct field -type Tags struct { - tags []*Tag -} - -// Tag defines a single struct's string literal tag -type Tag struct { - // Key is the tag key, such as json, xml, etc.. - // i.e: `json:"foo,omitempty". Here key is: "json" - Key string - - // Name is a part of the value - // i.e: `json:"foo,omitempty". Here name is: "foo" - Name string - - // Options is a part of the value. It contains a slice of tag options i.e: - // `json:"foo,omitempty". Here options is: ["omitempty"] - Options []string -} - -// Parse parses a single struct field tag and returns the set of tags. -func Parse(tag string) (*Tags, error) { - var tags []*Tag - - // NOTE(arslan) following code is from reflect and vet package with some - // modifications to collect all necessary information and extend it with - // usable methods - for tag != "" { - // Skip leading space. - i := 0 - for i < len(tag) && tag[i] == ' ' { - i++ - } - tag = tag[i:] - if tag == "" { - return nil, nil - } - - // Scan to colon. A space, a quote or a control character is a syntax - // error. Strictly speaking, control chars include the range [0x7f, - // 0x9f], not just [0x00, 0x1f], but in practice, we ignore the - // multi-byte control characters as it is simpler to inspect the tag's - // bytes than the tag's runes. - i = 0 - for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { - i++ - } - - if i == 0 { - return nil, errTagKeySyntax - } - if i+1 >= len(tag) || tag[i] != ':' { - return nil, errTagSyntax - } - if tag[i+1] != '"' { - return nil, errTagValueSyntax - } - - key := string(tag[:i]) - tag = tag[i+1:] - - // Scan quoted string to find value. - i = 1 - for i < len(tag) && tag[i] != '"' { - if tag[i] == '\\' { - i++ - } - i++ - } - if i >= len(tag) { - return nil, errTagValueSyntax - } - - qvalue := string(tag[:i+1]) - tag = tag[i+1:] - - value, err := strconv.Unquote(qvalue) - if err != nil { - return nil, errTagValueSyntax - } - - res := strings.Split(value, ",") - name := res[0] - options := res[1:] - if len(options) == 0 { - options = nil - } - - tags = append(tags, &Tag{ - Key: key, - Name: name, - Options: options, - }) - } - - return &Tags{ - tags: tags, - }, nil -} - -// Get returns the tag associated with the given key. If the key is present -// in the tag the value (which may be empty) is returned. Otherwise the -// returned value will be the empty string. The ok return value reports whether -// the tag exists or not (which the return value is nil). -func (t *Tags) Get(key string) (*Tag, error) { - for _, tag := range t.tags { - if tag.Key == key { - return tag, nil - } - } - - return nil, errTagNotExist -} - -// Set sets the given tag. If the tag key already exists it'll override it -func (t *Tags) Set(tag *Tag) error { - if tag.Key == "" { - return errKeyNotSet - } - - added := false - for i, tg := range t.tags { - if tg.Key == tag.Key { - added = true - t.tags[i] = tag - } - } - - if !added { - // this means this is a new tag, add it - t.tags = append(t.tags, tag) - } - - return nil -} - -// AddOptions adds the given option for the given key. If the option already -// exists it doesn't add it again. -func (t *Tags) AddOptions(key string, options ...string) { - for i, tag := range t.tags { - if tag.Key != key { - continue - } - - for _, opt := range options { - if !tag.HasOption(opt) { - tag.Options = append(tag.Options, opt) - } - } - - t.tags[i] = tag - } -} - -// DeleteOptions deletes the given options for the given key -func (t *Tags) DeleteOptions(key string, options ...string) { - hasOption := func(option string) bool { - for _, opt := range options { - if opt == option { - return true - } - } - return false - } - - for i, tag := range t.tags { - if tag.Key != key { - continue - } - - var updated []string - for _, opt := range tag.Options { - if !hasOption(opt) { - updated = append(updated, opt) - } - } - - tag.Options = updated - t.tags[i] = tag - } -} - -// Delete deletes the tag for the given keys -func (t *Tags) Delete(keys ...string) { - hasKey := func(key string) bool { - for _, k := range keys { - if k == key { - return true - } - } - return false - } - - var updated []*Tag - for _, tag := range t.tags { - if !hasKey(tag.Key) { - updated = append(updated, tag) - } - } - - t.tags = updated -} - -// Tags returns a slice of tags. The order is the original tag order unless it -// was changed. -func (t *Tags) Tags() []*Tag { - return t.tags -} - -// Tags returns a slice of tags. The order is the original tag order unless it -// was changed. -func (t *Tags) Keys() []string { - var keys []string - for _, tag := range t.tags { - keys = append(keys, tag.Key) - } - return keys -} - -// String reassembles the tags into a valid literal tag field representation -func (t *Tags) String() string { - tags := t.Tags() - if len(tags) == 0 { - return "" - } - - var buf bytes.Buffer - for i, tag := range t.Tags() { - buf.WriteString(tag.String()) - if i != len(tags)-1 { - buf.WriteString(" ") - } - } - return buf.String() -} - -// HasOption returns true if the given option is available in options -func (t *Tag) HasOption(opt string) bool { - for _, tagOpt := range t.Options { - if tagOpt == opt { - return true - } - } - - return false -} - -// String reassembles the tag into a valid tag field representation -func (t *Tag) String() string { - options := strings.Join(t.Options, ",") - if options != "" { - return fmt.Sprintf(`%s:"%s,%s"`, t.Key, t.Name, options) - } - return fmt.Sprintf(`%s:"%s"`, t.Key, t.Name) -} - -// GoString implements the fmt.GoStringer interface -func (t *Tag) GoString() string { - template := `{ - Key: '%s', - Name: '%s', - Option: '%s', - }` - - if t.Options == nil { - return fmt.Sprintf(template, t.Key, t.Name, "nil") - } - - options := strings.Join(t.Options, ",") - return fmt.Sprintf(template, t.Key, t.Name, options) -} - -func (t *Tags) Len() int { - return len(t.tags) -} - -func (t *Tags) Less(i int, j int) bool { - return t.tags[i].Key < t.tags[j].Key -} - -func (t *Tags) Swap(i int, j int) { - t.tags[i], t.tags[j] = t.tags[j], t.tags[i] -} diff --git a/gojay/vendor/github.com/fatih/structtag/tags_test.go b/gojay/vendor/github.com/fatih/structtag/tags_test.go @@ -1,390 +0,0 @@ -package structtag - -import ( - "reflect" - "sort" - "testing" -) - -func TestParse(t *testing.T) { - test := []struct { - name string - tag string - exp []*Tag - invalid bool - }{ - { - name: "empty tag", - tag: "", - }, - { - name: "tag with one key (invalid)", - tag: "json", - invalid: true, - }, - { - name: "tag with one key (valid)", - tag: `json:""`, - exp: []*Tag{ - { - Key: "json", - }, - }, - }, - { - name: "tag with one key and dash name", - tag: `json:"-"`, - exp: []*Tag{ - { - Key: "json", - Name: "-", - }, - }, - }, - { - name: "tag with key and name", - tag: `json:"foo"`, - exp: []*Tag{ - { - Key: "json", - Name: "foo", - }, - }, - }, - { - name: "tag with key, name and option", - tag: `json:"foo,omitempty"`, - exp: []*Tag{ - { - Key: "json", - Name: "foo", - Options: []string{"omitempty"}, - }, - }, - }, - { - name: "tag with multiple keys", - tag: `json:"" hcl:""`, - exp: []*Tag{ - { - Key: "json", - }, - { - Key: "hcl", - }, - }, - }, - { - name: "tag with multiple keys and names", - tag: `json:"foo" hcl:"foo"`, - exp: []*Tag{ - { - Key: "json", - Name: "foo", - }, - { - Key: "hcl", - Name: "foo", - }, - }, - }, - { - name: "tag with multiple keys and names", - tag: `json:"foo" hcl:"foo"`, - exp: []*Tag{ - { - Key: "json", - Name: "foo", - }, - { - Key: "hcl", - Name: "foo", - }, - }, - }, - { - name: "tag with multiple keys and different names", - tag: `json:"foo" hcl:"bar"`, - exp: []*Tag{ - { - Key: "json", - Name: "foo", - }, - { - Key: "hcl", - Name: "bar", - }, - }, - }, - { - name: "tag with multiple keys, different names and options", - tag: `json:"foo,omitempty" structs:"bar,omitnested"`, - exp: []*Tag{ - { - Key: "json", - Name: "foo", - Options: []string{"omitempty"}, - }, - { - Key: "structs", - Name: "bar", - Options: []string{"omitnested"}, - }, - }, - }, - { - name: "tag with multiple keys, different names and options", - tag: `json:"foo" structs:"bar,omitnested" hcl:"-"`, - exp: []*Tag{ - { - Key: "json", - Name: "foo", - }, - { - Key: "structs", - Name: "bar", - Options: []string{"omitnested"}, - }, - { - Key: "hcl", - Name: "-", - }, - }, - }, - } - - for _, ts := range test { - t.Run(ts.name, func(t *testing.T) { - tags, err := Parse(ts.tag) - invalid := err != nil - - if invalid != ts.invalid { - t.Errorf("invalid case\n\twant: %+v\n\tgot : %+v\n\terr : %s", ts.invalid, invalid, err) - } - - if invalid { - return - } - - got := tags.Tags() - - if !reflect.DeepEqual(ts.exp, got) { - t.Errorf("parse\n\twant: %#v\n\tgot : %#v", ts.exp, got) - } - }) - } -} - -func TestTags_Get(t *testing.T) { - tag := `json:"foo,omitempty" structs:"bar,omitnested"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - found, err := tags.Get("json") - if err != nil { - t.Fatal(err) - } - - want := `json:"foo,omitempty"` - - if found.String() != want { - t.Errorf("get\n\twant: %#v\n\tgot : %#v", want, found.String()) - } -} - -func TestTags_Set(t *testing.T) { - tag := `json:"foo,omitempty" structs:"bar,omitnested"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - err = tags.Set(&Tag{ - Key: "json", - Name: "bar", - Options: []string{}, - }) - if err != nil { - t.Fatal(err) - } - - found, err := tags.Get("json") - if err != nil { - t.Fatal(err) - } - - want := `json:"bar"` - if found.String() != want { - t.Errorf("set\n\twant: %#v\n\tgot : %#v", want, found.String()) - } -} - -func TestTags_Set_Append(t *testing.T) { - tag := `json:"foo,omitempty"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - err = tags.Set(&Tag{ - Key: "structs", - Name: "bar", - Options: []string{"omitnested"}, - }) - if err != nil { - t.Fatal(err) - } - - found, err := tags.Get("structs") - if err != nil { - t.Fatal(err) - } - - want := `structs:"bar,omitnested"` - if found.String() != want { - t.Errorf("set append\n\twant: %#v\n\tgot : %#v", want, found.String()) - } - - wantFull := `json:"foo,omitempty" structs:"bar,omitnested"` - if tags.String() != wantFull { - t.Errorf("set append\n\twant: %#v\n\tgot : %#v", wantFull, tags.String()) - } -} - -func TestTags_Set_KeyDoesNotExist(t *testing.T) { - tag := `json:"foo,omitempty" structs:"bar,omitnested"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - err = tags.Set(&Tag{ - Key: "", - Name: "bar", - Options: []string{}, - }) - if err == nil { - t.Fatal("setting tag with a nonexisting key should error") - } - - if err != errKeyNotSet { - t.Errorf("set\n\twant: %#v\n\tgot : %#v", errTagKeyMismatch, err) - } -} - -func TestTags_Delete(t *testing.T) { - tag := `json:"foo,omitempty" structs:"bar,omitnested" hcl:"-"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - tags.Delete("structs") - if tags.Len() != 2 { - t.Fatalf("tag length should be 2, have %d", tags.Len()) - } - - found, err := tags.Get("json") - if err != nil { - t.Fatal(err) - } - - want := `json:"foo,omitempty"` - if found.String() != want { - t.Errorf("delete\n\twant: %#v\n\tgot : %#v", want, found.String()) - } - - wantFull := `json:"foo,omitempty" hcl:"-"` - if tags.String() != wantFull { - t.Errorf("delete\n\twant: %#v\n\tgot : %#v", wantFull, tags.String()) - } -} - -func TestTags_DeleteOptions(t *testing.T) { - tag := `json:"foo,omitempty" structs:"bar,omitnested,omitempty" hcl:"-"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - tags.DeleteOptions("json", "omitempty") - - want := `json:"foo" structs:"bar,omitnested,omitempty" hcl:"-"` - if tags.String() != want { - t.Errorf("delete option\n\twant: %#v\n\tgot : %#v", want, tags.String()) - } - - tags.DeleteOptions("structs", "omitnested") - want = `json:"foo" structs:"bar,omitempty" hcl:"-"` - if tags.String() != want { - t.Errorf("delete option\n\twant: %#v\n\tgot : %#v", want, tags.String()) - } -} - -func TestTags_AddOption(t *testing.T) { - tag := `json:"foo" structs:"bar,omitempty" hcl:"-"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - tags.AddOptions("json", "omitempty") - - want := `json:"foo,omitempty" structs:"bar,omitempty" hcl:"-"` - if tags.String() != want { - t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String()) - } - - // this shouldn't change anything - tags.AddOptions("structs", "omitempty") - - want = `json:"foo,omitempty" structs:"bar,omitempty" hcl:"-"` - if tags.String() != want { - t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String()) - } - - // this should append to the existing - tags.AddOptions("structs", "omitnested", "flatten") - want = `json:"foo,omitempty" structs:"bar,omitempty,omitnested,flatten" hcl:"-"` - if tags.String() != want { - t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String()) - } -} - -func TestTags_String(t *testing.T) { - tag := `json:"foo" structs:"bar,omitnested" hcl:"-"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - if tags.String() != tag { - t.Errorf("string\n\twant: %#v\n\tgot : %#v", tag, tags.String()) - } -} - -func TestTags_Sort(t *testing.T) { - tag := `json:"foo" structs:"bar,omitnested" hcl:"-"` - - tags, err := Parse(tag) - if err != nil { - t.Fatal(err) - } - - sort.Sort(tags) - - want := `hcl:"-" json:"foo" structs:"bar,omitnested"` - if tags.String() != want { - t.Errorf("string\n\twant: %#v\n\tgot : %#v", want, tags.String()) - } -} diff --git a/gojay/visitor.go b/gojay/visitor.go @@ -1,54 +0,0 @@ -package main - -import ( - "go/ast" - "strings" -) - -func docContains(n *ast.CommentGroup, s string) bool { - for _, d := range n.List { - if strings.Contains(d.Text, s) { - return true - } - } - return false -} - -type vis struct { - pkg string - g *Gen - commentFound bool -} - -func (v *vis) Visit(n ast.Node) (w ast.Visitor) { - switch n := n.(type) { - case *ast.Package: - v.commentFound = false - return v - case *ast.File: - v.commentFound = false - return v - case *ast.GenDecl: - if len(v.g.types) == 0 && n.Doc != nil { - v.commentFound = docContains(n.Doc, gojayAnnotation) - } - return v - case *ast.TypeSpec: - if v.commentFound || v.g.isGenType(n.Name.Name) { - v.g.genTypes[n.Name.Name] = n - } - v.commentFound = false - return v - case *ast.StructType: - v.commentFound = false - return v - } - return v -} - -func newVisitor(g *Gen, pkgName string) *vis { - return &vis{ - g: g, - pkg: pkgName, - } -} diff --git a/gojaygen/gojaygen.go b/gojaygen/gojaygen.go @@ -1,22 +0,0 @@ -package main - -import ( - "flag" - "github.com/francoispqt/gojay/codegen" - "log" -) - -var dst = flag.String("o", "", "destination file to output generated implementations") -var src = flag.String("s", "", "source dir or file (absolute or relative path)") -var types = flag.String("t", "", "types to generate") -var annotation = flag.String("a", "json", "annotation tagg") -var poolObjects = flag.String("p", "", "generate code to reuse objects") - -func main() { - flag.Parse() - options := codegen.NewOptionsWithFlagSet(flag.CommandLine) - gen := &codegen.Generator{} - if err := gen.Generate(options); err != nil { - log.Fatal(err) - } -}