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

refactor(cmd/gf): improve gf gen ctrl using ast #3616

Merged
merged 3 commits into from
Jun 4, 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
108 changes: 35 additions & 73 deletions cmd/gf/internal/cmd/cmd_z_unit_gen_ctrl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,56 +83,12 @@ func Test_Gen_Ctrl_Default(t *testing.T) {
})
}

// https://github.com/gogf/gf/issues/3460
func Test_Gen_Ctrl_UseMerge_Issue3460(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctrlPath = gfile.Temp(guid.S())
//ctrlPath = gtest.DataPath("issue", "3460", "controller")
apiFolder = gtest.DataPath("issue", "3460", "api")
in = genctrl.CGenCtrlInput{
SrcFolder: apiFolder,
DstFolder: ctrlPath,
WatchFile: "",
SdkPath: "",
SdkStdVersion: false,
SdkNoV1: false,
Clear: false,
Merge: true,
}
)

err := gfile.Mkdir(ctrlPath)
t.AssertNil(err)
defer gfile.Remove(ctrlPath)

_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)

files, err := gfile.ScanDir(ctrlPath, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.Join(ctrlPath, "/hello/hello.go"),
filepath.Join(ctrlPath, "/hello/hello_new.go"),
filepath.Join(ctrlPath, "/hello/hello_v1_req.go"),
filepath.Join(ctrlPath, "/hello/hello_v2_req.go"),
})

expectCtrlPath := gtest.DataPath("issue", "3460", "controller")
expectFiles := []string{
filepath.Join(expectCtrlPath, "/hello/hello.go"),
filepath.Join(expectCtrlPath, "/hello/hello_new.go"),
filepath.Join(expectCtrlPath, "/hello/hello_v1_req.go"),
filepath.Join(expectCtrlPath, "/hello/hello_v2_req.go"),
}

// Line Feed maybe \r\n or \n
for i, expectFile := range expectFiles {
val := gfile.GetContents(files[i])
expect := gfile.GetContents(expectFile)
t.Assert(val, expect)
}
})
func expectFilesContent(t *gtest.T, paths []string, expectPaths []string) {
for i, expectFile := range expectPaths {
val := gfile.GetContents(paths[i])
expect := gfile.GetContents(expectFile)
t.Assert(val, expect)
}
}

// gf gen ctrl -m
Expand Down Expand Up @@ -300,20 +256,13 @@ type DictTypeAddRes struct {

}

func expectFilesContent(t *gtest.T, paths []string, expectPaths []string) {
for i, expectFile := range expectPaths {
val := gfile.GetContents(paths[i])
expect := gfile.GetContents(expectFile)
t.Assert(val, expect)
}
}

// https://github.com/gogf/gf/issues/3569
func Test_Gen_Ctrl_Comments_Issue3569(t *testing.T) {
gqcn marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/gogf/gf/issues/3460
func Test_Gen_Ctrl_UseMerge_Issue3460(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
ctrlPath = gfile.Temp(guid.S())
apiFolder = gtest.DataPath("issue", "3569", "api")
ctrlPath = gfile.Temp(guid.S())
//ctrlPath = gtest.DataPath("issue", "3460", "controller")
apiFolder = gtest.DataPath("issue", "3460", "api")
in = genctrl.CGenCtrlInput{
SrcFolder: apiFolder,
DstFolder: ctrlPath,
Expand All @@ -326,22 +275,35 @@ func Test_Gen_Ctrl_Comments_Issue3569(t *testing.T) {
}
)

err := gutil.FillStructWithDefault(&in)
t.AssertNil(err)

err = gfile.Mkdir(ctrlPath)
err := gfile.Mkdir(ctrlPath)
t.AssertNil(err)
defer gfile.Remove(ctrlPath)

_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
t.AssertNil(err)

//apiInterface file
var (
genApi = apiFolder + filepath.FromSlash("/hello/hello.go")
genApiExpect = apiFolder + filepath.FromSlash("/hello/hello_expect.go")
)
defer gfile.Remove(genApi)
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
files, err := gfile.ScanDir(ctrlPath, "*.go", true)
t.AssertNil(err)
t.Assert(files, []string{
filepath.Join(ctrlPath, "/hello/hello.go"),
filepath.Join(ctrlPath, "/hello/hello_new.go"),
filepath.Join(ctrlPath, "/hello/hello_v1_req.go"),
filepath.Join(ctrlPath, "/hello/hello_v2_req.go"),
})

expectCtrlPath := gtest.DataPath("issue", "3460", "controller")
expectFiles := []string{
filepath.Join(expectCtrlPath, "/hello/hello.go"),
filepath.Join(expectCtrlPath, "/hello/hello_new.go"),
filepath.Join(expectCtrlPath, "/hello/hello_v1_req.go"),
filepath.Join(expectCtrlPath, "/hello/hello_v2_req.go"),
}

// Line Feed maybe \r\n or \n
for i, expectFile := range expectFiles {
val := gfile.GetContents(files[i])
expect := gfile.GetContents(expectFile)
t.Assert(val, expect)
}
})
}
78 changes: 78 additions & 0 deletions cmd/gf/internal/cmd/genctrl/genctrl_ast_parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package genctrl

import (
"bytes"
"go/ast"
"go/parser"
"go/printer"
"go/token"

"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
)

