improve command gen pb by adding controller go files generating (#2518)

This commit is contained in:
John Guo
2023-03-14 09:47:42 +08:00
committed by GitHub
parent ae86f66545
commit b742e222d6
6 changed files with 233 additions and 25 deletions

View File

@ -124,13 +124,16 @@ type cBuildInput struct {
PackSrc string `short:"ps" name:"packSrc" brief:"pack one or more folders into one go file before building"`
PackDst string `short:"pd" name:"packDst" brief:"temporary go file path for pack, this go file will be automatically removed after built" d:"internal/packed/build_pack_data.go"`
ExitWhenError bool `short:"ew" name:"exitWhenError" brief:"exit building when any error occurs, default is false" orphan:"true"`
DumpENV bool `short:"de" name:"dumpEnv" brief:"dump current go build environment before building binary" orphan:"true"`
}
type cBuildOutput struct{}
func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, err error) {
// print used go env
_, _ = Env.Index(ctx, cEnvInput{})
if in.DumpENV {
_, _ = Env.Index(ctx, cEnvInput{})
}
mlog.SetHeaderPrint(true)

View File

@ -12,9 +12,10 @@ import (
type (
CGenPb struct{}
CGenPbInput struct {
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
Path string `name:"path" short:"p" dc:"protobuf file folder path" d:"manifest/protobuf"`
Output string `name:"output" short:"o" dc:"output folder path storing generated go files" d:"api"`
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
Path string `name:"path" short:"p" dc:"protobuf file folder path" d:"manifest/protobuf"`
OutputApi string `name:"outputApi" short:"oa" dc:"output folder path storing generated go files of api" d:"api"`
OutputCtrl string `name:"outputCtrl" short:"oc" dc:"output folder path storing generated go files of controller" d:"internal/controller"`
}
CGenPbOutput struct{}
)
@ -32,9 +33,13 @@ func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err
mlog.Fatalf(`proto files folder "%s" does not exist`, in.Path)
}
// output path checks.
outputPath := gfile.RealPath(in.Output)
if outputPath == "" {
mlog.Fatalf(`output folder "%s" does not exist`, in.Output)
outputApiPath := gfile.RealPath(in.OutputApi)
if outputApiPath == "" {
mlog.Fatalf(`output api folder "%s" does not exist`, in.OutputApi)
}
outputCtrlPath := gfile.RealPath(in.OutputCtrl)
if outputCtrlPath == "" {
mlog.Fatalf(`output controller folder "%s" does not exist`, in.OutputCtrl)
}
// folder scanning.
@ -52,8 +57,8 @@ func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err
for _, file := range files {
var command = gproc.NewProcess(protoc, nil)
command.Args = append(command.Args, "--proto_path="+gfile.Pwd())
command.Args = append(command.Args, "--go_out=paths=source_relative:"+outputPath)
command.Args = append(command.Args, "--go-grpc_out=paths=source_relative:"+outputPath)
command.Args = append(command.Args, "--go_out=paths=source_relative:"+outputApiPath)
command.Args = append(command.Args, "--go-grpc_out=paths=source_relative:"+outputApiPath)
command.Args = append(command.Args, file)
mlog.Print(command.String())
if err = command.Run(ctx); err != nil {
@ -61,7 +66,15 @@ func (c CGenPb) Pb(ctx context.Context, in CGenPbInput) (out *CGenPbOutput, err
}
}
// Generate struct tag according comment rules.
err = c.generateStructTag(ctx, generateStructTagInput{PbPath: outputPath})
err = c.generateStructTag(ctx, generateStructTagInput{OutputApiPath: outputApiPath})
if err != nil {
return
}
// Generate controllers according comment rules.
err = c.generateController(ctx, generateControllerInput{
OutputApiPath: outputApiPath,
OutputCtrlPath: outputCtrlPath,
})
if err != nil {
return
}

View File

@ -0,0 +1,189 @@
package genpb
import (
"context"
"fmt"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
type generateControllerInput struct {
OutputApiPath string
OutputCtrlPath string
}
type generateCtrl struct {
Name string
Package string
Version string
Methods []generateCtrlMethod
}
type generateCtrlMethod struct {
Name string
Definition string
}
const (
controllerTemplate = `
package {Package}
type Controller struct {
{Version}.Unimplemented{Name}Server
}
func Register(s *grpcx.GrpcServer) {
{Version}.Register{Name}Server(s.Server, &Controller{})
}
`
controllerMethodTemplate = `
func (*Controller) {Definition} {
return nil, gerror.NewCode(gcode.CodeNotImplemented)
}
`
)
func (c CGenPb) generateController(ctx context.Context, in generateControllerInput) (err error) {
files, err := gfile.ScanDirFile(in.OutputApiPath, "*_grpc.pb.go", true)
if err != nil {
return err
}
var controllers []generateCtrl
for _, file := range files {
fileControllers, err := c.parseControllers(file)
if err != nil {
return err
}
controllers = append(controllers, fileControllers...)
}
if len(controllers) == 0 {
return nil
}
// Generate controller files.
err = c.doGenerateControllers(in, controllers)
return
}
func (c CGenPb) parseControllers(filePath string) ([]generateCtrl, error) {
var (
controllers []generateCtrl
content = gfile.GetContents(filePath)
)
_, err := gregex.ReplaceStringFuncMatch(
`type (\w+)Server interface {([\s\S]+?)}`,
content,
func(match []string) string {
ctrl := generateCtrl{
Name: match[1],
Package: gfile.Basename(gfile.Dir(gfile.Dir(filePath))),
Version: gfile.Basename(gfile.Dir(filePath)),
Methods: make([]generateCtrlMethod, 0),
}
lines := gstr.Split(match[2], "\n")
for _, line := range lines {
line = gstr.Trim(line)
if line == "" || !gstr.IsLetterUpper(line[0]) {
continue
}
// Comment.
if gregex.IsMatchString(`^//.+`, line) {
continue
}
line, _ = gregex.ReplaceStringFuncMatch(
`^(\w+)\(context\.Context, \*(\w+)\) \(\*(\w+), error\)$`,
line,
func(match []string) string {
return fmt.Sprintf(
`%s(ctx context.Context, req *%s.%s) (res *%s.%s, err error)`,
match[1], ctrl.Version, match[2], ctrl.Version, match[3],
)
},
)
ctrl.Methods = append(ctrl.Methods, generateCtrlMethod{
Name: gstr.Split(line, "(")[0],
Definition: line,
})
}
if len(ctrl.Methods) > 0 {
controllers = append(controllers, ctrl)
}
return match[0]
},
)
return controllers, err
}
func (c CGenPb) doGenerateControllers(in generateControllerInput, controllers []generateCtrl) (err error) {
for _, controller := range controllers {
err = c.doGenerateController(in, controller)
if err != nil {
return err
}
}
err = utils.ReplaceGeneratedContentGFV2(in.OutputCtrlPath)
return nil
}
func (c CGenPb) doGenerateController(in generateControllerInput, controller generateCtrl) (err error) {
var (
folderPath = gfile.Join(in.OutputCtrlPath, controller.Package)
filePath = gfile.Join(folderPath, controller.Package+".go")
isDirty bool
)
if !gfile.Exists(folderPath) {
if err = gfile.Mkdir(folderPath); err != nil {
return err
}
}
if !gfile.Exists(filePath) {
templateContent := gstr.ReplaceByMap(controllerTemplate, g.MapStrStr{
"{Name}": controller.Name,
"{Version}": controller.Version,
"{Package}": controller.Package,
})
if err = gfile.PutContents(filePath, templateContent); err != nil {
return err
}
isDirty = true
}
// Exist controller content.
var ctrlContent string
files, err := gfile.ScanDirFile(folderPath, "*.go", false)
if err != nil {
return err
}
for _, file := range files {
if ctrlContent != "" {
ctrlContent += "\n"
}
ctrlContent += gfile.GetContents(file)
}
// Generate method content.
var generatedContent string
for _, method := range controller.Methods {
if gstr.Contains(ctrlContent, fmt.Sprintf(`%s(`, method.Name)) {
continue
}
if generatedContent != "" {
generatedContent += "\n"
}
generatedContent += gstr.ReplaceByMap(controllerMethodTemplate, g.MapStrStr{
"{Definition}": method.Definition,
})
}
if generatedContent != "" {
err = gfile.PutContentsAppend(filePath, generatedContent)
if err != nil {
return err
}
isDirty = true
}
if isDirty {
utils.GoFmt(filePath)
}
return nil
}

View File

@ -13,11 +13,11 @@ import (
)
type generateStructTagInput struct {
PbPath string
OutputApiPath string
}
func (c CGenPb) generateStructTag(ctx context.Context, in generateStructTagInput) (err error) {
files, err := gfile.ScanDirFile(in.PbPath, "*.pb.go", true)
files, err := gfile.ScanDirFile(in.OutputApiPath, "*.pb.go", true)
if err != nil {
return err
}

View File

@ -258,7 +258,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
}
// Replace v1 to v2 for GoFrame.
if err = c.replaceGeneratedServiceContentGFV2(in); err != nil {
if err = utils.ReplaceGeneratedContentGFV2(in.DstFolder); err != nil {
return nil, err
}
mlog.Printf(`gofmt go files in "%s"`, in.DstFolder)
@ -268,14 +268,3 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
mlog.Print(`done!`)
return
}
func (c CGenService) replaceGeneratedServiceContentGFV2(in CGenServiceInput) (err error) {
return gfile.ReplaceDirFunc(func(path, content string) string {
if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) {
content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`)
content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`)
return content
}
return content
}, in.DstFolder, "*.go", false)
}

View File

@ -3,12 +3,13 @@ package utils
import (
"context"
"fmt"
"golang.org/x/tools/imports"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/text/gstr"
"golang.org/x/tools/imports"
)
// GoFmt formats the source file and adds or removes import statements as necessary.
@ -52,3 +53,16 @@ func IsFileDoNotEdit(filePath string) bool {
}
return gstr.Contains(gfile.GetContents(filePath), consts.DoNotEditKey)
}
// ReplaceGeneratedContentGFV2 replaces generated go content from goframe v1 to v2.
func ReplaceGeneratedContentGFV2(folderPath string) (err error) {
return gfile.ReplaceDirFunc(func(path, content string) string {
if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) {
content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`)
content = gstr.Replace(content, `"github.com/gogf/gf/`, `"github.com/gogf/gf/v2/`)
content = gstr.Replace(content, `"github.com/gogf/gf/v2/contrib/`, `"github.com/gogf/gf/contrib/`)
return content
}
return content
}, folderPath, "*.go", true)
}