feat: Support for multiple rpc service generation and rpc grouping (#1972)

* Add group & compatible flag

* Add group & compatible flag

* Support for multiple rpc service generation and rpc grouping

* Support for multiple rpc service generation and rpc grouping

* Format code

* Format code

* Add comments

* Fix unit test

* Refactor function name

* Add example & Update grpc readme

* go mod tidy

* update mod

* update mod
This commit is contained in:
anqiansong
2022-07-21 12:47:46 +08:00
committed by GitHub
parent 1b51d0ce82
commit ca3c687f1c
47 changed files with 2305 additions and 377 deletions

View File

@@ -10,17 +10,27 @@ import (
)
type ZRpcContext struct {
Src string
ProtocCmd string
// Sre is the source file of the proto.
Src string
// ProtoCmd is the command to generate proto files.
ProtocCmd string
// ProtoGenGrpcDir is the directory to store the generated proto files.
ProtoGenGrpcDir string
ProtoGenGoDir string
IsGooglePlugin bool
GoOutput string
GrpcOutput string
Output string
// ProtoGenGoDir is the directory to store the generated go files.
ProtoGenGoDir string
// IsGooglePlugin is the flag to indicate whether the proto file is generated by google plugin.
IsGooglePlugin bool
// GoOutput is the output directory of the generated go files.
GoOutput string
// GrpcOutput is the output directory of the generated grpc files.
GrpcOutput string
// Output is the output directory of the generated files.
Output string
// Multiple is the flag to indicate whether the proto file is generated in multiple mode.
Multiple bool
}
// Generate generates an rpc service, through the proto file,
// Generate generates a rpc service, through the proto file,
// code storage directory, and proto import parameters to control
// the source file and target location of the rpc service that needs to be generated
func (g *Generator) Generate(zctx *ZRpcContext) error {
@@ -45,7 +55,7 @@ func (g *Generator) Generate(zctx *ZRpcContext) error {
}
p := parser.NewDefaultProtoParser()
proto, err := p.Parse(zctx.Src)
proto, err := p.Parse(zctx.Src, zctx.Multiple)
if err != nil {
return err
}
@@ -75,22 +85,22 @@ func (g *Generator) Generate(zctx *ZRpcContext) error {
return err
}
err = g.GenLogic(dirCtx, proto, g.cfg)
err = g.GenLogic(dirCtx, proto, g.cfg, zctx)
if err != nil {
return err
}
err = g.GenServer(dirCtx, proto, g.cfg)
err = g.GenServer(dirCtx, proto, g.cfg, zctx)
if err != nil {
return err
}
err = g.GenMain(dirCtx, proto, g.cfg)
err = g.GenMain(dirCtx, proto, g.cfg, zctx)
if err != nil {
return err
}
err = g.GenCall(dirCtx, proto, g.cfg)
err = g.GenCall(dirCtx, proto, g.cfg, zctx)
console.NewColorConsole().MarkDone()

View File

@@ -12,7 +12,6 @@ import (
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stringx"
"github.com/zeromicro/go-zero/tools/goctl/rpc/execx"
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)
func TestRpcGenerate(t *testing.T) {
@@ -41,8 +40,9 @@ func TestRpcGenerate(t *testing.T) {
// case go path
t.Run("GOPATH", func(t *testing.T) {
ctx := &ZRpcContext{
Src: "./test.proto",
ProtocCmd: fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s", common, projectDir, projectDir),
Src: "./test.proto",
ProtocCmd: fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s",
common, projectDir, projectDir),
IsGooglePlugin: true,
GoOutput: projectDir,
GrpcOutput: projectDir,
@@ -53,15 +53,16 @@ func TestRpcGenerate(t *testing.T) {
_, err = execx.Run("go test "+projectName, projectDir)
if err != nil {
assert.True(t, func() bool {
return strings.Contains(err.Error(), "not in GOROOT") || strings.Contains(err.Error(), "cannot find package")
return strings.Contains(err.Error(),
"not in GOROOT") || strings.Contains(err.Error(), "cannot find package")
}())
}
})
// case go mod
t.Run("GOMOD", func(t *testing.T) {
workDir := pathx.MustTempDir()
name := filepath.Base(workDir)
workDir := projectDir
name := filepath.Base(projectDir)
_, err = execx.Run("go mod init "+name, workDir)
if err != nil {
logx.Error(err)
@@ -70,8 +71,9 @@ func TestRpcGenerate(t *testing.T) {
projectDir = filepath.Join(workDir, projectName)
ctx := &ZRpcContext{
Src: "./test.proto",
ProtocCmd: fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s", common, projectDir, projectDir),
Src: "./test.proto",
ProtocCmd: fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s",
common, projectDir, projectDir),
IsGooglePlugin: true,
GoOutput: projectDir,
GrpcOutput: projectDir,
@@ -79,31 +81,5 @@ func TestRpcGenerate(t *testing.T) {
}
err = g.Generate(ctx)
assert.Nil(t, err)
_, err = execx.Run("go test "+projectName, projectDir)
if err != nil {
assert.True(t, func() bool {
return strings.Contains(err.Error(), "not in GOROOT") || strings.Contains(err.Error(), "cannot find package")
}())
}
})
// case not in go mod and go path
t.Run("OTHER", func(t *testing.T) {
ctx := &ZRpcContext{
Src: "./test.proto",
ProtocCmd: fmt.Sprintf("protoc -I=%s test.proto --go_out=%s --go_opt=Mbase/common.proto=./base --go-grpc_out=%s", common, projectDir, projectDir),
IsGooglePlugin: true,
GoOutput: projectDir,
GrpcOutput: projectDir,
Output: projectDir,
}
err = g.Generate(ctx)
assert.Nil(t, err)
_, err = execx.Run("go test "+projectName, projectDir)
if err != nil {
assert.True(t, func() bool {
return strings.Contains(err.Error(), "not in GOROOT") || strings.Contains(err.Error(), "cannot find package")
}())
}
})
}

View File

@@ -35,9 +35,88 @@ var callTemplateText string
// GenCall generates the rpc client code, which is the entry point for the rpc service call.
// It is a layer of encapsulation for the rpc client and shields the details in the pb.
func (g *Generator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
func (g *Generator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config,
c *ZRpcContext) error {
if !c.Multiple {
return g.genCallInCompatibility(ctx, proto, cfg)
}
return g.genCallGroup(ctx, proto, cfg)
}
func (g *Generator) genCallGroup(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetCall()
service := proto.Service
head := util.GetHead(proto.Name)
for _, service := range proto.Service {
childPkg, err := dir.GetChildPackage(service.Name)
if err != nil {
return err
}
callFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name)
if err != nil {
return err
}
childDir := filepath.Base(childPkg)
filename := filepath.Join(dir.Filename, childDir, fmt.Sprintf("%s.go", callFilename))
isCallPkgSameToPbPkg := childDir == ctx.GetProtoGo().Filename
isCallPkgSameToGrpcPkg := childDir == ctx.GetProtoGo().Filename
functions, err := g.genFunction(proto.PbPackage, service, isCallPkgSameToGrpcPkg)
if err != nil {
return err
}
iFunctions, err := g.getInterfaceFuncs(proto.PbPackage, service, isCallPkgSameToGrpcPkg)
if err != nil {
return err
}
text, err := pathx.LoadTemplate(category, callTemplateFile, callTemplateText)
if err != nil {
return err
}
alias := collection.NewSet()
if !isCallPkgSameToPbPkg {
for _, item := range proto.Message {
msgName := getMessageName(*item.Message)
alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(msgName),
fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(msgName))))
}
}
pbPackage := fmt.Sprintf(`"%s"`, ctx.GetPb().Package)
protoGoPackage := fmt.Sprintf(`"%s"`, ctx.GetProtoGo().Package)
if isCallPkgSameToGrpcPkg {
pbPackage = ""
protoGoPackage = ""
}
aliasKeys := alias.KeysStr()
sort.Strings(aliasKeys)
if err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"name": callFilename,
"alias": strings.Join(aliasKeys, pathx.NL),
"head": head,
"filePackage": dir.Base,
"pbPackage": pbPackage,
"protoGoPackage": protoGoPackage,
"serviceName": stringx.From(service.Name).ToCamel(),
"functions": strings.Join(functions, pathx.NL),
"interface": strings.Join(iFunctions, pathx.NL),
}, filename, true); err != nil {
return err
}
}
return nil
}
func (g *Generator) genCallInCompatibility(ctx DirContext, proto parser.Proto,
cfg *conf.Config) error {
dir := ctx.GetCall()
service := proto.Service[0]
head := util.GetHead(proto.Name)
isCallPkgSameToPbPkg := ctx.GetCall().Filename == ctx.GetPb().Filename
isCallPkgSameToGrpcPkg := ctx.GetCall().Filename == ctx.GetProtoGo().Filename
@@ -67,7 +146,8 @@ func (g *Generator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config
if !isCallPkgSameToPbPkg {
for _, item := range proto.Message {
msgName := getMessageName(*item.Message)
alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(msgName), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(msgName))))
alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(msgName),
fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(msgName))))
}
}
@@ -79,7 +159,7 @@ func (g *Generator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config
}
aliasKeys := alias.KeysStr()
sort.Strings(aliasKeys)
err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
return util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"name": callFilename,
"alias": strings.Join(aliasKeys, pathx.NL),
"head": head,
@@ -90,7 +170,6 @@ func (g *Generator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config
"functions": strings.Join(functions, pathx.NL),
"interface": strings.Join(iFunctions, pathx.NL),
}, filename, true)
return err
}
func getMessageName(msg proto.Message) string {
@@ -115,7 +194,8 @@ func getMessageName(msg proto.Message) string {
return strings.Join(list, "_")
}
func (g *Generator) genFunction(goPackage string, service parser.Service, isCallPkgSameToGrpcPkg bool) ([]string, error) {
func (g *Generator) genFunction(goPackage string, service parser.Service,
isCallPkgSameToGrpcPkg bool) ([]string, error) {
functions := make([]string, 0)
for _, rpc := range service.RPC {
@@ -125,9 +205,11 @@ func (g *Generator) genFunction(goPackage string, service parser.Service, isCall
}
comment := parser.GetComment(rpc.Doc())
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name),
parser.CamelCase(rpc.Name), "Client")
if isCallPkgSameToGrpcPkg {
streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name),
parser.CamelCase(rpc.Name), "Client")
}
buffer, err := util.With("sharedFn").Parse(text).Execute(map[string]interface{}{
"serviceName": stringx.From(service.Name).ToCamel(),
@@ -153,19 +235,23 @@ func (g *Generator) genFunction(goPackage string, service parser.Service, isCall
return functions, nil
}
func (g *Generator) getInterfaceFuncs(goPackage string, service parser.Service, isCallPkgSameToGrpcPkg bool) ([]string, error) {
func (g *Generator) getInterfaceFuncs(goPackage string, service parser.Service,
isCallPkgSameToGrpcPkg bool) ([]string, error) {
functions := make([]string, 0)
for _, rpc := range service.RPC {
text, err := pathx.LoadTemplate(category, callInterfaceFunctionTemplateFile, callInterfaceFunctionTemplate)
text, err := pathx.LoadTemplate(category, callInterfaceFunctionTemplateFile,
callInterfaceFunctionTemplate)
if err != nil {
return nil, err
}
comment := parser.GetComment(rpc.Doc())
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name),
parser.CamelCase(rpc.Name), "Client")
if isCallPkgSameToGrpcPkg {
streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Client")
streamServer = fmt.Sprintf("%s_%s%s", parser.CamelCase(service.Name),
parser.CamelCase(rpc.Name), "Client")
}
buffer, err := util.With("interfaceFn").Parse(text).Execute(
map[string]interface{}{

View File

@@ -27,17 +27,28 @@ func (l *{{.logicName}}) {{.method}} ({{if .hasReq}}in {{.request}}{{if .stream}
var logicTemplate string
// GenLogic generates the logic file of the rpc service, which corresponds to the RPC definition items in proto.
func (g *Generator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
func (g *Generator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config,
c *ZRpcContext) error {
if !c.Multiple {
return g.genLogicInCompatibility(ctx, proto, cfg)
}
return g.genLogicGroup(ctx, proto, cfg)
}
func (g *Generator) genLogicInCompatibility(ctx DirContext, proto parser.Proto,
cfg *conf.Config) error {
dir := ctx.GetLogic()
service := proto.Service.Service.Name
for _, rpc := range proto.Service.RPC {
service := proto.Service[0].Service.Name
for _, rpc := range proto.Service[0].RPC {
logicName := fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel())
logicFilename, err := format.FileNamingFormat(cfg.NamingFormat, rpc.Name+"_logic")
if err != nil {
return err
}
filename := filepath.Join(dir.Filename, logicFilename+".go")
functions, err := g.genLogicFunction(service, proto.PbPackage, rpc)
functions, err := g.genLogicFunction(service, proto.PbPackage, logicName, rpc)
if err != nil {
return err
}
@@ -50,9 +61,10 @@ func (g *Generator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Confi
return err
}
err = util.With("logic").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"logicName": fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel()),
"functions": functions,
"imports": strings.Join(imports.KeysStr(), pathx.NL),
"logicName": fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel()),
"functions": functions,
"packageName": "logic",
"imports": strings.Join(imports.KeysStr(), pathx.NL),
}, filename, false)
if err != nil {
return err
@@ -61,16 +73,72 @@ func (g *Generator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Confi
return nil
}
func (g *Generator) genLogicFunction(serviceName, goPackage string, rpc *parser.RPC) (string, error) {
func (g *Generator) genLogicGroup(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetLogic()
for _, item := range proto.Service {
serviceName := item.Name
for _, rpc := range item.RPC {
var (
err error
filename string
logicName string
logicFilename string
packageName string
)
logicName = fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel())
childPkg, err := dir.GetChildPackage(serviceName)
if err != nil {
return err
}
serviceDir := filepath.Base(childPkg)
nameJoin := fmt.Sprintf("%s_logic", serviceName)
packageName = strings.ToLower(stringx.From(nameJoin).ToCamel())
logicFilename, err = format.FileNamingFormat(cfg.NamingFormat, rpc.Name+"_logic")
if err != nil {
return err
}
filename = filepath.Join(dir.Filename, serviceDir, logicFilename+".go")
functions, err := g.genLogicFunction(serviceName, proto.PbPackage, logicName, rpc)
if err != nil {
return err
}
imports := collection.NewSet()
imports.AddStr(fmt.Sprintf(`"%v"`, ctx.GetSvc().Package))
imports.AddStr(fmt.Sprintf(`"%v"`, ctx.GetPb().Package))
text, err := pathx.LoadTemplate(category, logicTemplateFileFile, logicTemplate)
if err != nil {
return err
}
if err = util.With("logic").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"logicName": logicName,
"functions": functions,
"packageName": packageName,
"imports": strings.Join(imports.KeysStr(), pathx.NL),
}, filename, false); err != nil {
return err
}
}
}
return nil
}
func (g *Generator) genLogicFunction(serviceName, goPackage, logicName string,
rpc *parser.RPC) (string,
error) {
functions := make([]string, 0)
text, err := pathx.LoadTemplate(category, logicFuncTemplateFileFile, logicFunctionTemplate)
if err != nil {
return "", err
}
logicName := stringx.From(rpc.Name + "_logic").ToCamel()
comment := parser.GetComment(rpc.Doc())
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(serviceName), parser.CamelCase(rpc.Name), "Server")
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(serviceName),
parser.CamelCase(rpc.Name), "Server")
buffer, err := util.With("fun").Parse(text).Execute(map[string]interface{}{
"logicName": logicName,
"method": parser.CamelCase(rpc.Name),

View File

@@ -11,14 +11,20 @@ import (
"github.com/zeromicro/go-zero/tools/goctl/util"
"github.com/zeromicro/go-zero/tools/goctl/util/format"
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
"github.com/zeromicro/go-zero/tools/goctl/util/stringx"
)
//go:embed main.tpl
var mainTemplate string
type MainServiceTemplateData struct {
Service string
ServerPkg string
Pkg string
}
// GenMain generates the main file of the rpc service, which is an rpc service program call entry
func (g *Generator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
func (g *Generator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config,
c *ZRpcContext) error {
mainFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
if err != nil {
return err
@@ -28,9 +34,35 @@ func (g *Generator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config
imports := make([]string, 0)
pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
remoteImport := fmt.Sprintf(`"%v"`, ctx.GetServer().Package)
configImport := fmt.Sprintf(`"%v"`, ctx.GetConfig().Package)
imports = append(imports, configImport, pbImport, remoteImport, svcImport)
imports = append(imports, configImport, pbImport, svcImport)
var serviceNames []MainServiceTemplateData
for _, e := range proto.Service {
var (
remoteImport string
serverPkg string
)
if !c.Multiple {
serverPkg = "server"
remoteImport = fmt.Sprintf(`"%v"`, ctx.GetServer().Package)
} else {
childPkg, err := ctx.GetServer().GetChildPackage(e.Name)
if err != nil {
return err
}
serverPkg = filepath.Base(childPkg + "Server")
remoteImport = fmt.Sprintf(`%s "%v"`, serverPkg, childPkg)
}
imports = append(imports, remoteImport)
serviceNames = append(serviceNames, MainServiceTemplateData{
Service: parser.CamelCase(e.Name),
ServerPkg: serverPkg,
Pkg: proto.PbPackage,
})
}
text, err := pathx.LoadTemplate(category, mainTemplateFile, mainTemplate)
if err != nil {
return err
@@ -42,10 +74,9 @@ func (g *Generator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config
}
return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"serviceName": etcFileName,
"imports": strings.Join(imports, pathx.NL),
"pkg": proto.PbPackage,
"serviceNew": stringx.From(proto.Service.Name).ToCamel(),
"service": parser.CamelCase(proto.Service.Name),
"serviceName": etcFileName,
"imports": strings.Join(imports, pathx.NL),
"pkg": proto.PbPackage,
"serviceNames": serviceNames,
}, fileName, false)
}

View File

@@ -18,7 +18,7 @@ import (
const functionTemplate = `
{{if .hasComment}}{{.comment}}{{end}}
func (s *{{.server}}Server) {{.method}} ({{if .notStream}}ctx context.Context,{{if .hasReq}} in {{.request}}{{end}}{{else}}{{if .hasReq}} in {{.request}},{{end}}stream {{.streamBody}}{{end}}) ({{if .notStream}}{{.response}},{{end}}error) {
l := logic.New{{.logicName}}({{if .notStream}}ctx,{{else}}stream.Context(),{{end}}s.svcCtx)
l := {{.logicPkg}}.New{{.logicName}}({{if .notStream}}ctx,{{else}}stream.Context(),{{end}}s.svcCtx)
return l.{{.method}}({{if .hasReq}}in{{if .stream}} ,stream{{end}}{{else}}{{if .stream}}stream{{end}}{{end}})
}
`
@@ -27,7 +27,85 @@ func (s *{{.server}}Server) {{.method}} ({{if .notStream}}ctx context.Context,{{
var serverTemplate string
// GenServer generates rpc server file, which is an implementation of rpc server
func (g *Generator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
func (g *Generator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config,
c *ZRpcContext) error {
if !c.Multiple {
return g.genServerInCompatibility(ctx, proto, cfg, c)
}
return g.genServerGroup(ctx, proto, cfg)
}
func (g *Generator) genServerGroup(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetServer()
for _, service := range proto.Service {
var (
serverFile string
logicImport string
)
serverFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name+"_server")
if err != nil {
return err
}
serverChildPkg, err := dir.GetChildPackage(service.Name)
if err != nil {
return err
}
logicChildPkg, err := ctx.GetLogic().GetChildPackage(service.Name)
if err != nil {
return err
}
serverDir := filepath.Base(serverChildPkg)
logicImport = fmt.Sprintf(`"%v"`, logicChildPkg)
serverFile = filepath.Join(dir.Filename, serverDir, serverFilename+".go")
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package)
imports := collection.NewSet()
imports.AddStr(logicImport, svcImport, pbImport)
head := util.GetHead(proto.Name)
funcList, err := g.genFunctions(proto.PbPackage, service, true)
if err != nil {
return err
}
text, err := pathx.LoadTemplate(category, serverTemplateFile, serverTemplate)
if err != nil {
return err
}
notStream := false
for _, rpc := range service.RPC {
if !rpc.StreamsRequest && !rpc.StreamsReturns {
notStream = true
break
}
}
if err = util.With("server").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head,
"unimplementedServer": fmt.Sprintf("%s.Unimplemented%sServer", proto.PbPackage,
stringx.From(service.Name).ToCamel()),
"server": stringx.From(service.Name).ToCamel(),
"imports": strings.Join(imports.KeysStr(), pathx.NL),
"funcs": strings.Join(funcList, pathx.NL),
"notStream": notStream,
}, serverFile, true); err != nil {
return err
}
}
return nil
}
func (g *Generator) genServerInCompatibility(ctx DirContext, proto parser.Proto,
cfg *conf.Config, c *ZRpcContext) error {
dir := ctx.GetServer()
logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -37,14 +115,14 @@ func (g *Generator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Conf
imports.AddStr(logicImport, svcImport, pbImport)
head := util.GetHead(proto.Name)
service := proto.Service
service := proto.Service[0]
serverFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name+"_server")
if err != nil {
return err
}
serverFile := filepath.Join(dir.Filename, serverFilename+".go")
funcList, err := g.genFunctions(proto.PbPackage, service)
funcList, err := g.genFunctions(proto.PbPackage, service, false)
if err != nil {
return err
}
@@ -62,30 +140,44 @@ func (g *Generator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Conf
}
}
err = util.With("server").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head,
"unimplementedServer": fmt.Sprintf("%s.Unimplemented%sServer", proto.PbPackage, stringx.From(service.Name).ToCamel()),
"server": stringx.From(service.Name).ToCamel(),
"imports": strings.Join(imports.KeysStr(), pathx.NL),
"funcs": strings.Join(funcList, pathx.NL),
"notStream": notStream,
return util.With("server").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head,
"unimplementedServer": fmt.Sprintf("%s.Unimplemented%sServer", proto.PbPackage,
stringx.From(service.Name).ToCamel()),
"server": stringx.From(service.Name).ToCamel(),
"imports": strings.Join(imports.KeysStr(), pathx.NL),
"funcs": strings.Join(funcList, pathx.NL),
"notStream": notStream,
}, serverFile, true)
return err
}
func (g *Generator) genFunctions(goPackage string, service parser.Service) ([]string, error) {
var functionList []string
func (g *Generator) genFunctions(goPackage string, service parser.Service, multiple bool) ([]string, error) {
var (
functionList []string
logicPkg string
)
for _, rpc := range service.RPC {
text, err := pathx.LoadTemplate(category, serverFuncTemplateFile, functionTemplate)
if err != nil {
return nil, err
}
var logicName string
if !multiple {
logicPkg = "logic"
logicName = fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel())
} else {
nameJoin := fmt.Sprintf("%s_logic", service.Name)
logicPkg = strings.ToLower(stringx.From(nameJoin).ToCamel())
logicName = fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel())
}
comment := parser.GetComment(rpc.Doc())
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name), parser.CamelCase(rpc.Name), "Server")
streamServer := fmt.Sprintf("%s.%s_%s%s", goPackage, parser.CamelCase(service.Name),
parser.CamelCase(rpc.Name), "Server")
buffer, err := util.With("func").Parse(text).Execute(map[string]interface{}{
"server": stringx.From(service.Name).ToCamel(),
"logicName": fmt.Sprintf("%sLogic", stringx.From(rpc.Name).ToCamel()),
"logicName": logicName,
"method": parser.CamelCase(rpc.Name),
"request": fmt.Sprintf("*%s.%s", goPackage, parser.CamelCase(rpc.RequestType)),
"response": fmt.Sprintf("*%s.%s", goPackage, parser.CamelCase(rpc.ReturnsType)),
@@ -95,6 +187,7 @@ func (g *Generator) genFunctions(goPackage string, service parser.Service) ([]st
"stream": rpc.StreamsRequest || rpc.StreamsReturns,
"notStream": !rpc.StreamsRequest && !rpc.StreamsReturns,
"streamBody": streamServer,
"logicPkg": logicPkg,
})
if err != nil {
return nil, err

View File

@@ -1,4 +1,4 @@
package logic
package {{.packageName}}
import (
"context"

View File

@@ -21,11 +21,10 @@ func main() {
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
svr := server.New{{.serviceNew}}Server(ctx)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
{{.pkg}}.Register{{.service}}Server(grpcServer, svr)
{{range .serviceNames}} {{.Pkg}}.Register{{.Service}}Server(grpcServer, {{.ServerPkg}}.New{{.Service}}Server(ctx))
{{end}}
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}

View File

@@ -43,9 +43,10 @@ type (
// Dir defines a directory
Dir struct {
Base string
Filename string
Package string
Base string
Filename string
Package string
GetChildPackage func(childPath string) (string, error)
}
defaultDirContext struct {
@@ -55,9 +56,11 @@ type (
}
)
func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, _ *conf.Config, c *ZRpcContext) (DirContext, error) {
func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, _ *conf.Config, c *ZRpcContext) (DirContext,
error) {
inner := make(map[string]Dir)
etcDir := filepath.Join(ctx.WorkDir, "etc")
clientDir := filepath.Join(ctx.WorkDir, "client")
internalDir := filepath.Join(ctx.WorkDir, "internal")
configDir := filepath.Join(internalDir, "config")
logicDir := filepath.Join(internalDir, "logic")
@@ -70,64 +73,125 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, _ *conf.Config, c *ZRpcC
protoGoDir = c.ProtoGenGoDir
}
callDir := filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name).ToCamel()))
if strings.EqualFold(proto.Service.Name, filepath.Base(proto.GoPackage)) {
callDir = filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name+"_client").ToCamel()))
getChildPackage := func(parent, childPath string) (string, error) {
child := strings.TrimPrefix(childPath, parent)
abs := filepath.Join(parent, strings.ToLower(child))
if c.Multiple {
if err := pathx.MkdirIfNotExist(abs); err != nil {
return "", err
}
}
childPath = strings.TrimPrefix(abs, ctx.Dir)
pkg := filepath.Join(ctx.Path, childPath)
return filepath.ToSlash(pkg), nil
}
if !c.Multiple {
callDir := filepath.Join(ctx.WorkDir,
strings.ToLower(stringx.From(proto.Service[0].Name).ToCamel()))
if strings.EqualFold(proto.Service[0].Name, filepath.Base(proto.GoPackage)) {
callDir = filepath.Join(ctx.WorkDir,
strings.ToLower(stringx.From(proto.Service[0].Name+"_client").ToCamel()))
}
inner[call] = Dir{
Filename: callDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path,
strings.TrimPrefix(callDir, ctx.Dir))),
Base: filepath.Base(callDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(callDir, childPath)
},
}
} else {
inner[call] = Dir{
Filename: clientDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path,
strings.TrimPrefix(clientDir, ctx.Dir))),
Base: filepath.Base(clientDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(clientDir, childPath)
},
}
}
inner[wd] = Dir{
Filename: ctx.WorkDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(ctx.WorkDir, ctx.Dir))),
Base: filepath.Base(ctx.WorkDir),
Package: filepath.ToSlash(filepath.Join(ctx.Path,
strings.TrimPrefix(ctx.WorkDir, ctx.Dir))),
Base: filepath.Base(ctx.WorkDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(ctx.WorkDir, childPath)
},
}
inner[etc] = Dir{
Filename: etcDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(etcDir, ctx.Dir))),
Base: filepath.Base(etcDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(etcDir, childPath)
},
}
inner[internal] = Dir{
Filename: internalDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(internalDir, ctx.Dir))),
Base: filepath.Base(internalDir),
Package: filepath.ToSlash(filepath.Join(ctx.Path,
strings.TrimPrefix(internalDir, ctx.Dir))),
Base: filepath.Base(internalDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(internalDir, childPath)
},
}
inner[config] = Dir{
Filename: configDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(configDir, ctx.Dir))),
Base: filepath.Base(configDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(configDir, childPath)
},
}
inner[logic] = Dir{
Filename: logicDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(logicDir, ctx.Dir))),
Base: filepath.Base(logicDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(logicDir, childPath)
},
}
inner[server] = Dir{
Filename: serverDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(serverDir, ctx.Dir))),
Base: filepath.Base(serverDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(serverDir, childPath)
},
}
inner[svc] = Dir{
Filename: svcDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(svcDir, ctx.Dir))),
Base: filepath.Base(svcDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(svcDir, childPath)
},
}
inner[pb] = Dir{
Filename: pbDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(pbDir, ctx.Dir))),
Base: filepath.Base(pbDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(pbDir, childPath)
},
}
inner[protoGo] = Dir{
Filename: protoGoDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(protoGoDir, ctx.Dir))),
Base: filepath.Base(protoGoDir),
Package: filepath.ToSlash(filepath.Join(ctx.Path,
strings.TrimPrefix(protoGoDir, ctx.Dir))),
Base: filepath.Base(protoGoDir),
GetChildPackage: func(childPath string) (string, error) {
return getChildPackage(protoGoDir, childPath)
},
}
inner[call] = Dir{
Filename: callDir,
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(callDir, ctx.Dir))),
Base: filepath.Base(callDir),
}
for _, v := range inner {
err := pathx.MkdirIfNotExist(v.Filename)
if err != nil {
@@ -151,8 +215,9 @@ func (d *defaultDirContext) SetPbDir(pbDir, grpcDir string) {
d.inner[protoGo] = Dir{
Filename: grpcDir,
Package: filepath.ToSlash(filepath.Join(d.ctx.Path, strings.TrimPrefix(grpcDir, d.ctx.Dir))),
Base: filepath.Base(grpcDir),
Package: filepath.ToSlash(filepath.Join(d.ctx.Path,
strings.TrimPrefix(grpcDir, d.ctx.Dir))),
Base: filepath.Base(grpcDir),
}
}

View File

@@ -23,18 +23,16 @@ const (
)
var templates = map[string]string{
callTemplateFile: callTemplateText,
callInterfaceFunctionTemplateFile: callInterfaceFunctionTemplate,
callFunctionTemplateFile: callFunctionTemplate,
configTemplateFileFile: configTemplate,
etcTemplateFileFile: etcTemplate,
logicTemplateFileFile: logicTemplate,
logicFuncTemplateFileFile: logicFunctionTemplate,
mainTemplateFile: mainTemplate,
serverTemplateFile: serverTemplate,
serverFuncTemplateFile: functionTemplate,
svcTemplateFile: svcTemplate,
rpcTemplateFile: rpcTemplateText,
callTemplateFile: callTemplateText,
configTemplateFileFile: configTemplate,
etcTemplateFileFile: etcTemplate,
logicTemplateFileFile: logicTemplate,
logicFuncTemplateFileFile: logicFunctionTemplate,
mainTemplateFile: mainTemplate,
serverTemplateFile: serverTemplate,
serverFuncTemplateFile: functionTemplate,
svcTemplateFile: svcTemplate,
rpcTemplateFile: rpcTemplateText,
}
// GenTemplates is the entry for command goctl template,