// getStructsNameInSrc retrieves all struct names
// that end in "Req" and have "g.Meta" in their body.
func (c CGenCtrl) getStructsNameInSrc(filePath string) (structsName []string, err error) {
var (
fileContent = gfile.GetContents(filePath)
fileSet = token.NewFileSet()
)

node, err := parser.ParseFile(fileSet, "", fileContent, parser.ParseComments)
if err != nil {
return
}

ast.Inspect(node, func(n ast.Node) bool {
if typeSpec, ok := n.(*ast.TypeSpec); ok {
methodName := typeSpec.Name.Name
if !gstr.HasSuffix(methodName, "Req") {
// ignore struct name that do not end in "Req"
return true
}
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
var buf bytes.Buffer
if err := printer.Fprint(&buf, fileSet, structType); err != nil {
return false
}
// ignore struct name that match a request, but has no g.Meta in its body.
if !gstr.Contains(buf.String(), `g.Meta`) {
return true
}
structsName = append(structsName, methodName)
}
}
return true
})

return
}

// getImportsInDst retrieves all import paths in the file.
func (c CGenCtrl) getImportsInDst(filePath string) (imports []string, err error) {
var (
fileContent = gfile.GetContents(filePath)
fileSet = token.NewFileSet()
)

node, err := parser.ParseFile(fileSet, "", fileContent, parser.ParseComments)
if err != nil {
return
}

ast.Inspect(node, func(n ast.Node) bool {
if imp, ok := n.(*ast.ImportSpec); ok {
imports = append(imports, imp.Path.Value)
}
return true
})

return
}
43 changes: 43 additions & 0 deletions cmd/gf/internal/cmd/genctrl/genctrl_ast_parse_clear.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package genctrl

import (
"bytes"
"go/ast"
"go/parser"
"go/printer"
"go/token"

"github.com/gogf/gf/v2/os/gfile"
)

// getFuncInDst retrieves all function declarations and bodies in the file.
func (c *controllerClearer) getFuncInDst(filePath string) (funcs []string, err error) {
var (
fileContent = gfile.GetContents(filePath)
fileSet = token.NewFileSet()
)

node, err := parser.ParseFile(fileSet, "", fileContent, parser.ParseComments)
if err != nil {
return
}

ast.Inspect(node, func(n ast.Node) bool {
if fun, ok := n.(*ast.FuncDecl); ok {
var buf bytes.Buffer
if err := printer.Fprint(&buf, fileSet, fun); err != nil {
return false
}
funcs = append(funcs, buf.String())
}
return true
})

return
}
66 changes: 0 additions & 66 deletions cmd/gf/internal/cmd/genctrl/genctrl_calculate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@
package genctrl

import (
"bytes"
"go/ast"
"go/parser"
"go/printer"
"go/token"

"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
Expand Down Expand Up @@ -139,63 +133,3 @@ func (c CGenCtrl) getApiItemsInDst(dstFolder string) (items []apiItem, err error
}
return
}

// getStructsNameInSrc retrieves all struct names
// that end in "Req" and have "g.Meta" in their body.
func (c CGenCtrl) getStructsNameInSrc(filePath string) (structsName []string, err error) {
var (
fileContent = gfile.GetContents(filePath)
fileSet = token.NewFileSet()
)

node, err := parser.ParseFile(fileSet, "", fileContent, parser.ParseComments)
if err != nil {
return
}

ast.Inspect(node, func(n ast.Node) bool {
if typeSpec, ok := n.(*ast.TypeSpec); ok {
methodName := typeSpec.Name.Name
if !gstr.HasSuffix(methodName, "Req") {
// ignore struct name that do not end in "Req"
return true
}
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
var buf bytes.Buffer
if err := printer.Fprint(&buf, fileSet, structType); err != nil {
return false
}
// ignore struct name that match a request, but has no g.Meta in its body.
if !gstr.Contains(buf.String(), `g.Meta`) {
return true
}
structsName = append(structsName, methodName)
}
}
return true
})

return
}

// getImportsInDst retrieves all import paths in the file.
func (c CGenCtrl) getImportsInDst(filePath string) (imports []string, err error) {
var (
fileContent = gfile.GetContents(filePath)
fileSet = token.NewFileSet()
)

node, err := parser.ParseFile(fileSet, "", fileContent, parser.ParseComments)
if err != nil {
return
}

ast.Inspect(node, func(n ast.Node) bool {
if imp, ok := n.(*ast.ImportSpec); ok {
imports = append(imports, imp.Path.Value)
}
return true
})

return
}
12 changes: 5 additions & 7 deletions cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl_clear.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)

Expand All @@ -36,17 +35,16 @@ func (c *controllerClearer) doClear(dstModuleFolderPath string, item apiItem) (e
methodFilePath = gfile.Join(dstModuleFolderPath, fmt.Sprintf(
`%s_%s_%s.go`, item.Module, item.Version, methodNameSnake,
))
fileContent = gstr.Trim(gfile.GetContents(methodFilePath))
)
// retrieve it without using AST, because it's simple.
match, err := gregex.MatchString(`.+?Req.+?Res.+?{([\s\S]+?)}`, fileContent)

funcs, err := c.getFuncInDst(methodFilePath)
if err != nil {
return err
}
if len(match) > 1 {
implements := gstr.Trim(match[1])

if len(funcs) > 1 {
// One line.
if !gstr.Contains(implements, "\n") && gstr.Contains(implements, `CodeNotImplemented`) {
if !gstr.Contains(funcs[0], "\n") && gstr.Contains(funcs[0], `CodeNotImplemented`) {
mlog.Printf(
`remove unimplemented and of no api definitions controller file: %s`,
methodFilePath,
Expand Down
8 changes: 8 additions & 0 deletions cmd/gf/internal/cmd/testdata/genctrl/api/article/v2/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ type UpdateReq struct {
}

type UpdateRes struct{}

//type GetListReq struct {
// g.Meta `path:"/article/list" method:"get" tags:"ArticleService"`
//}
//
//type GetListRes struct {
// list []struct{}
//}
15 changes: 0 additions & 15 deletions cmd/gf/internal/cmd/testdata/issue/3569/api/hello/hello_expect.go

This file was deleted.

Loading
Loading