Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(goctl): support nested struct #4211

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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