Skip to content

Commit

Permalink
(goctl): support nested struct (#4211)
Browse files Browse the repository at this point in the history
  • Loading branch information
kesonan committed Jun 25, 2024
1 parent 4ec9cac commit a012a91
Show file tree
Hide file tree
Showing 28 changed files with 317 additions and 75 deletions.
77 changes: 64 additions & 13 deletions tools/goctl/api/dartgen/gendata.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package dartgen

import (
"bytes"
"os"
"strings"
"text/template"

"github.com/zeromicro/go-zero/tools/goctl/api/spec"
)

const dataTemplate = `// --{{with .Info}}{{.Title}}{{end}}--
{{ range .Types}}
const dataTemplate = `// --{{with .APISpec.Info}}{{.Title}}{{end}}--
{{ range .APISpec.Types}}
class {{.Name}}{
{{range .Members}}
/// {{.Comment}}
Expand All @@ -28,12 +29,16 @@ class {{.Name}}{
'{{getPropertyFromMember .}}': {{if isDirectType .Type.Name}}{{lowCamelCase .Name}}{{else if isClassListType .Type.Name}}{{lowCamelCase .Name}}.map((i) => i.toJson()){{else}}{{lowCamelCase .Name}}.toJson(){{end}},{{end}}
};
}
{{ range $.InnerClassList}}
{{.}}
{{end}}
}
{{end}}
`

const dataTemplateV2 = `// --{{with .Info}}{{.Title}}{{end}}--
{{ range .Types}}
const dataTemplateV2 = `// --{{with .APISpec.Info}}{{.Title}}{{end}}--
{{ range .APISpec.Types}}
class {{.Name}} {
{{range .Members}}
{{if .Comment}}{{.Comment}}{{end}}
Expand Down Expand Up @@ -73,9 +78,18 @@ class {{.Name}} {
,{{end}}
};
}
{{ range $.InnerClassList}}
{{.}}
{{end}}
}
{{end}}`

type DartSpec struct {
APISpec *spec.ApiSpec
InnerClassList []string
}

