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:
@@ -7,7 +7,7 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持prot
|
||||
* 简单易用
|
||||
* 快速提升开发效率
|
||||
* 出错率低
|
||||
* 贴近protoc
|
||||
* 贴近 protoc
|
||||
|
||||
|
||||
## 快速开始
|
||||
@@ -24,168 +24,41 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持prot
|
||||
|
||||
执行后代码结构如下:
|
||||
|
||||
```golang
|
||||
```text
|
||||
.
|
||||
├── etc // yaml配置文件
|
||||
│ └── greet.yaml
|
||||
├── go.mod
|
||||
├── greet // pb.go文件夹①
|
||||
│ └── greet.pb.go
|
||||
├── greet.go // main函数
|
||||
├── greet.proto // proto 文件
|
||||
├── greetclient // call logic ②
|
||||
│ └── greet.go
|
||||
└── internal
|
||||
├── config // yaml配置对应的实体
|
||||
│ └── config.go
|
||||
├── logic // 业务代码
|
||||
│ └── pinglogic.go
|
||||
├── server // rpc server
|
||||
│ └── greetserver.go
|
||||
└── svc // 依赖资源
|
||||
└── servicecontext.go
|
||||
```
|
||||
|
||||
> ① pb文件夹名(老版本文件夹固定为pb)称取自于proto文件中option go_package的值最后一层级按照一定格式进行转换,若无此声明,则取自于package的值,大致代码如下:
|
||||
|
||||
```go
|
||||
if option.Name == "go_package" {
|
||||
ret.GoPackage = option.Constant.Source
|
||||
}
|
||||
...
|
||||
if len(ret.GoPackage) == 0 {
|
||||
ret.GoPackage = ret.Package.Name
|
||||
}
|
||||
ret.PbPackage = GoSanitized(filepath.Base(ret.GoPackage))
|
||||
...
|
||||
└── greet
|
||||
├── etc
|
||||
│ └── greet.yaml
|
||||
├── greet
|
||||
│ ├── greet.go
|
||||
│ ├── greet.pb.go
|
||||
│ └── greet_grpc.pb.go
|
||||
├── greet.go
|
||||
├── greet.proto
|
||||
└── internal
|
||||
├── config
|
||||
│ └── config.go
|
||||
├── logic
|
||||
│ └── pinglogic.go
|
||||
├── server
|
||||
│ └── greetserver.go
|
||||
└── svc
|
||||
└── servicecontext.go
|
||||
```
|
||||
> GoSanitized方法请参考google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71
|
||||
|
||||
> ② call 层文件夹名称取自于proto中service的名称,如该sercice的名称和pb文件夹名称相等,则会在srervice后面补充client进行区分,使pb和call分隔。
|
||||
|
||||
```go
|
||||
if strings.ToLower(proto.Service.Name) == strings.ToLower(proto.GoPackage) {
|
||||
callDir = filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name+"_client").ToCamel()))
|
||||
}
|
||||
```
|
||||
|
||||
rpc一键生成常见问题解决,见 <a href="#常见问题解决">常见问题解决</a>
|
||||
|
||||
### 方式二:通过指定proto生成rpc服务
|
||||
|
||||
* 生成proto模板
|
||||
|
||||
```Bash
|
||||
goctl rpc template -o=user.proto
|
||||
```
|
||||
|
||||
```golang
|
||||
syntax = "proto3";
|
||||
|
||||
package remote;
|
||||
|
||||
message Request {
|
||||
// 用户名
|
||||
string username = 1;
|
||||
// 用户密码
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message Response {
|
||||
// 用户名称
|
||||
string name = 1;
|
||||
// 用户性别
|
||||
string gender = 2;
|
||||
}
|
||||
|
||||
service User {
|
||||
// 登录
|
||||
rpc Login(Request)returns(Response);
|
||||
}
|
||||
```
|
||||
|
||||
* 生成rpc服务代码
|
||||
|
||||
```Bash
|
||||
goctl rpc proto -src=user.proto
|
||||
```
|
||||
|
||||
## 准备工作
|
||||
|
||||
* 安装了go环境
|
||||
* 安装了protoc&protoc-gen-go,并且已经设置环境变量
|
||||
* 更多问题请见 <a href="#注意事项">注意事项</a>
|
||||
|
||||
## 用法
|
||||
|
||||
### rpc服务生成用法
|
||||
|
||||
```Bash
|
||||
goctl rpc proto -h
|
||||
$ goctl rpc template -o=user.proto
|
||||
```
|
||||
|
||||
```Bash
|
||||
NAME:
|
||||
goctl rpc proto - generate rpc from proto
|
||||
|
||||
USAGE:
|
||||
goctl rpc proto [command options] [arguments...]
|
||||
|
||||
OPTIONS:
|
||||
--src value, -s value the file path of the proto source file
|
||||
--proto_path value, -I value native command of protoc, specify the directory in which to search for imports. [optional]
|
||||
--go_opt value native command of protoc-gen-go, specify the mapping from proto to go, eg --go_opt=proto_import=go_package_import. [optional]
|
||||
--dir value, -d value the target path of the code
|
||||
--style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]
|
||||
--idea whether the command execution environment is from idea plugin. [optional]
|
||||
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
* --src 必填,proto数据源,目前暂时支持单个proto文件生成
|
||||
* --proto_path 可选,protoc原生子命令,用于指定proto import从何处查找,可指定多个路径,如`goctl rpc -I={path1} -I={path2} ...`
|
||||
,在没有import时可不填。当前proto路径不用指定,已经内置,`-I`的详细用法请参考`protoc -h`
|
||||
* --go_opt 可选,protoc-gen-go插件原生flag,用于指定go_package
|
||||
* --dir 可选,默认为proto文件所在目录,生成代码的目标目录
|
||||
* --style 可选,指定生成文件名的命名风格
|
||||
* --idea 可选,是否为idea插件中执行,终端执行可以忽略
|
||||
|
||||
|
||||
### 开发人员需要做什么
|
||||
|
||||
关注业务代码编写,将重复性、与业务无关的工作交给goctl,生成好rpc服务代码后,开发人员仅需要修改
|
||||
|
||||
* 服务中的配置文件编写(etc/xx.json、internal/config/config.go)
|
||||
* 服务中业务逻辑编写(internal/logic/xxlogic.go)
|
||||
* 服务中资源上下文的编写(internal/svc/servicecontext.go)
|
||||
|
||||
|
||||
### 注意事项
|
||||
|
||||
* proto不支持暂多文件同时生成
|
||||
* proto不支持外部依赖包引入,message不支持inline
|
||||
* 目前main文件、shared文件、handler文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有
|
||||
|
||||
```shell script
|
||||
// Code generated by goctl. DO NOT EDIT!
|
||||
// Source: xxx.proto
|
||||
```
|
||||
|
||||
的标识,请注意不要将也写业务性代码写在里面。
|
||||
|
||||
## proto import
|
||||
* 对于rpc中的requestType和returnType必须在main proto文件定义,对于proto中的message可以像protoc一样import其他proto文件。
|
||||
|
||||
proto示例:
|
||||
|
||||
### 错误import
|
||||
|
||||
```proto
|
||||
syntax = "proto3";
|
||||
|
||||
package greet;
|
||||
|
||||
import "base/common.proto"
|
||||
package user;
|
||||
option go_package="./user";
|
||||
|
||||
message Request {
|
||||
string ping = 1;
|
||||
@@ -195,40 +68,136 @@ message Response {
|
||||
string pong = 1;
|
||||
}
|
||||
|
||||
service Greet {
|
||||
rpc Ping(base.In) returns(base.Out);// request和return 不支持import
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 正确import
|
||||
```proto
|
||||
syntax = "proto3";
|
||||
|
||||
package greet;
|
||||
|
||||
import "base/common.proto"
|
||||
|
||||
message Request {
|
||||
base.In in = 1;// 支持import
|
||||
}
|
||||
|
||||
message Response {
|
||||
base.Out out = 2;// 支持import
|
||||
}
|
||||
|
||||
service Greet {
|
||||
service User {
|
||||
rpc Ping(Request) returns(Response);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 常见问题解决(go mod工程)
|
||||
* 生成rpc服务代码
|
||||
|
||||
* 错误一:
|
||||
```text
|
||||
A required privilege is not held by the client.
|
||||
```bash
|
||||
$ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.
|
||||
```
|
||||
这个问题只有goctl 版本在`goctl.exe version 1.2.1` 以后的 Windows操作系统出现,主要原因是goctl需要以管理员身份运行,这样才能将`goctl.exe` 创建一个 `ptocot-gen-gcotl`
|
||||
的符号链接。
|
||||
|
||||
|
||||
## 用法
|
||||
|
||||
### rpc 服务生成用法
|
||||
|
||||
```Bash
|
||||
$ goctl rpc protoc -h
|
||||
Generate grpc code
|
||||
|
||||
Usage:
|
||||
goctl rpc protoc [flags]
|
||||
|
||||
Examples:
|
||||
goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.
|
||||
|
||||
Flags:
|
||||
--branch string The branch of the remote repo, it does work with --remote
|
||||
-h, --help help for protoc
|
||||
--home string The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
|
||||
-m, --multiple Generated in multiple rpc service mode
|
||||
--remote string The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
|
||||
The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure
|
||||
--style string The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] (default "gozero")
|
||||
-v, --verbose Enable log output
|
||||
--zrpc_out string The zrpc output directory
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
* --branch 指定远程仓库模板分支
|
||||
* --home 指定goctl模板根目录
|
||||
* -m, --multiple 指定生成多个rpc服务模式, 默认为 false, 如果为 false, 则只支持生成一个rpc service, 如果为 true, 则支持生成多个 rpc service,且多个 rpc service 会分组。
|
||||
* --style 指定文件输出格式
|
||||
* -v, --verbose 显示日志
|
||||
* --zrpc_out 指定zrpc输出目录
|
||||
|
||||
> ## --multiple
|
||||
> 是否开启多个 rpc service 生成,如果开启,则满足一下新特性
|
||||
> 1. 支持 1 到多个 rpc service
|
||||
> 2. 生成 rpc 服务会按照服务名称分组(尽管只有一个 rpc service)
|
||||
> 3. rpc client 的文件目录变更为固定名称 `client`
|
||||
>
|
||||
> 如果不开启,则和旧版本 rpc 生成逻辑一样(兼容)
|
||||
> 1. 有且只能有一个 rpc service
|
||||
|
||||
|
||||
## rpc 服务生成 example
|
||||
详情见 [example/rpc](https://github.com/zeromicro/go-zero/tree/master/tools/goctl/example)
|
||||
|
||||
## --multiple 为 true 和 false 的目录区别
|
||||
源 proto 文件
|
||||
|
||||
```protobuf
|
||||
syntax = "proto3";
|
||||
|
||||
package hello;
|
||||
|
||||
option go_package = "./hello";
|
||||
|
||||
message HelloReq {
|
||||
string in = 1;
|
||||
}
|
||||
|
||||
message HelloResp {
|
||||
string msg = 1;
|
||||
}
|
||||
|
||||
service Greet {
|
||||
rpc SayHello(HelloReq) returns (HelloResp);
|
||||
}
|
||||
```
|
||||
|
||||
### --multiple=true
|
||||
|
||||
```text
|
||||
hello
|
||||
├── client // 区别1:rpc client 目录固定为 client 名称
|
||||
│ └── greet // 区别2:会按照 rpc service 名称分组
|
||||
│ └── greet.go
|
||||
├── etc
|
||||
│ └── hello.yaml
|
||||
├── hello.go
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── greet // 区别2:会按照 rpc service 名称分组
|
||||
│ │ └── sayhellologic.go
|
||||
│ ├── server
|
||||
│ │ └── greet // 区别2:会按照 rpc service 名称分组
|
||||
│ │ └── greetserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
└── hello
|
||||
├── hello.pb.go
|
||||
└── hello_grpc.pb.go
|
||||
```
|
||||
|
||||
### --multiple=false (旧版本目录,向后兼容)
|
||||
```text
|
||||
hello
|
||||
├── etc
|
||||
│ └── hello.yaml
|
||||
├── greet
|
||||
│ └── greet.go
|
||||
├── hello.go
|
||||
├── internal
|
||||
│ ├── config
|
||||
│ │ └── config.go
|
||||
│ ├── logic
|
||||
│ │ └── sayhellologic.go
|
||||
│ ├── server
|
||||
│ │ └── greetserver.go
|
||||
│ └── svc
|
||||
│ └── servicecontext.go
|
||||
└── pb
|
||||
└── hello
|
||||
├── hello.pb.go
|
||||
└── hello_grpc.pb.go
|
||||
```
|
||||
@@ -42,6 +42,8 @@ var (
|
||||
VarBoolIdea bool
|
||||
// VarBoolVerbose describes whether verbose.
|
||||
VarBoolVerbose bool
|
||||
// VarBoolMultiple describes whether support generating multiple rpc services or not.
|
||||
VarBoolMultiple bool
|
||||
)
|
||||
|
||||
// RPCNew is to generate rpc greet service, this greet service can speed
|
||||
|
||||
@@ -95,6 +95,7 @@ func ZRPC(_ *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
var ctx generator.ZRpcContext
|
||||
ctx.Multiple = VarBoolMultiple
|
||||
ctx.Src = source
|
||||
ctx.GoOutput = goOut
|
||||
ctx.GrpcOutput = grpcOut
|
||||
|
||||
@@ -37,25 +37,37 @@ var (
|
||||
|
||||
func init() {
|
||||
Cmd.Flags().StringVar(&cli.VarStringOutput, "o", "", "Output a sample proto file")
|
||||
Cmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority")
|
||||
Cmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure")
|
||||
Cmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote")
|
||||
Cmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path of "+
|
||||
"the template, --home and --remote cannot be set at the same time, if they are, --remote has"+
|
||||
" higher priority")
|
||||
Cmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git repo"+
|
||||
" of the template, --home and --remote cannot be set at the same time, if they are, --remote"+
|
||||
" has higher priority\n\tThe git repo directory must be consistent with the "+
|
||||
"https://github.com/zeromicro/go-zero-template directory structure")
|
||||
Cmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch of the "+
|
||||
"remote repo, it does work with --remote")
|
||||
|
||||
newCmd.Flags().StringSliceVar(&cli.VarStringSliceGoOpt, "go_opt", nil, "")
|
||||
newCmd.Flags().StringSliceVar(&cli.VarStringSliceGoGRPCOpt, "go-grpc_opt", nil, "")
|
||||
newCmd.Flags().StringVar(&cli.VarStringStyle, "style", "gozero", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]")
|
||||
newCmd.Flags().BoolVar(&cli.VarBoolIdea, "idea", false, "Whether the command execution environment is from idea plugin.")
|
||||
newCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path of the template, "+
|
||||
"--home and --remote cannot be set at the same time, if they are, --remote has higher priority")
|
||||
newCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git repo of the template, "+
|
||||
"--home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo "+
|
||||
"directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure")
|
||||
newCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch of the remote repo, it "+
|
||||
"does work with --remote")
|
||||
newCmd.Flags().StringVar(&cli.VarStringStyle, "style", "gozero", "The file "+
|
||||
"naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]")
|
||||
newCmd.Flags().BoolVar(&cli.VarBoolIdea, "idea", false, "Whether the command "+
|
||||
"execution environment is from idea plugin.")
|
||||
newCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path "+
|
||||
"of the template, --home and --remote cannot be set at the same time, if they are, --remote "+
|
||||
"has higher priority")
|
||||
newCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git "+
|
||||
"repo of the template, --home and --remote cannot be set at the same time, if they are, "+
|
||||
"--remote has higher priority\n\tThe git repo directory must be consistent with the "+
|
||||
"https://github.com/zeromicro/go-zero-template directory structure")
|
||||
newCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "",
|
||||
"The branch of the remote repo, it does work with --remote")
|
||||
newCmd.Flags().BoolVarP(&cli.VarBoolVerbose, "verbose", "v", false, "Enable log output")
|
||||
newCmd.Flags().MarkHidden("go_opt")
|
||||
newCmd.Flags().MarkHidden("go-grpc_opt")
|
||||
|
||||
protocCmd.Flags().BoolVarP(&cli.VarBoolMultiple, "multiple", "m", false,
|
||||
"Generated in multiple rpc service mode")
|
||||
protocCmd.Flags().StringSliceVar(&cli.VarStringSliceGoOut, "go_out", nil, "")
|
||||
protocCmd.Flags().StringSliceVar(&cli.VarStringSliceGoGRPCOut, "go-grpc_out", nil, "")
|
||||
protocCmd.Flags().StringSliceVar(&cli.VarStringSliceGoOpt, "go_opt", nil, "")
|
||||
@@ -63,14 +75,17 @@ func init() {
|
||||
protocCmd.Flags().StringSliceVar(&cli.VarStringSlicePlugin, "plugin", nil, "")
|
||||
protocCmd.Flags().StringSliceVarP(&cli.VarStringSliceProtoPath, "proto_path", "I", nil, "")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringZRPCOut, "zrpc_out", "", "The zrpc output directory")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringStyle, "style", "gozero", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path of the template, "+
|
||||
"--home and --remote cannot be set at the same time, if they are, --remote has higher priority")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git repo of the template, "+
|
||||
"--home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo "+
|
||||
"directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch of the remote repo, it "+
|
||||
"does work with --remote")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringStyle, "style", "gozero", "The file "+
|
||||
"naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home "+
|
||||
"path of the template, --home and --remote cannot be set at the same time, if they are, "+
|
||||
"--remote has higher priority")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote "+
|
||||
"git repo of the template, --home and --remote cannot be set at the same time, if they are, "+
|
||||
"--remote has higher priority\n\tThe git repo directory must be consistent with the "+
|
||||
"https://github.com/zeromicro/go-zero-template directory structure")
|
||||
protocCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "",
|
||||
"The branch of the remote repo, it does work with --remote")
|
||||
protocCmd.Flags().BoolVarP(&cli.VarBoolVerbose, "verbose", "v", false, "Enable log output")
|
||||
protocCmd.Flags().MarkHidden("go_out")
|
||||
protocCmd.Flags().MarkHidden("go-grpc_out")
|
||||
@@ -80,9 +95,15 @@ func init() {
|
||||
protocCmd.Flags().MarkHidden("proto_path")
|
||||
|
||||
templateCmd.Flags().StringVar(&cli.VarStringOutput, "o", "", "Output a sample proto file")
|
||||
templateCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority")
|
||||
templateCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure")
|
||||
templateCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote")
|
||||
templateCmd.Flags().StringVar(&cli.VarStringHome, "home", "", "The goctl home"+
|
||||
" path of the template, --home and --remote cannot be set at the same time, if they are, "+
|
||||
"--remote has higher priority")
|
||||
templateCmd.Flags().StringVar(&cli.VarStringRemote, "remote", "", "The remote "+
|
||||
"git repo of the template, --home and --remote cannot be set at the same time, if they are, "+
|
||||
"--remote has higher priority\n\tThe git repo directory must be consistent with the "+
|
||||
"https://github.com/zeromicro/go-zero-template directory structure")
|
||||
templateCmd.Flags().StringVar(&cli.VarStringBranch, "branch", "", "The branch"+
|
||||
" of the remote repo, it does work with --remote")
|
||||
|
||||
Cmd.AddCommand(newCmd)
|
||||
Cmd.AddCommand(protocCmd)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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")
|
||||
}())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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{}{
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package logic
|
||||
package {{.packageName}}
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -14,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// DefaultProtoParser types a empty struct
|
||||
// DefaultProtoParser types an empty struct
|
||||
DefaultProtoParser struct{}
|
||||
)
|
||||
|
||||
@@ -25,7 +23,7 @@ func NewDefaultProtoParser() *DefaultProtoParser {
|
||||
|
||||
// Parse provides to parse the proto file into a golang structure,
|
||||
// which is convenient for subsequent rpc generation and use
|
||||
func (p *DefaultProtoParser) Parse(src string) (Proto, error) {
|
||||
func (p *DefaultProtoParser) Parse(src string, multiple ...bool) (Proto, error) {
|
||||
var ret Proto
|
||||
|
||||
abs, err := filepath.Abs(src)
|
||||
@@ -45,7 +43,7 @@ func (p *DefaultProtoParser) Parse(src string) (Proto, error) {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
var serviceList []Service
|
||||
var serviceList Services
|
||||
proto.Walk(
|
||||
set,
|
||||
proto.WithImport(func(i *proto.Import) {
|
||||
@@ -76,31 +74,18 @@ func (p *DefaultProtoParser) Parse(src string) (Proto, error) {
|
||||
}
|
||||
}),
|
||||
)
|
||||
if len(serviceList) == 0 {
|
||||
return ret, errors.New("rpc service not found")
|
||||
if err = serviceList.validate(abs, multiple...); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if len(serviceList) > 1 {
|
||||
return ret, errors.New("only one service expected")
|
||||
}
|
||||
service := serviceList[0]
|
||||
name := filepath.Base(abs)
|
||||
|
||||
for _, rpc := range service.RPC {
|
||||
if strings.Contains(rpc.RequestType, ".") {
|
||||
return ret, fmt.Errorf("line %v:%v, request type must defined in %s", rpc.Position.Line, rpc.Position.Column, name)
|
||||
}
|
||||
if strings.Contains(rpc.ReturnsType, ".") {
|
||||
return ret, fmt.Errorf("line %v:%v, returns type must defined in %s", rpc.Position.Line, rpc.Position.Column, name)
|
||||
}
|
||||
}
|
||||
if len(ret.GoPackage) == 0 {
|
||||
ret.GoPackage = ret.Package.Name
|
||||
}
|
||||
|
||||
ret.PbPackage = GoSanitized(filepath.Base(ret.GoPackage))
|
||||
ret.Src = abs
|
||||
ret.Name = name
|
||||
ret.Service = service
|
||||
ret.Name = filepath.Base(abs)
|
||||
ret.Service = serviceList
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -19,17 +19,22 @@ func TestDefaultProtoParse(t *testing.T) {
|
||||
assert.Equal(t, "test", data.Package.Name)
|
||||
assert.Equal(t, true, data.GoPackage == "go")
|
||||
assert.Equal(t, true, data.PbPackage == "_go")
|
||||
assert.Equal(t, []string{"Inline", "Inner", "TestMessage", "TestReply", "TestReq"}, func() []string {
|
||||
var list []string
|
||||
for _, item := range data.Message {
|
||||
list = append(list, item.Name)
|
||||
}
|
||||
sort.Strings(list)
|
||||
return list
|
||||
}())
|
||||
assert.Equal(t, []string{"Inline", "Inner", "TestMessage", "TestReply", "TestReq"},
|
||||
func() []string {
|
||||
var list []string
|
||||
for _, item := range data.Message {
|
||||
list = append(list, item.Name)
|
||||
}
|
||||
sort.Strings(list)
|
||||
return list
|
||||
}())
|
||||
|
||||
assert.Equal(t, true, func() bool {
|
||||
s := data.Service
|
||||
if len(data.Service) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
s := data.Service[0]
|
||||
if s.Name != "TestService" {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@ type Proto struct {
|
||||
GoPackage string
|
||||
Import []Import
|
||||
Message []Message
|
||||
Service Service
|
||||
Service Services
|
||||
}
|
||||
|
||||
@@ -1,10 +1,54 @@
|
||||
package parser
|
||||
|
||||
import "github.com/emicklei/proto"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
// Service describes the rpc service, which is the relevant
|
||||
// content after the translation of the proto file
|
||||
type Service struct {
|
||||
*proto.Service
|
||||
RPC []*RPC
|
||||
"github.com/emicklei/proto"
|
||||
)
|
||||
|
||||
type (
|
||||
// Services is a slice of Service.
|
||||
Services []Service
|
||||
|
||||
// Service describes the rpc service, which is the relevant
|
||||
// content after the translation of the proto file
|
||||
Service struct {
|
||||
*proto.Service
|
||||
RPC []*RPC
|
||||
}
|
||||
)
|
||||
|
||||
func (s Services) validate(filename string, multipleOpt ...bool) error {
|
||||
if len(s) == 0 {
|
||||
return errors.New("rpc service not found")
|
||||
}
|
||||
|
||||
var multiple bool
|
||||
for _, c := range multipleOpt {
|
||||
multiple = c
|
||||
}
|
||||
|
||||
if !multiple && len(s) > 1 {
|
||||
return errors.New("only one service expected")
|
||||
}
|
||||
|
||||
name := filepath.Base(filename)
|
||||
for _, service := range s {
|
||||
for _, rpc := range service.RPC {
|
||||
if strings.Contains(rpc.RequestType, ".") {
|
||||
return fmt.Errorf("line %v:%v, request type must defined in %s",
|
||||
rpc.Position.Line,
|
||||
rpc.Position.Column, name)
|
||||
}
|
||||
if strings.Contains(rpc.ReturnsType, ".") {
|
||||
return fmt.Errorf("line %v:%v, returns type must defined in %s",
|
||||
rpc.Position.Line,
|
||||
rpc.Position.Column, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user