From ee211dbdac27558e1a48fc7dacf5c1ad658176a9 Mon Sep 17 00:00:00 2001 From: oldme <45782393+oldme-git@users.noreply.github.com> Date: Tue, 4 Jun 2024 21:13:23 +0800 Subject: [PATCH] cmd/gf: improve gf gen ctrl using ast (#3616) --- .../internal/cmd/cmd_z_unit_gen_ctrl_test.go | 108 ++++++------------ .../internal/cmd/genctrl/genctrl_ast_parse.go | 78 +++++++++++++ .../cmd/genctrl/genctrl_ast_parse_clear.go | 43 +++++++ .../internal/cmd/genctrl/genctrl_calculate.go | 66 ----------- .../genctrl/genctrl_generate_ctrl_clear.go | 12 +- .../testdata/genctrl/api/article/v2/edit.go | 8 ++ .../issue/3569/api/hello/hello_expect.go | 15 --- .../testdata/issue/3569/api/hello/v1/req.go | 24 ---- 8 files changed, 169 insertions(+), 185 deletions(-) create mode 100644 cmd/gf/internal/cmd/genctrl/genctrl_ast_parse.go create mode 100644 cmd/gf/internal/cmd/genctrl/genctrl_ast_parse_clear.go delete mode 100644 cmd/gf/internal/cmd/testdata/issue/3569/api/hello/hello_expect.go delete mode 100644 cmd/gf/internal/cmd/testdata/issue/3569/api/hello/v1/req.go diff --git a/cmd/gf/internal/cmd/cmd_z_unit_gen_ctrl_test.go b/cmd/gf/internal/cmd/cmd_z_unit_gen_ctrl_test.go index cf77bc8dfd6..1270cc67427 100644 --- a/cmd/gf/internal/cmd/cmd_z_unit_gen_ctrl_test.go +++ b/cmd/gf/internal/cmd/cmd_z_unit_gen_ctrl_test.go @@ -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) + } }) } diff --git a/cmd/gf/internal/cmd/genctrl/genctrl_ast_parse.go b/cmd/gf/internal/cmd/genctrl/genctrl_ast_parse.go new file mode 100644 index 00000000000..cf27089cb2f --- /dev/null +++ b/cmd/gf/internal/cmd/genctrl/genctrl_ast_parse.go @@ -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 +} diff --git a/cmd/gf/internal/cmd/genctrl/genctrl_ast_parse_clear.go b/cmd/gf/internal/cmd/genctrl/genctrl_ast_parse_clear.go new file mode 100644 index 00000000000..a1bd7391b62 --- /dev/null +++ b/cmd/gf/internal/cmd/genctrl/genctrl_ast_parse_clear.go @@ -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 +} diff --git a/cmd/gf/internal/cmd/genctrl/genctrl_calculate.go b/cmd/gf/internal/cmd/genctrl/genctrl_calculate.go index f794559c980..3fb8825ade0 100644 --- a/cmd/gf/internal/cmd/genctrl/genctrl_calculate.go +++ b/cmd/gf/internal/cmd/genctrl/genctrl_calculate.go @@ -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 -} diff --git a/cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl_clear.go b/cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl_clear.go index b419399e65a..73d562648b6 100644 --- a/cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl_clear.go +++ b/cmd/gf/internal/cmd/genctrl/genctrl_generate_ctrl_clear.go @@ -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, diff --git a/cmd/gf/internal/cmd/testdata/genctrl/api/article/v2/edit.go b/cmd/gf/internal/cmd/testdata/genctrl/api/article/v2/edit.go index 8e8267a578e..2ae20696361 100644 --- a/cmd/gf/internal/cmd/testdata/genctrl/api/article/v2/edit.go +++ b/cmd/gf/internal/cmd/testdata/genctrl/api/article/v2/edit.go @@ -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{} +//} diff --git a/cmd/gf/internal/cmd/testdata/issue/3569/api/hello/hello_expect.go b/cmd/gf/internal/cmd/testdata/issue/3569/api/hello/hello_expect.go deleted file mode 100644 index 745910243de..00000000000 --- a/cmd/gf/internal/cmd/testdata/issue/3569/api/hello/hello_expect.go +++ /dev/null @@ -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) -} diff --git a/cmd/gf/internal/cmd/testdata/issue/3569/api/hello/v1/req.go b/cmd/gf/internal/cmd/testdata/issue/3569/api/hello/v1/req.go deleted file mode 100644 index 97d74123f95..00000000000 --- a/cmd/gf/internal/cmd/testdata/issue/3569/api/hello/v1/req.go +++ /dev/null @@ -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 { -//}