func genData(dir string, api *spec.ApiSpec, isLegacy bool) error {
err := os.MkdirAll(dir, 0o755)
if err != nil {
Expand Down Expand Up @@ -104,12 +118,12 @@ func genData(dir string, api *spec.ApiSpec, isLegacy bool) error {
return err
}

err = convertDataType(api)
err, dartSpec := convertDataType(api, isLegacy)
if err != nil {
return err
}

return t.Execute(file, api)
return t.Execute(file, dartSpec)
}

func genTokens(dir string, isLeagcy bool) error {
Expand All @@ -132,24 +146,61 @@ func genTokens(dir string, isLeagcy bool) error {
return err
}

func convertDataType(api *spec.ApiSpec) error {
func convertDataType(api *spec.ApiSpec, isLegacy bool) (error, *DartSpec) {
var result DartSpec
types := api.Types
if len(types) == 0 {
return nil
return nil, &result
}

for _, ty := range types {
defineStruct, ok := ty.(spec.DefineStruct)
if ok {
for index, member := range defineStruct.Members {
tp, err := specTypeToDart(member.Type)
if err != nil {
return err
structMember, ok := member.Type.(spec.DefineStruct)
if ok && structMember.IsNestedStruct() {
defineStruct.Members[index].Type = spec.PrimitiveType{RawName: member.Name}
t := template.New("dataTemplate")
t = t.Funcs(funcMap)
tpl := dataTemplateV2
if isLegacy {
tpl = dataTemplate
}
t, err := t.Parse(tpl)
if err != nil {
return err, nil
}

var innerClassSpec = &spec.ApiSpec{
Types: []spec.Type{
spec.DefineStruct{
RawName: member.Name,
Members: structMember.Members,
},
},
}
err, dartSpec := convertDataType(innerClassSpec, isLegacy)
if err != nil {
return err, nil
}

writer := bytes.NewBuffer(nil)
err = t.Execute(writer, dartSpec)
if err != nil {
return err, nil
}
result.InnerClassList = append(result.InnerClassList, writer.String())
} else {
tp, err := specTypeToDart(member.Type)
if err != nil {
return err, nil
}
defineStruct.Members[index].Type = buildSpecType(member.Type, tp)
}
defineStruct.Members[index].Type = buildSpecType(member.Type, tp)
}
}
}
result.APISpec = api

return nil
return nil, &result
}
25 changes: 22 additions & 3 deletions tools/goctl/api/gogen/gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ var (
importTwiceApi string
//go:embed testdata/another_import_api.api
anotherImportApi string
//go:embed testdata/example.api
exampleApi string
)

func TestParser(t *testing.T) {
Expand Down Expand Up @@ -316,15 +318,32 @@ func TestCamelStyle(t *testing.T) {
validateWithCamel(t, filename, "GoZero")
}

func TestExampleGen(t *testing.T) {
env.Set(t, env.GoctlExperimental, env.ExperimentalOn)
filename := "greet.api"
err := os.WriteFile(filename, []byte(exampleApi), os.ModePerm)
assert.Nil(t, err)
t.Cleanup(func() {
_ = os.Remove(filename)
})

spec, err := parser.Parse(filename)
assert.Nil(t, err)
assert.Equal(t, len(spec.Types), 10)

validate(t, filename)
}

func validate(t *testing.T, api string) {
validateWithCamel(t, api, "gozero")
}

func validateWithCamel(t *testing.T, api, camel string) {
dir := "workspace"
defer func() {
os.RemoveAll(dir)
}()
t.Cleanup(func() {
_ = os.RemoveAll(dir)
})

err := pathx.MkdirIfNotExist(dir)
assert.Nil(t, err)
err = initMod(dir)
Expand Down
18 changes: 15 additions & 3 deletions tools/goctl/api/gogen/gentypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,21 @@ func writeType(writer io.Writer, tp spec.Type) error {
return fmt.Errorf("unspport struct type: %s", tp.Name())
}

fmt.Fprintf(writer, "type %s struct {\n", util.Title(tp.Name()))
for _, member := range structType.Members {
_, err := fmt.Fprintf(writer, "type %s struct {\n", util.Title(tp.Name()))
if err != nil {
return err
}

if err := writeMember(writer, structType.Members); err != nil {
return err
}

_, err = fmt.Fprintf(writer, "}")
return err
}

func writeMember(writer io.Writer, members []spec.Member) error {
for _, member := range members {
if member.IsInline {
if _, err := fmt.Fprintf(writer, "%s\n", strings.Title(member.Type.Name())); err != nil {
return err
Expand All @@ -88,6 +101,5 @@ func writeType(writer io.Writer, tp spec.Type) error {
return err
}
}
fmt.Fprintf(writer, "}")
return nil
}
99 changes: 99 additions & 0 deletions tools/goctl/api/gogen/testdata/example.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
syntax = "v1"

info(
title: "demo title"
desc: "demo desc"
author: "keson.an"
date: "2024-06-25"
version: "v1"
)

// empty structure
type Foo {
}

// type lit
type Bar {
Foo int `json:"foo"`
Bar bool `json:"bar"`
Baz []string `json:"baz"`
Qux map[string]string `json:"qux"`
}

type Baz {
Foo `json:"foo"`
// array type
Arr [2]int `json:"arr"`
// nested type
Bar {
Foo string `json:"foo"`
Bar bool `json:"bar"`
Baz {
Foo string `json:"foo"`
Bar bool `json:"bar"`
}
Qux {
Foo string `json:"foo"`
Bar bool `json:"bar"`
} `json:"qux"`
} `json:"bar"`
}


type UpdateReq {
Arg1 string `json:"arg1"`
}

type ListItem {
Value1 string `json:"value1"`
}

type LoginReq {
Username string `json:"username"`
Password string `json:"password"`
}

type LoginResp {
Name string `json:"name"`
}

type FormExampleReq {
Name string `form:"name"`
}

type PathExampleReq {
ID string `path:"id"`
}

type PathExampleResp {
Name string `json:"name"`
}

@server(
jwt: Auth
prefix: /v1
group: g1
timeout: 3s
middleware: AuthInterceptor
maxBytes: 1048576
)
service Foo {
@handler ping
get /ping

@handler update
post /update (UpdateReq)

@handler list
get /list returns ([]ListItem)

@handler login
post /login (LoginReq) returns (LoginResp)

@handler formExample
post /form/example (FormExampleReq)

@handler pathExample
get /path/example/:id (PathExampleReq) returns (PathExampleResp)
}

51 changes: 47 additions & 4 deletions tools/goctl/api/gogen/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,59 @@ func genFile(c fileGenConfig) error {

func writeProperty(writer io.Writer, name, tag, comment string, tp spec.Type, indent int) error {
util.WriteIndent(writer, indent)
var err error
var (
err error
isNestedStruct bool
)
structType, ok := tp.(spec.DefineStruct)
if ok && structType.IsNestedStruct() {
isNestedStruct = true
}
if len(comment) > 0 {
comment = strings.TrimPrefix(comment, "//")
comment = "//" + comment
_, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tp.Name(), tag, comment)
}

if isNestedStruct {
_, err = fmt.Fprintf(writer, "%s struct {\n", strings.Title(name))
if err != nil {
return err
}

if err := writeMember(writer, structType.Members); err != nil {
return err
}

_, err := fmt.Fprintf(writer, "} %s", tag)
if err != nil {
return err
}

if len(comment) > 0 {
_, err = fmt.Fprintf(writer, " %s", comment)
if err != nil {
return err
}
}
_, err = fmt.Fprint(writer, "\n")
if err != nil {
return err
}
} else {
_, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tp.Name(), tag)
if len(comment) > 0 {
_, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tp.Name(), tag, comment)
if err != nil {
return err
}
} else {
_, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tp.Name(), tag)
if err != nil {
return err
}
}
}

return err
return nil
}

func getAuths(api *spec.ApiSpec) []string {
Expand Down
4 changes: 1 addition & 3 deletions tools/goctl/api/parser/g4/ast/import.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package ast

import (
"github.com/zeromicro/go-zero/tools/goctl/api/parser/g4/gen/api"
)
import "github.com/zeromicro/go-zero/tools/goctl/api/parser/g4/gen/api"

// ImportExpr defines import syntax for api
type ImportExpr struct {
Expand Down
Loading

0 comments on commit a012a91

Please sign in to comment.