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

This commit is contained in:
oldme
2024-06-04 21:13:23 +08:00
committed by GitHub
parent 4d916ae73e
commit ee211dbdac
8 changed files with 169 additions and 185 deletions

View File

@ -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
@ -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) {
// 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,
@ -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)
}
})
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
@ -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
}

View File

@ -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"
)
@ -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,

View File

@ -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{}
//}

View File

@ -1,15 +0,0 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package hello
import (
"context"
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/testdata/issue/3569/api/hello/v1"
)
type IHelloV1 interface {
Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error)
}

View File

@ -1,24 +0,0 @@
package v1
import "github.com/gogf/gf/v2/frame/g"
type HelloReq struct {
g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"api-Hello"`
}
type HelloRes struct {
g.Meta `mime:"text/html" example:"string"`
}
// type Hello2Req struct {
// g.Meta `path:"/hello2" tags:"Hello" method:"get" summary:"api-Hello2"`
// }
// type Hello2Res struct {
// g.Meta `mime:"text/html" example:"string"`
// }
//type Hello3Req struct {
// g.Meta `path:"/hello3" tags:"Hello" method:"get" summary:"api-Hello3"`
// PodState consts.PodPhase
//}
//type Hello3Res struct {
//}