Skip to content

Commit

Permalink
cmd/gf: refactor gf gen service with AST (#3488)
Browse files Browse the repository at this point in the history
  • Loading branch information
oldme-git authored May 27, 2024
1 parent b675d01 commit 8f4d5f7
Show file tree
Hide file tree
Showing 16 changed files with 822 additions and 371 deletions.
4 changes: 4 additions & 0 deletions cmd/gf/internal/cmd/cmd_z_unit_gen_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,16 @@ func Test_Gen_Service_Default(t *testing.T) {
t.AssertNil(err)
t.Assert(files, []string{
dstFolder + filepath.FromSlash("/article.go"),
dstFolder + filepath.FromSlash("/delivery.go"),
dstFolder + filepath.FromSlash("/user.go"),
})

// contents
testPath := gtest.DataPath("genservice", "service")
expectFiles := []string{
testPath + filepath.FromSlash("/article.go"),
testPath + filepath.FromSlash("/delivery.go"),
testPath + filepath.FromSlash("/user.go"),
}
for i := range files {
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
Expand Down
147 changes: 39 additions & 108 deletions cmd/gf/internal/cmd/genservice/genservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import (
"context"
"fmt"
"path/filepath"
"sync"
"sync/atomic"

"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gset"
Expand All @@ -21,9 +25,6 @@ import (
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gtag"

"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
)

const (
Expand Down Expand Up @@ -147,19 +148,22 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
}

var (
isDirty bool // Temp boolean.
isDirty atomic.Value // Temp boolean.
files []string // Temp file array.
fileContent string // Temp file content for handling go file.
initImportSrcPackages []string // Used for generating logic.go.
inputPackages = in.Packages // Custom packages.
dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder)) // Package name for generated go files.
generatedDstFilePathSet = gset.NewStrSet() // All generated file path set.
)
isDirty.Store(false)

// The first level folders.
srcFolderPaths, err := gfile.ScanDir(in.SrcFolder, "*", false)
if err != nil {
return nil, err
}
// it will use goroutine to generate service files for each package.
var wg = sync.WaitGroup{}
for _, srcFolderPath := range srcFolderPaths {
if !gfile.IsDir(srcFolderPath) {
continue
Expand All @@ -173,111 +177,30 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
}
// Parse single logic package folder.
var (
// StructName => FunctionDefinitions
srcPkgInterfaceMap = gmap.NewListMap()
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
importAliasToPathMap = gmap.NewStrStrMap() // for conflict imports check. alias => import path(with `"`)
importPathToAliasMap = gmap.NewStrStrMap() // for conflict imports check. import path(with `"`) => alias
srcPackageName = gfile.Basename(srcFolderPath)
ok bool
dstFilePath = gfile.Join(in.DstFolder,
srcPackageName = gfile.Basename(srcFolderPath)
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
srcStructFunctions = gmap.NewListMap()
dstFilePath = gfile.Join(in.DstFolder,
c.getDstFileNameCase(srcPackageName, in.DstFileNameCase)+".go",
)
srcCodeCommentedMap = make(map[string]string)
)
generatedDstFilePathSet.Add(dstFilePath)
// if it were to use goroutine,
// it would cause the order of the generated functions in the file to be disordered.
for _, file := range files {
var packageItems []packageItem
fileContent = gfile.GetContents(file)
// Calculate code comments in source Go files.
err = c.calculateCodeCommented(in, fileContent, srcCodeCommentedMap)
if err != nil {
return nil, err
}
// remove all comments.
fileContent, err = gregex.ReplaceString(`(//.*)|((?s)/\*.*?\*/)`, "", fileContent)
pkgItems, funcItems, err := c.parseItemsInSrc(file)
if err != nil {
return nil, err
}

// Calculate imported packages of source go files.
packageItems, err = c.calculateImportedPackages(fileContent)
// Calculate imported packages for service generating.
err = c.calculateImportedItems(in, pkgItems, funcItems, srcImportedPackages)
if err != nil {
return nil, err
}
// try finding the conflicts imports between files.
for _, item := range packageItems {
var alias = item.Alias
if alias == "" {
alias = gfile.Basename(gstr.Trim(item.Path, `"`))
}

// ignore unused import paths, which do not exist in function definitions.
if !gregex.IsMatchString(fmt.Sprintf(`func .+?([^\w])%s(\.\w+).+?{`, alias), fileContent) {
mlog.Debugf(`ignore unused package: %s`, item.RawImport)
continue
}

// find the exist alias with the same import path.
var existAlias = importPathToAliasMap.Get(item.Path)
if existAlias != "" {
fileContent, err = gregex.ReplaceStringFuncMatch(
fmt.Sprintf(`([^\w])%s(\.\w+)`, alias), fileContent,
func(match []string) string {
return match[1] + existAlias + match[2]
},
)
if err != nil {
return nil, err
}
continue
}

// resolve alias conflicts.
var importPath = importAliasToPathMap.Get(alias)
if importPath == "" {
importAliasToPathMap.Set(alias, item.Path)
importPathToAliasMap.Set(item.Path, alias)
srcImportedPackages.Add(item.RawImport)
continue
}
if importPath != item.Path {
// update the conflicted alias for import path with suffix.
// eg:
// v1 -> v10
// v11 -> v110
for aliasIndex := 0; ; aliasIndex++ {
item.Alias = fmt.Sprintf(`%s%d`, alias, aliasIndex)
var existPathForAlias = importAliasToPathMap.Get(item.Alias)
if existPathForAlias != "" {
if existPathForAlias == item.Path {
break
}
continue
}
break
}
importPathToAliasMap.Set(item.Path, item.Alias)
importAliasToPathMap.Set(item.Alias, item.Path)
// reformat the import path with alias.
item.RawImport = fmt.Sprintf(`%s %s`, item.Alias, item.Path)

// update the file content with new alias import.
fileContent, err = gregex.ReplaceStringFuncMatch(
fmt.Sprintf(`([^\w])%s(\.\w+)`, alias), fileContent,
func(match []string) string {
return match[1] + item.Alias + match[2]
},
)
if err != nil {
return nil, err
}
srcImportedPackages.Add(item.RawImport)
}
}

// Calculate functions and interfaces for service generating.
err = c.calculateInterfaceFunctions(in, fileContent, srcPkgInterfaceMap)
err = c.calculateFuncItems(in, funcItems, srcStructFunctions)
if err != nil {
return nil, err
}
Expand All @@ -295,22 +218,28 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
)
continue
}

// Generating service go file for single logic package.
if ok, err = c.generateServiceFile(generateServiceFilesInput{
wg.Add(1)
go func(generateServiceFilesInput generateServiceFilesInput) {
defer wg.Done()
ok, err := c.generateServiceFile(generateServiceFilesInput)
if err != nil {
mlog.Printf(`error generating service file "%s": %v`, generateServiceFilesInput.DstFilePath, err)
}
if !isDirty.Load().(bool) && ok {
isDirty.Store(true)
}
}(generateServiceFilesInput{
CGenServiceInput: in,
SrcStructFunctions: srcPkgInterfaceMap,
SrcImportedPackages: srcImportedPackages.Slice(),
SrcPackageName: srcPackageName,
SrcImportedPackages: srcImportedPackages.Slice(),
SrcStructFunctions: srcStructFunctions,
DstPackageName: dstPackageName,
DstFilePath: dstFilePath,
SrcCodeCommentedMap: srcCodeCommentedMap,
}); err != nil {
return
}
if ok {
isDirty = true
}
})
}
wg.Wait()

if in.Clear {
files, err = gfile.ScanDirFile(in.DstFolder, "*.go", false)
Expand All @@ -320,7 +249,9 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
var relativeFilePath string
for _, file := range files {
relativeFilePath = gstr.SubStrFromR(file, in.DstFolder)
if !generatedDstFilePathSet.Contains(relativeFilePath) && utils.IsFileDoNotEdit(relativeFilePath) {
if !generatedDstFilePathSet.Contains(relativeFilePath) &&
utils.IsFileDoNotEdit(relativeFilePath) {

mlog.Printf(`remove no longer used service file: %s`, relativeFilePath)
if err = gfile.Remove(file); err != nil {
return nil, err
Expand All @@ -329,7 +260,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
}
}

if isDirty {
if isDirty.Load().(bool) {
// Generate initialization go file.
if len(initImportSrcPackages) > 0 {
if err = c.generateInitializationFile(in, initImportSrcPackages); err != nil {
Expand Down
Loading

0 comments on commit 8f4d5f7

Please sign in to comment.