| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- // 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 (
- "context"
- "github.com/gogf/gf/v2/container/gset"
- "github.com/gogf/gf/v2/frame/g"
- "github.com/gogf/gf/v2/os/gfile"
- "github.com/gogf/gf/v2/os/gtime"
- "github.com/gogf/gf/v2/text/gregex"
- "github.com/gogf/gf/v2/util/gconv"
- "github.com/gogf/gf/v2/util/gtag"
- "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
- )
- const (
- CGenCtrlConfig = `gfcli.gen.ctrl`
- CGenCtrlUsage = `gf gen ctrl [OPTION]`
- CGenCtrlBrief = `parse api definitions to generate controller/sdk go files`
- CGenCtrlEg = `
- gf gen ctrl
- `
- CGenCtrlBriefSrcFolder = `source folder path to be parsed. default: api`
- CGenCtrlBriefDstFolder = `destination folder path storing automatically generated go files. default: internal/controller`
- CGenCtrlBriefWatchFile = `used in file watcher, it re-generates go files only if given file is under srcFolder`
- CGenCtrlBriefSdkPath = `also generate SDK go files for api definitions to specified directory`
- CGenCtrlBriefSdkStdVersion = `use standard version prefix for generated sdk request path`
- CGenCtrlBriefSdkNoV1 = `do not add version suffix for interface module name if version is v1`
- CGenCtrlBriefClear = `auto delete generated and unimplemented controller go files if api definitions are missing`
- )
- const (
- PatternApiDefinition = `type\s+(\w+)Req\s+struct\s+{`
- PatternCtrlDefinition = `func\s+\(.+?\)\s+\w+\(.+?\*(\w+)\.(\w+)Req\)\s+\(.+?\*(\w+)\.(\w+)Res,\s+\w+\s+error\)\s+{`
- )
- const (
- genCtrlFileLockSeconds = 10
- )
- func init() {
- gtag.Sets(g.MapStrStr{
- `CGenCtrlConfig`: CGenCtrlConfig,
- `CGenCtrlUsage`: CGenCtrlUsage,
- `CGenCtrlBrief`: CGenCtrlBrief,
- `CGenCtrlEg`: CGenCtrlEg,
- `CGenCtrlBriefSrcFolder`: CGenCtrlBriefSrcFolder,
- `CGenCtrlBriefDstFolder`: CGenCtrlBriefDstFolder,
- `CGenCtrlBriefWatchFile`: CGenCtrlBriefWatchFile,
- `CGenCtrlBriefSdkPath`: CGenCtrlBriefSdkPath,
- `CGenCtrlBriefSdkStdVersion`: CGenCtrlBriefSdkStdVersion,
- `CGenCtrlBriefSdkNoV1`: CGenCtrlBriefSdkNoV1,
- `CGenCtrlBriefClear`: CGenCtrlBriefClear,
- })
- }
- type (
- CGenCtrl struct{}
- CGenCtrlInput struct {
- g.Meta `name:"ctrl" config:"{CGenCtrlConfig}" usage:"{CGenCtrlUsage}" brief:"{CGenCtrlBrief}" eg:"{CGenCtrlEg}"`
- SrcFolder string `short:"s" name:"srcFolder" brief:"{CGenCtrlBriefSrcFolder}" d:"api"`
- DstFolder string `short:"d" name:"dstFolder" brief:"{CGenCtrlBriefDstFolder}" d:"internal/controller"`
- WatchFile string `short:"w" name:"watchFile" brief:"{CGenCtrlBriefWatchFile}"`
- SdkPath string `short:"k" name:"sdkPath" brief:"{CGenCtrlBriefSdkPath}"`
- SdkStdVersion bool `short:"v" name:"sdkStdVersion" brief:"{CGenCtrlBriefSdkStdVersion}" orphan:"true"`
- SdkNoV1 bool `short:"n" name:"sdkNoV1" brief:"{CGenCtrlBriefSdkNoV1}" orphan:"true"`
- Clear bool `short:"c" name:"clear" brief:"{CGenCtrlBriefClear}" orphan:"true"`
- }
- CGenCtrlOutput struct{}
- )
- func (c CGenCtrl) Ctrl(ctx context.Context, in CGenCtrlInput) (out *CGenCtrlOutput, err error) {
- if in.WatchFile != "" {
- err = c.generateByWatchFile(
- in.WatchFile, in.SdkPath, in.SdkStdVersion, in.SdkNoV1, in.Clear,
- )
- mlog.Print(`done!`)
- return
- }
- if !gfile.Exists(in.SrcFolder) {
- mlog.Fatalf(`source folder path "%s" does not exist`, in.SrcFolder)
- }
- // retrieve all api modules.
- apiModuleFolderPaths, err := gfile.ScanDir(in.SrcFolder, "*", false)
- if err != nil {
- return nil, err
- }
- for _, apiModuleFolderPath := range apiModuleFolderPaths {
- if !gfile.IsDir(apiModuleFolderPath) {
- continue
- }
- // generate go files by api module.
- var (
- module = gfile.Basename(apiModuleFolderPath)
- dstModuleFolderPath = gfile.Join(in.DstFolder, module)
- )
- err = c.generateByModule(
- apiModuleFolderPath, dstModuleFolderPath, in.SdkPath,
- in.SdkStdVersion, in.SdkNoV1, in.Clear,
- )
- if err != nil {
- return nil, err
- }
- }
- mlog.Print(`done!`)
- return
- }
- func (c CGenCtrl) generateByWatchFile(watchFile, sdkPath string, sdkStdVersion, sdkNoV1, clear bool) (err error) {
- // File lock to avoid multiple processes.
- var (
- flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
- flockContent = gfile.GetContents(flockFilePath)
- )
- if flockContent != "" {
- if gtime.Timestamp()-gconv.Int64(flockContent) < genCtrlFileLockSeconds {
- // If another generating process is running, it just exits.
- mlog.Debug(`another "gen service" process is running, exit`)
- return
- }
- }
- defer gfile.Remove(flockFilePath)
- _ = gfile.PutContents(flockFilePath, gtime.TimestampStr())
- // check this updated file is an api file.
- // watch file should be in standard goframe project structure.
- var (
- apiVersionPath = gfile.Dir(watchFile)
- apiModuleFolderPath = gfile.Dir(apiVersionPath)
- shouldBeNameOfAPi = gfile.Basename(gfile.Dir(apiModuleFolderPath))
- )
- if shouldBeNameOfAPi != "api" {
- return nil
- }
- // watch file should have api definitions.
- if gfile.Exists(watchFile) {
- if !gregex.IsMatchString(PatternApiDefinition, gfile.GetContents(watchFile)) {
- return nil
- }
- }
- var (
- projectRootPath = gfile.Dir(gfile.Dir(apiModuleFolderPath))
- module = gfile.Basename(apiModuleFolderPath)
- dstModuleFolderPath = gfile.Join(projectRootPath, "internal", "controller", module)
- )
- return c.generateByModule(
- apiModuleFolderPath, dstModuleFolderPath, sdkPath, sdkStdVersion, sdkNoV1, clear,
- )
- }
- // parseApiModule parses certain api and generate associated go files by certain module, not all api modules.
- func (c CGenCtrl) generateByModule(
- apiModuleFolderPath, dstModuleFolderPath, sdkPath string,
- sdkStdVersion, sdkNoV1, clear bool,
- ) (err error) {
- // parse src and dst folder go files.
- apiItemsInSrc, err := c.getApiItemsInSrc(apiModuleFolderPath)
- if err != nil {
- return err
- }
- apiItemsInDst, err := c.getApiItemsInDst(dstModuleFolderPath)
- if err != nil {
- return err
- }
- // generate api interface go files.
- if err = newApiInterfaceGenerator().Generate(apiModuleFolderPath, apiItemsInSrc); err != nil {
- return
- }
- // generate controller go files.
- // api filtering for already implemented api controllers.
- var (
- alreadyImplementedCtrlSet = gset.NewStrSet()
- toBeImplementedApiItems = make([]apiItem, 0)
- )
- for _, item := range apiItemsInDst {
- alreadyImplementedCtrlSet.Add(item.String())
- }
- for _, item := range apiItemsInSrc {
- if alreadyImplementedCtrlSet.Contains(item.String()) {
- continue
- }
- toBeImplementedApiItems = append(toBeImplementedApiItems, item)
- }
- if len(toBeImplementedApiItems) > 0 {
- err = newControllerGenerator().Generate(dstModuleFolderPath, toBeImplementedApiItems)
- if err != nil {
- return
- }
- }
- // delete unimplemented controllers if api definitions are missing.
- if clear {
- var (
- apiDefinitionSet = gset.NewStrSet()
- extraApiItemsInCtrl = make([]apiItem, 0)
- )
- for _, item := range apiItemsInSrc {
- apiDefinitionSet.Add(item.String())
- }
- for _, item := range apiItemsInDst {
- if apiDefinitionSet.Contains(item.String()) {
- continue
- }
- extraApiItemsInCtrl = append(extraApiItemsInCtrl, item)
- }
- if len(extraApiItemsInCtrl) > 0 {
- err = newControllerClearer().Clear(dstModuleFolderPath, extraApiItemsInCtrl)
- if err != nil {
- return
- }
- }
- }
- // generate sdk go files.
- if sdkPath != "" {
- if err = newApiSdkGenerator().Generate(apiItemsInSrc, sdkPath, sdkStdVersion, sdkNoV1); err != nil {
- return
- }
- }
- return
- }
|