diff --git a/doc/goctl-rpc.md b/doc/goctl-rpc.md
index 3f3d68b9..7c5c2d50 100644
--- a/doc/goctl-rpc.md
+++ b/doc/goctl-rpc.md
@@ -7,6 +7,9 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持prot
* 简单易用
* 快速提升开发效率
* 出错率低
+* 支持基于main proto作为相对路径的import
+* 支持map、enum类型
+* 支持any类型
## 快速开始
@@ -111,14 +114,12 @@ rpc一键生成常见问题解决见 常见问题
│ └── usermodel.go
├── user.go
└── user.proto
-
```
## 准备工作
* 安装了go环境
* 安装了protoc&protoc-gen-go,并且已经设置环境变量
-* mockgen(可选,将移除)
* 更多问题请见 注意事项
## 用法
@@ -140,7 +141,6 @@ OPTIONS:
--src value, -s value the file path of the proto source file
--dir value, -d value the target path of the code,default path is "${pwd}". [option]
--service value, --srv value the name of rpc service. [option]
- --shared[已废弃] value the dir of the shared file,default path is "${pwd}/shared. [option]"
--idea whether the command execution environment is from idea plugin. [option]
```
@@ -159,13 +159,13 @@ OPTIONS:
```
则服务名称亦为user,而非proto所在文件夹名称了,这里推荐使用这种结构,可以方便在同一个服务名下建立不同类型的服务(api、rpc、mq等),便于代码管理与维护。
-* --shared[⚠️已废弃] 非必填,默认为$dir(xxx.proto)/shared,rpc client逻辑代码存放目录。
> 注意:这里的shared文件夹名称将会是代码中的package名称。
* --idea 非必填,是否为idea插件中执行,保留字段,终端执行可以忽略
-## 开发人员需要做什么
+
+### 开发人员需要做什么
关注业务代码编写,将重复性、与业务无关的工作交给goctl,生成好rpc服务代码后,开饭人员仅需要修改
@@ -173,14 +173,11 @@ OPTIONS:
* 服务中业务逻辑编写(internal/logic/xxlogic.go)
* 服务中资源上下文的编写(internal/svc/servicecontext.go)
-## 扩展
-对于需要进行rpc mock的开发人员,在安装了`mockgen`工具的前提下可以在rpc的shared文件中生成好对应的mock文件。
-
-## 注意事项
+### 注意事项
* `google.golang.org/grpc`需要降级到v1.26.0,且protoc-gen-go版本不能高于v1.3.2(see [https://github.com/grpc/grpc-go/issues/3347](https://github.com/grpc/grpc-go/issues/3347))即
-
+
```shell script
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
```
@@ -189,12 +186,76 @@ OPTIONS:
* proto不支持外部依赖包引入,message不支持inline
* 目前main文件、shared文件、handler文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有
- ```shell script
+```shell script
// Code generated by goctl. DO NOT EDIT!
// Source: xxx.proto
- ```
-
- 的标识,请注意不要将也写业务性代码写在里面。
+```
+
+的标识,请注意不要将也写业务性代码写在里面。
+
+## any和import支持
+* 支持any类型声明
+* 支持import其他proto文件
+
+ any类型固定import为`google/protobuf/any.proto`,且从${GOPATH}/src中查找,proto的import支持main proto的相对路径的import,且与proto文件对应的pb.go文件必须在proto目录中能被找到。不支持工程外的其他proto文件import。
+
+> ⚠️注意: 不支持proto嵌套import,即:被import的proto文件不支持import。
+
+### import书写格式
+import书写格式
+```golang
+// @{package_of_pb}
+import {proto_omport}
+```
+@{package_of_pb}:pb文件的真实import目录。
+{proto_omport}:proto import
+
+
+如:demo中的
+
+```golang
+// @greet/base
+import "base/base.proto";
+```
+
+工程目录结构如下
+```
+greet
+│ ├── base
+│ │ ├── base.pb.go
+│ │ └── base.proto
+│ ├── demo.proto
+│ ├── go.mod
+│ └── go.sum
+```
+
+demo
+```golang
+syntax = "proto3";
+import "google/protobuf/any.proto";
+// @greet/base
+import "base/base.proto";
+package stream;
+
+
+enum Gender{
+ UNKNOWN = 0;
+ MAN = 1;
+ WOMAN = 2;
+}
+
+message StreamResp{
+ string name = 2;
+ Gender gender = 3;
+ google.protobuf.Any details = 5;
+ base.StreamReq req = 6;
+}
+service StreamGreeter {
+ rpc greet(base.StreamReq) returns (StreamResp);
+}
+```
+
+
## 常见问题解决(go mod工程)
diff --git a/go.mod b/go.mod
index 49934070..9bb0507a 100644
--- a/go.mod
+++ b/go.mod
@@ -9,7 +9,7 @@ require (
github.com/alicebob/miniredis v2.5.0+incompatible
github.com/dchest/siphash v1.2.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
- github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819
+ github.com/emicklei/proto v1.9.0
github.com/fatih/color v1.9.0 // indirect
github.com/frankban/quicktest v1.7.2 // indirect
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
@@ -56,7 +56,7 @@ require (
golang.org/x/tools v0.0.0-20200410132612-ae9902aceb98 // indirect
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f // indirect
google.golang.org/grpc v1.29.1
- google.golang.org/protobuf v1.25.0 // indirect
+ google.golang.org/protobuf v1.25.0
gopkg.in/cheggaaa/pb.v1 v1.0.28
gopkg.in/h2non/gock.v1 v1.0.15
gopkg.in/yaml.v2 v2.2.8
diff --git a/go.sum b/go.sum
index 92e5167c..952b9131 100644
--- a/go.sum
+++ b/go.sum
@@ -50,10 +50,10 @@ github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4=
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819 h1:9778zj477h/VauD8kHbOtbytW2KGQefJ/wUGE5w+mzw=
-github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/emicklei/proto v1.9.0 h1:l0QiNT6Qs7Yj0Mb4X6dnWBQer4ebei2BFcgQLbGqUDc=
+github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
diff --git a/tools/goctl/rpc/README.md b/tools/goctl/rpc/README.md
index 010cee0d..7c5c2d50 100644
--- a/tools/goctl/rpc/README.md
+++ b/tools/goctl/rpc/README.md
@@ -7,6 +7,9 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持prot
* 简单易用
* 快速提升开发效率
* 出错率低
+* 支持基于main proto作为相对路径的import
+* 支持map、enum类型
+* 支持any类型
## 快速开始
@@ -117,7 +120,6 @@ rpc一键生成常见问题解决见 常见问题
* 安装了go环境
* 安装了protoc&protoc-gen-go,并且已经设置环境变量
-* mockgen(可选,将移除)
* 更多问题请见 注意事项
## 用法
@@ -139,7 +141,6 @@ OPTIONS:
--src value, -s value the file path of the proto source file
--dir value, -d value the target path of the code,default path is "${pwd}". [option]
--service value, --srv value the name of rpc service. [option]
- --shared[已废弃] value the dir of the shared file,default path is "${pwd}/shared. [option]"
--idea whether the command execution environment is from idea plugin. [option]
```
@@ -158,12 +159,12 @@ OPTIONS:
```
则服务名称亦为user,而非proto所在文件夹名称了,这里推荐使用这种结构,可以方便在同一个服务名下建立不同类型的服务(api、rpc、mq等),便于代码管理与维护。
-* --shared[⚠️已废弃] 非必填,默认为$dir(xxx.proto)/shared,rpc client逻辑代码存放目录。
> 注意:这里的shared文件夹名称将会是代码中的package名称。
* --idea 非必填,是否为idea插件中执行,保留字段,终端执行可以忽略
+
### 开发人员需要做什么
关注业务代码编写,将重复性、与业务无关的工作交给goctl,生成好rpc服务代码后,开饭人员仅需要修改
@@ -172,9 +173,6 @@ OPTIONS:
* 服务中业务逻辑编写(internal/logic/xxlogic.go)
* 服务中资源上下文的编写(internal/svc/servicecontext.go)
-## 扩展
-
-对于需要进行rpc mock的开发人员,在安装了`mockgen`工具的前提下可以在rpc的shared文件中生成好对应的mock文件。
### 注意事项
@@ -195,6 +193,70 @@ OPTIONS:
的标识,请注意不要将也写业务性代码写在里面。
+## any和import支持
+* 支持any类型声明
+* 支持import其他proto文件
+
+ any类型固定import为`google/protobuf/any.proto`,且从${GOPATH}/src中查找,proto的import支持main proto的相对路径的import,且与proto文件对应的pb.go文件必须在proto目录中能被找到。不支持工程外的其他proto文件import。
+
+> ⚠️注意: 不支持proto嵌套import,即:被import的proto文件不支持import。
+
+### import书写格式
+import书写格式
+```golang
+// @{package_of_pb}
+import {proto_omport}
+```
+@{package_of_pb}:pb文件的真实import目录。
+{proto_omport}:proto import
+
+
+如:demo中的
+
+```golang
+// @greet/base
+import "base/base.proto";
+```
+
+工程目录结构如下
+```
+greet
+│ ├── base
+│ │ ├── base.pb.go
+│ │ └── base.proto
+│ ├── demo.proto
+│ ├── go.mod
+│ └── go.sum
+```
+
+demo
+```golang
+syntax = "proto3";
+import "google/protobuf/any.proto";
+// @greet/base
+import "base/base.proto";
+package stream;
+
+
+enum Gender{
+ UNKNOWN = 0;
+ MAN = 1;
+ WOMAN = 2;
+}
+
+message StreamResp{
+ string name = 2;
+ Gender gender = 3;
+ google.protobuf.Any details = 5;
+ base.StreamReq req = 6;
+}
+service StreamGreeter {
+ rpc greet(base.StreamReq) returns (StreamResp);
+}
+```
+
+
+
## 常见问题解决(go mod工程)
* 错误一:
diff --git a/tools/goctl/rpc/base.pb.go b/tools/goctl/rpc/base.pb.go
new file mode 100644
index 00000000..0f2653f9
--- /dev/null
+++ b/tools/goctl/rpc/base.pb.go
@@ -0,0 +1,108 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: base.proto
+
+package base
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type IdRequest struct {
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *IdRequest) Reset() { *m = IdRequest{} }
+func (m *IdRequest) String() string { return proto.CompactTextString(m) }
+func (*IdRequest) ProtoMessage() {}
+func (*IdRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_db1b6b0986796150, []int{0}
+}
+
+func (m *IdRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_IdRequest.Unmarshal(m, b)
+}
+func (m *IdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_IdRequest.Marshal(b, m, deterministic)
+}
+func (m *IdRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_IdRequest.Merge(m, src)
+}
+func (m *IdRequest) XXX_Size() int {
+ return xxx_messageInfo_IdRequest.Size(m)
+}
+func (m *IdRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_IdRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_IdRequest proto.InternalMessageInfo
+
+func (m *IdRequest) GetId() string {
+ if m != nil {
+ return m.Id
+ }
+ return ""
+}
+
+type EmptyResponse struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
+func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
+func (*EmptyResponse) ProtoMessage() {}
+func (*EmptyResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_db1b6b0986796150, []int{1}
+}
+
+func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EmptyResponse.Unmarshal(m, b)
+}
+func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
+}
+func (m *EmptyResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EmptyResponse.Merge(m, src)
+}
+func (m *EmptyResponse) XXX_Size() int {
+ return xxx_messageInfo_EmptyResponse.Size(m)
+}
+func (m *EmptyResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
+
+func init() {
+ proto.RegisterType((*IdRequest)(nil), "base.IdRequest")
+ proto.RegisterType((*EmptyResponse)(nil), "base.EmptyResponse")
+}
+
+func init() { proto.RegisterFile("base.proto", fileDescriptor_db1b6b0986796150) }
+
+var fileDescriptor_db1b6b0986796150 = []byte{
+ // 91 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0x4a, 0x2c, 0x4e,
+ 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0xb1, 0x95, 0xa4, 0xb9, 0x38, 0x3d, 0x53,
+ 0x82, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0xf8, 0xb8, 0x98, 0x32, 0x53, 0x24, 0x18, 0x15,
+ 0x18, 0x35, 0x38, 0x83, 0x98, 0x32, 0x53, 0x94, 0xf8, 0xb9, 0x78, 0x5d, 0x73, 0x0b, 0x4a, 0x2a,
+ 0x83, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x93, 0xd8, 0xc0, 0x5a, 0x8d, 0x01, 0x01, 0x00,
+ 0x00, 0xff, 0xff, 0xe1, 0x39, 0x3c, 0x22, 0x48, 0x00, 0x00, 0x00,
+}
diff --git a/tools/goctl/rpc/base.proto b/tools/goctl/rpc/base.proto
new file mode 100644
index 00000000..501f12f7
--- /dev/null
+++ b/tools/goctl/rpc/base.proto
@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package base;
+
+message IdRequest {
+ string id = 1;
+}
+
+message EmptyResponse {
+
+}
diff --git a/tools/goctl/rpc/ctx/ctx.go b/tools/goctl/rpc/ctx/ctx.go
index 2ad5e78d..5facee42 100644
--- a/tools/goctl/rpc/ctx/ctx.go
+++ b/tools/goctl/rpc/ctx/ctx.go
@@ -11,6 +11,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/console"
"github.com/tal-tech/go-zero/tools/goctl/util/project"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
+ "github.com/tal-tech/go-zero/tools/goctl/vars"
"github.com/urfave/cli"
)
@@ -58,7 +59,7 @@ func MustCreateRpcContext(protoSrc, targetDir, serviceName string, idea bool) *R
}
serviceNameString := stringx.From(serviceName)
if serviceNameString.IsEmptyOrSpace() {
- log.Fatalln("service name is not found")
+ log.Fatalln("service name not found")
}
info, err := project.Prepare(targetDir, true)
@@ -80,7 +81,7 @@ func MustCreateRpcContext(protoSrc, targetDir, serviceName string, idea bool) *R
func MustCreateRpcContextFromCli(ctx *cli.Context) *RpcContext {
os := runtime.GOOS
switch os {
- case "darwin", "linux", "windows":
+ case vars.OsMac, vars.OsLinux, vars.OsWindows:
default:
logx.Must(fmt.Errorf("unexpected os: %s", os))
}
diff --git a/tools/goctl/rpc/execx/execx.go b/tools/goctl/rpc/execx/execx.go
index 327c5a35..039a94c7 100644
--- a/tools/goctl/rpc/execx/execx.go
+++ b/tools/goctl/rpc/execx/execx.go
@@ -6,15 +6,17 @@ import (
"fmt"
"os/exec"
"runtime"
+
+ "github.com/tal-tech/go-zero/tools/goctl/vars"
)
func Run(arg string, dir string) (string, error) {
goos := runtime.GOOS
var cmd *exec.Cmd
switch goos {
- case "darwin", "linux":
+ case vars.OsMac, vars.OsLinux:
cmd = exec.Command("sh", "-c", arg)
- case "windows":
+ case vars.OsWindows:
cmd = exec.Command("cmd.exe", "/c", arg)
default:
return "", fmt.Errorf("unexpected os: %v", goos)
diff --git a/tools/goctl/rpc/gen/gen.go b/tools/goctl/rpc/gen/gen.go
index 12c32d69..8c9f3c0a 100644
--- a/tools/goctl/rpc/gen/gen.go
+++ b/tools/goctl/rpc/gen/gen.go
@@ -1,6 +1,7 @@
package gen
import (
+ "github.com/logrusorgru/aurora"
"github.com/tal-tech/go-zero/tools/goctl/rpc/ctx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
@@ -31,10 +32,11 @@ func NewDefaultRpcGenerator(ctx *ctx.RpcContext) *defaultRpcGenerator {
}
func (g *defaultRpcGenerator) Generate() (err error) {
- g.Ctx.Info("generating code...")
+ g.Ctx.Info(aurora.Blue("-> goctl rpc reference documents: ").String() + "「https://github.com/tal-tech/go-zero/blob/master/doc/goctl-rpc.md」")
+ g.Ctx.Warning("-> generating rpc code ...")
defer func() {
if err == nil {
- g.Ctx.Success("Done.")
+ g.Ctx.MarkDone()
}
}()
err = g.createDir()
diff --git a/tools/goctl/rpc/gen/gencall.go b/tools/goctl/rpc/gen/gencall.go
index fac0ecfc..feaf534f 100644
--- a/tools/goctl/rpc/gen/gencall.go
+++ b/tools/goctl/rpc/gen/gencall.go
@@ -2,17 +2,16 @@ package gen
import (
"fmt"
- "os"
- "os/exec"
"path/filepath"
"strings"
- "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
+ "github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
const (
+ typesFilename = "types.go"
callTemplateText = `{{.head}}
//go:generate mockgen -destination ./{{.name}}_mock.go -package {{.filePackage}} -source $GOFILE
@@ -54,14 +53,17 @@ import "errors"
var errJsonConvert = errors.New("json convert error")
+{{.const}}
+
{{.types}}
`
callInterfaceFunctionTemplate = `{{if .hasComment}}{{.comment}}
{{end}}{{.method}}(ctx context.Context,in *{{.pbRequest}}) (*{{.pbResponse}},error)`
+
callFunctionTemplate = `
{{if .hasComment}}{{.comment}}{{end}}
-func (m *default{{.rpcServiceName}}) {{.method}}(ctx context.Context,in *{{.pbRequest}}) (*{{.pbResponse}}, error) {
- var request {{.package}}.{{.pbRequest}}
+func (m *default{{.rpcServiceName}}) {{.method}}(ctx context.Context,in *{{.pbRequestName}}) (*{{.pbResponse}}, error) {
+ var request {{.pbRequest}}
bts, err := jsonx.Marshal(in)
if err != nil {
return nil, errJsonConvert
@@ -108,21 +110,23 @@ func (g *defaultRpcGenerator) genCall() error {
return err
}
+ constLit, err := file.GenEnumCode()
+ if err != nil {
+ return err
+ }
+
service := file.Service[0]
callPath := filepath.Join(g.dirM[dirTarget], service.Name.Lower())
-
if err = util.MkdirIfNotExist(callPath); err != nil {
return err
}
- pbPkg := file.Package
- remotePackage := fmt.Sprintf(`%v "%v"`, pbPkg, g.mustGetPackage(dirPb))
- filename := filepath.Join(callPath, "types.go")
+ filename := filepath.Join(callPath, typesFilename)
head := util.GetHead(g.Ctx.ProtoSource)
err = util.With("types").GoFmt(true).Parse(callTemplateTypes).SaveTo(map[string]interface{}{
"head": head,
+ "const": constLit,
"filePackage": service.Name.Lower(),
- "pbPkg": pbPkg,
"serviceName": g.Ctx.ServiceName.Title(),
"lowerStartServiceName": g.Ctx.ServiceName.UnTitle(),
"types": typeCode,
@@ -131,10 +135,8 @@ func (g *defaultRpcGenerator) genCall() error {
return err
}
- _, err = exec.LookPath("mockgen")
- mockGenInstalled := err == nil
filename = filepath.Join(callPath, fmt.Sprintf("%s.go", service.Name.Lower()))
- functions, err := g.getFuncs(service)
+ functions, importList, err := g.genFunction(service)
if err != nil {
return err
}
@@ -144,72 +146,56 @@ func (g *defaultRpcGenerator) genCall() error {
return err
}
- mockFile := filepath.Join(callPath, fmt.Sprintf("%s_mock.go", service.Name.Lower()))
- _ = os.Remove(mockFile)
err = util.With("shared").GoFmt(true).Parse(callTemplateText).SaveTo(map[string]interface{}{
"name": service.Name.Lower(),
"head": head,
"filePackage": service.Name.Lower(),
- "pbPkg": pbPkg,
- "package": remotePackage,
+ "package": strings.Join(importList, util.NL),
"serviceName": service.Name.Title(),
- "functions": strings.Join(functions, "\n"),
- "interface": strings.Join(iFunctions, "\n"),
+ "functions": strings.Join(functions, util.NL),
+ "interface": strings.Join(iFunctions, util.NL),
}, filename, true)
- if err != nil {
- return err
- }
- // if mockgen is already installed, it will generate code of gomock for shared files
- // Deprecated: it will be removed
- if mockGenInstalled && g.Ctx.IsInGoEnv {
- _, _ = execx.Run(fmt.Sprintf("go generate %s", filename), "")
- }
-
- return nil
+ return err
}
-func (g *defaultRpcGenerator) getFuncs(service *parser.RpcService) ([]string, error) {
+func (g *defaultRpcGenerator) genFunction(service *parser.RpcService) ([]string, []string, error) {
file := g.ast
pkgName := file.Package
functions := make([]string, 0)
+ imports := collection.NewSet()
+ imports.AddStr(fmt.Sprintf(`%v "%v"`, pkgName, g.mustGetPackage(dirPb)))
for _, method := range service.Funcs {
- var comment string
- if len(method.Document) > 0 {
- comment = method.Document[0]
- }
+ imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
buffer, err := util.With("sharedFn").Parse(callFunctionTemplate).Execute(map[string]interface{}{
"rpcServiceName": service.Name.Title(),
"method": method.Name.Title(),
"package": pkgName,
- "pbRequest": method.InType,
- "pbResponse": method.OutType,
- "hasComment": len(method.Document) > 0,
- "comment": comment,
+ "pbRequestName": method.ParameterIn.Name,
+ "pbRequest": method.ParameterIn.Expression,
+ "pbResponse": method.ParameterOut.Name,
+ "hasComment": method.HaveDoc(),
+ "comment": method.GetDoc(),
})
if err != nil {
- return nil, err
+ return nil, nil, err
}
functions = append(functions, buffer.String())
}
- return functions, nil
+ return functions, imports.KeysStr(), nil
}
func (g *defaultRpcGenerator) getInterfaceFuncs(service *parser.RpcService) ([]string, error) {
functions := make([]string, 0)
for _, method := range service.Funcs {
- var comment string
- if len(method.Document) > 0 {
- comment = method.Document[0]
- }
buffer, err := util.With("interfaceFn").Parse(callInterfaceFunctionTemplate).Execute(
map[string]interface{}{
- "hasComment": len(method.Document) > 0,
- "comment": comment,
+ "hasComment": method.HaveDoc(),
+ "comment": method.GetDoc(),
"method": method.Name.Title(),
- "pbRequest": method.InType,
- "pbResponse": method.OutType,
+ "pbRequest": method.ParameterIn.Name,
+ "pbResponse": method.ParameterOut.Name,
})
if err != nil {
return nil, err
diff --git a/tools/goctl/rpc/gen/gendir.go b/tools/goctl/rpc/gen/gendir.go
index aa798e76..bb0bac22 100644
--- a/tools/goctl/rpc/gen/gendir.go
+++ b/tools/goctl/rpc/gen/gendir.go
@@ -6,6 +6,7 @@ import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util"
+ "github.com/tal-tech/go-zero/tools/goctl/vars"
)
// target
@@ -43,9 +44,9 @@ func (g *defaultRpcGenerator) mustGetPackage(dir string) string {
relativePath := strings.TrimPrefix(target, projectPath)
os := runtime.GOOS
switch os {
- case "windows":
+ case vars.OsWindows:
relativePath = filepath.ToSlash(relativePath)
- case "darwin", "linux":
+ case vars.OsMac, vars.OsLinux:
default:
g.Ctx.Fatalln("unexpected os: %s", os)
}
diff --git a/tools/goctl/rpc/gen/genlogic.go b/tools/goctl/rpc/gen/genlogic.go
index ca4468ca..b5e76435 100644
--- a/tools/goctl/rpc/gen/genlogic.go
+++ b/tools/goctl/rpc/gen/genlogic.go
@@ -37,10 +37,10 @@ func New{{.logicName}}(ctx context.Context,svcCtx *svc.ServiceContext) *{{.logic
{{.functions}}
`
logicFunctionTemplate = `{{if .hasComment}}{{.comment}}{{end}}
-func (l *{{.logicName}}) {{.method}} (in *{{.package}}.{{.request}}) (*{{.package}}.{{.response}}, error) {
+func (l *{{.logicName}}) {{.method}} (in {{.request}}) ({{.response}}, error) {
// todo: add your logic here and delete this line
- return &{{.package}}.{{.response}}{}, nil
+ return &{{.responseType}}{}, nil
}
`
)
@@ -53,18 +53,18 @@ func (g *defaultRpcGenerator) genLogic() error {
for _, method := range item.Funcs {
logicName := fmt.Sprintf("%slogic.go", method.Name.Lower())
filename := filepath.Join(logicPath, logicName)
- functions, err := genLogicFunction(protoPkg, method)
+ functions, importList, err := g.genLogicFunction(protoPkg, method)
if err != nil {
return err
}
imports := collection.NewSet()
- pbImport := fmt.Sprintf(`%v "%v"`, protoPkg, g.mustGetPackage(dirPb))
svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
- imports.AddStr(pbImport, svcImport)
+ imports.AddStr(svcImport)
+ imports.AddStr(importList...)
err = util.With("logic").GoFmt(true).Parse(logicTemplate).SaveTo(map[string]interface{}{
"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
"functions": functions,
- "imports": strings.Join(imports.KeysStr(), "\n"),
+ "imports": strings.Join(imports.KeysStr(), util.NL),
}, filename, false)
if err != nil {
return err
@@ -74,20 +74,26 @@ func (g *defaultRpcGenerator) genLogic() error {
return nil
}
-func genLogicFunction(packageName string, method *parser.Func) (string, error) {
+func (g *defaultRpcGenerator) genLogicFunction(packageName string, method *parser.Func) (string, []string, error) {
var functions = make([]string, 0)
+ var imports = collection.NewSet()
+ if method.ParameterIn.Package == packageName || method.ParameterOut.Package == packageName {
+ imports.AddStr(fmt.Sprintf(`%v "%v"`, packageName, g.mustGetPackage(dirPb)))
+ }
+ imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
+ imports.AddStr(g.ast.Imports[method.ParameterOut.Package])
buffer, err := util.With("fun").Parse(logicFunctionTemplate).Execute(map[string]interface{}{
- "logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
- "method": method.Name.Title(),
- "package": packageName,
- "request": method.InType,
- "response": method.OutType,
- "hasComment": len(method.Document) > 0,
- "comment": strings.Join(method.Document, "\n"),
+ "logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
+ "method": method.Name.Title(),
+ "request": method.ParameterIn.StarExpression,
+ "response": method.ParameterOut.StarExpression,
+ "responseType": method.ParameterOut.Expression,
+ "hasComment": method.HaveDoc(),
+ "comment": method.GetDoc(),
})
if err != nil {
- return "", err
+ return "", nil, err
}
functions = append(functions, buffer.String())
- return strings.Join(functions, "\n"), nil
+ return strings.Join(functions, util.NL), imports.KeysStr(), nil
}
diff --git a/tools/goctl/rpc/gen/genmain.go b/tools/goctl/rpc/gen/genmain.go
index 938c07da..33c412ac 100644
--- a/tools/goctl/rpc/gen/genmain.go
+++ b/tools/goctl/rpc/gen/genmain.go
@@ -65,7 +65,7 @@ func (g *defaultRpcGenerator) genMain() error {
"serviceName": g.Ctx.ServiceName.Lower(),
"srv": srv,
"registers": registers,
- "imports": strings.Join(imports, "\n"),
+ "imports": strings.Join(imports, util.NL),
}, fileName, true)
}
@@ -77,5 +77,5 @@ func (g *defaultRpcGenerator) genServer(pkg string, list []*parser.RpcService) (
list1 = append(list1, fmt.Sprintf("%sSrv := server.New%sServer(ctx)", name, item.Name.Title()))
list2 = append(list2, fmt.Sprintf("%s.Register%sServer(grpcServer, %sSrv)", pkg, item.Name.Title(), name))
}
- return strings.Join(list1, "\n"), strings.Join(list2, "\n")
+ return strings.Join(list1, util.NL), strings.Join(list2, util.NL)
}
diff --git a/tools/goctl/rpc/gen/genpb.go b/tools/goctl/rpc/gen/genpb.go
index 67296804..cd55d659 100644
--- a/tools/goctl/rpc/gen/genpb.go
+++ b/tools/goctl/rpc/gen/genpb.go
@@ -1,68 +1,37 @@
package gen
import (
- "errors"
+ "bytes"
"fmt"
- "io/ioutil"
"path/filepath"
"strings"
- "github.com/dsymonds/gotoc/parser"
-
- "github.com/tal-tech/go-zero/core/lang"
+ "github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
- astParser "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
- "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
+ "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
-func (g *defaultRpcGenerator) genPb() error {
- importPath, filename := filepath.Split(g.Ctx.ProtoFileSrc)
- tree, err := parser.ParseFiles([]string{filename}, []string{importPath})
- if err != nil {
- return err
- }
-
- if len(tree.Files) == 0 {
- return errors.New("proto ast parse failed")
- }
-
- file := tree.Files[0]
- if len(file.Package) == 0 {
- return errors.New("expected package, but nothing found")
- }
-
- targetStruct := make(map[string]lang.PlaceholderType)
- for _, item := range file.Messages {
- if len(item.Messages) > 0 {
- return fmt.Errorf(`line %v: unexpected inner message near: "%v""`, item.Messages[0].Position.Line, item.Messages[0].Name)
- }
-
- name := stringx.From(item.Name)
- if _, ok := targetStruct[name.Lower()]; ok {
- return fmt.Errorf("line %v: duplicate %v", item.Position.Line, name)
- }
- targetStruct[name.Lower()] = lang.Placeholder
- }
+const (
+ protocCmd = "protoc"
+ grpcPluginCmd = "--go_out=plugins=grpc"
+)
+func (g *defaultRpcGenerator) genPb() error {
pbPath := g.dirM[dirPb]
- protoFileName := filepath.Base(g.Ctx.ProtoFileSrc)
- err = g.protocGenGo(pbPath)
+ imports, containsAny, err := parser.ParseImport(g.Ctx.ProtoFileSrc)
if err != nil {
return err
}
- pbGo := strings.TrimSuffix(protoFileName, ".proto") + ".pb.go"
- pbFile := filepath.Join(pbPath, pbGo)
- bts, err := ioutil.ReadFile(pbFile)
+ err = g.protocGenGo(pbPath, imports)
if err != nil {
return err
}
-
- aspParser := astParser.NewAstParser(bts, targetStruct, g.Ctx.Console)
- ast, err := aspParser.Parse()
+ ast, err := parser.Transfer(g.Ctx.ProtoFileSrc, pbPath, imports, g.Ctx.Console)
if err != nil {
return err
}
+ ast.ContainsAny = containsAny
if len(ast.Service) == 0 {
return fmt.Errorf("service not found")
@@ -71,10 +40,35 @@ func (g *defaultRpcGenerator) genPb() error {
return nil
}
-func (g *defaultRpcGenerator) protocGenGo(target string) error {
- src := filepath.Dir(g.Ctx.ProtoFileSrc)
- sh := fmt.Sprintf(`protoc -I=%s --go_out=plugins=grpc:%s %s`, src, target, g.Ctx.ProtoFileSrc)
- stdout, err := execx.Run(sh, "")
+func (g *defaultRpcGenerator) protocGenGo(target string, imports []*parser.Import) error {
+ dir := filepath.Dir(g.Ctx.ProtoFileSrc)
+ // cmd join,see the document of proto generating class @https://developers.google.com/protocol-buffers/docs/proto3#generating
+ // template: protoc -I=${import_path} -I=${other_import_path} -I=${...} --go_out=plugins=grpc,M${pb_package_kv}, M${...} :${target_dir}
+ // eg: protoc -I=${GOPATH}/src -I=. example.proto --go_out=plugins=grpc,Mbase/base.proto=github.com/go-zero/base.proto:.
+ // note: the external import out of the project which are found in ${GOPATH}/src so far.
+
+ buffer := new(bytes.Buffer)
+ buffer.WriteString(protocCmd + " ")
+ targetImportFiltered := collection.NewSet()
+
+ for _, item := range imports {
+ buffer.WriteString(fmt.Sprintf("-I=%s ", item.OriginalDir))
+ if len(item.BridgeImport) == 0 {
+ continue
+ }
+ targetImportFiltered.AddStr(item.BridgeImport)
+
+ }
+ buffer.WriteString("-I=${GOPATH}/src ")
+ buffer.WriteString(fmt.Sprintf("-I=%s %s ", dir, g.Ctx.ProtoFileSrc))
+
+ buffer.WriteString(grpcPluginCmd)
+ if targetImportFiltered.Count() > 0 {
+ buffer.WriteString(fmt.Sprintf(",%v", strings.Join(targetImportFiltered.KeysStr(), ",")))
+ }
+ buffer.WriteString(":" + target)
+ g.Ctx.Debug("-> " + buffer.String())
+ stdout, err := execx.Run(buffer.String(), "")
if err != nil {
return err
}
diff --git a/tools/goctl/rpc/gen/genserver.go b/tools/goctl/rpc/gen/genserver.go
index 470c41a3..c9d61782 100644
--- a/tools/goctl/rpc/gen/genserver.go
+++ b/tools/goctl/rpc/gen/genserver.go
@@ -5,6 +5,7 @@ import (
"path/filepath"
"strings"
+ "github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
@@ -32,7 +33,7 @@ func New{{.server}}Server(svcCtx *svc.ServiceContext) *{{.server}}Server {
`
functionTemplate = `
{{if .hasComment}}{{.comment}}{{end}}
-func (s *{{.server}}Server) {{.method}} (ctx context.Context, in *{{.package}}.{{.request}}) (*{{.package}}.{{.response}}, error) {
+func (s *{{.server}}Server) {{.method}} (ctx context.Context, in {{.request}}) ({{.response}}, error) {
l := logic.New{{.logicName}}(ctx,s.svcCtx)
return l.{{.method}}(in)
}
@@ -45,29 +46,26 @@ func (s *{{.server}}Server) {{.method}} (ctx context.Context, in *{{.package}}.{
func (g *defaultRpcGenerator) genHandler() error {
serverPath := g.dirM[dirServer]
file := g.ast
- pkg := file.Package
- pbImport := fmt.Sprintf(`%v "%v"`, pkg, g.mustGetPackage(dirPb))
logicImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirLogic))
svcImport := fmt.Sprintf(`"%v"`, g.mustGetPackage(dirSvc))
- imports := []string{
- pbImport,
- logicImport,
- svcImport,
- }
+ imports := collection.NewSet()
+ imports.AddStr(logicImport, svcImport)
+
head := util.GetHead(g.Ctx.ProtoSource)
for _, service := range file.Service {
filename := fmt.Sprintf("%vserver.go", service.Name.Lower())
serverFile := filepath.Join(serverPath, filename)
- funcList, err := g.genFunctions(service)
+ funcList, importList, err := g.genFunctions(service)
if err != nil {
return err
}
+ imports.AddStr(importList...)
err = util.With("server").GoFmt(true).Parse(serverTemplate).SaveTo(map[string]interface{}{
"head": head,
"types": fmt.Sprintf(typeFmt, service.Name.Title()),
"server": service.Name.Title(),
- "imports": strings.Join(imports, "\n\t"),
- "funcs": strings.Join(funcList, "\n"),
+ "imports": strings.Join(imports.KeysStr(), util.NL),
+ "funcs": strings.Join(funcList, util.NL),
}, serverFile, true)
if err != nil {
return err
@@ -76,25 +74,31 @@ func (g *defaultRpcGenerator) genHandler() error {
return nil
}
-func (g *defaultRpcGenerator) genFunctions(service *parser.RpcService) ([]string, error) {
+func (g *defaultRpcGenerator) genFunctions(service *parser.RpcService) ([]string, []string, error) {
file := g.ast
pkg := file.Package
var functionList []string
+ imports := collection.NewSet()
for _, method := range service.Funcs {
+ if method.ParameterIn.Package == pkg || method.ParameterOut.Package == pkg {
+ imports.AddStr(fmt.Sprintf(`%v "%v"`, pkg, g.mustGetPackage(dirPb)))
+ }
+ imports.AddStr(g.ast.Imports[method.ParameterIn.Package])
+ imports.AddStr(g.ast.Imports[method.ParameterOut.Package])
buffer, err := util.With("func").Parse(functionTemplate).Execute(map[string]interface{}{
"server": service.Name.Title(),
"logicName": fmt.Sprintf("%sLogic", method.Name.Title()),
"method": method.Name.Title(),
"package": pkg,
- "request": method.InType,
- "response": method.OutType,
- "hasComment": len(method.Document),
- "comment": strings.Join(method.Document, "\n"),
+ "request": method.ParameterIn.StarExpression,
+ "response": method.ParameterOut.StarExpression,
+ "hasComment": method.HaveDoc(),
+ "comment": method.GetDoc(),
})
if err != nil {
- return nil, err
+ return nil, nil, err
}
functionList = append(functionList, buffer.String())
}
- return functionList, nil
+ return functionList, imports.KeysStr(), nil
}
diff --git a/tools/goctl/rpc/gen/template.go b/tools/goctl/rpc/gen/template.go
index eff5b1b7..d81c44a0 100644
--- a/tools/goctl/rpc/gen/template.go
+++ b/tools/goctl/rpc/gen/template.go
@@ -39,6 +39,8 @@ func NewRpcTemplate(out string, idea bool) *rpcTemplate {
}
func (r *rpcTemplate) MustGenerate(showState bool) {
+ r.Info("查看rpc生成请移步至「https://github.com/tal-tech/go-zero/blob/master/doc/goctl-rpc.md」")
+ r.Info("generating template...")
protoFilename := filepath.Base(r.out)
serviceName := stringx.From(strings.TrimSuffix(protoFilename, filepath.Ext(protoFilename)))
err := util.With("t").Parse(rpcTemplateText).SaveTo(map[string]string{
diff --git a/tools/goctl/rpc/gen_test.go b/tools/goctl/rpc/gen_test.go
new file mode 100644
index 00000000..e32e99b2
--- /dev/null
+++ b/tools/goctl/rpc/gen_test.go
@@ -0,0 +1,35 @@
+package base
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
+ "github.com/tal-tech/go-zero/tools/goctl/util/console"
+)
+
+func TestParseImport(t *testing.T) {
+ src, _ := filepath.Abs("./test.proto")
+ base, _ := filepath.Abs("./base.proto")
+ imports, containsAny, err := parser.ParseImport(src)
+ assert.Nil(t, err)
+ assert.Equal(t, true, containsAny)
+ assert.Equal(t, 1, len(imports))
+ assert.Equal(t, "github.com/tal-tech/go-zero/tools/goctl/rpc", imports[0].PbImportName)
+ assert.Equal(t, base, imports[0].OriginalProtoPath)
+}
+
+func TestTransfer(t *testing.T) {
+ src, _ := filepath.Abs("./test.proto")
+ abs, _ := filepath.Abs("./test")
+ imports, _, _ := parser.ParseImport(src)
+ proto, err := parser.Transfer(src, abs, imports, console.NewConsole(false))
+ assert.Nil(t, err)
+ assert.Equal(t, 1, len(proto.Service))
+ assert.Equal(t, "Greeter", proto.Service[0].Name.Source())
+ assert.Equal(t, 5, len(proto.Structure))
+ data, ok := proto.Structure["map"]
+ assert.Equal(t, true, ok)
+ assert.Equal(t, "M", data.Field[0].Name.Source())
+}
diff --git a/tools/goctl/rpc/parser/parser.go b/tools/goctl/rpc/parser/parser.go
new file mode 100644
index 00000000..16dec9fd
--- /dev/null
+++ b/tools/goctl/rpc/parser/parser.go
@@ -0,0 +1,46 @@
+package parser
+
+import (
+ "path/filepath"
+ "strings"
+
+ "github.com/tal-tech/go-zero/core/lang"
+ "github.com/tal-tech/go-zero/tools/goctl/util/console"
+)
+
+func Transfer(proto, target string, externalImport []*Import, console console.Console) (*PbAst, error) {
+ messageM := make(map[string]lang.PlaceholderType)
+ enumM := make(map[string]*Enum)
+ protoAst, err := parseProto(proto, messageM, enumM)
+ if err != nil {
+ return nil, err
+ }
+ for _, item := range externalImport {
+ err = checkImport(item.OriginalProtoPath)
+ if err != nil {
+ return nil, err
+ }
+ innerAst, err := parseProto(item.OriginalProtoPath, protoAst.Message, protoAst.Enum)
+ if err != nil {
+ return nil, err
+ }
+ for k, v := range innerAst.Message {
+ protoAst.Message[k] = v
+ }
+ for k, v := range innerAst.Enum {
+ protoAst.Enum[k] = v
+ }
+ }
+ protoAst.Import = externalImport
+ protoAst.PbSrc = filepath.Join(target, strings.TrimSuffix(filepath.Base(proto), ".proto")+".pb.go")
+ return transfer(protoAst, console)
+}
+
+func transfer(proto *Proto, console console.Console) (*PbAst, error) {
+ parser := MustNewAstParser(proto, console)
+ parse, err := parser.Parse()
+ if err != nil {
+ return nil, err
+ }
+ return parse, nil
+}
diff --git a/tools/goctl/rpc/parser/pbast.go b/tools/goctl/rpc/parser/pbast.go
index 2bcbaefd..6a5872f5 100644
--- a/tools/goctl/rpc/parser/pbast.go
+++ b/tools/goctl/rpc/parser/pbast.go
@@ -6,6 +6,7 @@ import (
"go/ast"
"go/parser"
"go/token"
+ "io/ioutil"
"sort"
"strings"
@@ -18,8 +19,9 @@ import (
const (
flagStar = "*"
+ flagDot = "."
suffixServer = "Server"
- referenceContext = "context."
+ referenceContext = "context"
unknownPrefix = "XXX_"
ignoreJsonTagExpression = `json:"-"`
)
@@ -34,19 +36,23 @@ var (
}`
fieldTemplate = `{{if .hasDoc}}{{.doc}}
{{end}}{{.name}} {{.type}} {{.tag}}{{if .hasComment}}{{.comment}}{{end}}`
+
+ anyTypeTemplate = "Any struct {\n\tTypeUrl string `json:\"typeUrl\"`\n\tValue []byte `json:\"value\"`\n}"
+
objectM = make(map[string]*Struct)
)
type (
astParser struct {
- golang []byte
filterStruct map[string]lang.PlaceholderType
+ filterEnum map[string]*Enum
console.Console
fileSet *token.FileSet
+ proto *Proto
}
Field struct {
Name stringx.String
- TypeName string
+ Type Type
JsonTag string
Document []string
Comment []string
@@ -57,13 +63,33 @@ type (
Comment []string
Field []*Field
}
+ ConstLit struct {
+ Name stringx.String
+ Document []string
+ Comment []string
+ Lit []*Lit
+ }
+ Lit struct {
+ Key string
+ Value int
+ }
+ Type struct {
+ // eg:context.Context
+ Expression string
+ // eg: *context.Context
+ StarExpression string
+ // Invoke Type Expression
+ InvokeTypeExpression string
+ // eg:context
+ Package string
+ // eg:Context
+ Name string
+ }
Func struct {
- Name stringx.String
- InType string
- InTypeName string // remove *Context,such as LoginRequest、UserRequest
- OutTypeName string // remove *Context
- OutType string
- Document []string
+ Name stringx.String
+ ParameterIn Type
+ ParameterOut Type
+ Document []string
}
RpcService struct {
Name stringx.String
@@ -71,54 +97,98 @@ type (
}
// parsing for rpc
PbAst struct {
- Package string
- // external reference
- Imports map[string]string
- Strcuts map[string]*Struct
- // rpc server's functions,not all functions
- Service []*RpcService
+ ContainsAny bool
+ Imports map[string]string
+ Structure map[string]*Struct
+ Service []*RpcService
+ *Proto
}
)
-func NewAstParser(golang []byte, filterStruct map[string]lang.PlaceholderType, log console.Console) *astParser {
+func MustNewAstParser(proto *Proto, log console.Console) *astParser {
return &astParser{
- golang: golang,
- filterStruct: filterStruct,
+ filterStruct: proto.Message,
+ filterEnum: proto.Enum,
Console: log,
fileSet: token.NewFileSet(),
+ proto: proto,
}
}
func (a *astParser) Parse() (*PbAst, error) {
- fSet := a.fileSet
- f, err := parser.ParseFile(fSet, "", a.golang, parser.ParseComments)
+ var pbAst PbAst
+ pbAst.ContainsAny = a.proto.ContainsAny
+ pbAst.Proto = a.proto
+ pbAst.Structure = make(map[string]*Struct)
+ pbAst.Imports = make(map[string]string)
+ structure, imports, services, err := a.parse(a.proto.PbSrc)
+ if err != nil {
+ return nil, err
+ }
+ dependencyStructure, err := a.parseExternalDependency()
if err != nil {
return nil, err
}
+ for k, v := range structure {
+ pbAst.Structure[k] = v
+ }
+ for k, v := range dependencyStructure {
+ pbAst.Structure[k] = v
+ }
+ for key, path := range imports {
+ pbAst.Imports[key] = path
+ }
+ pbAst.Service = append(pbAst.Service, services...)
+ return &pbAst, nil
+}
+func (a *astParser) parse(pbSrc string) (structure map[string]*Struct, imports map[string]string, services []*RpcService, retErr error) {
+ structure = make(map[string]*Struct)
+ imports = make(map[string]string)
+ data, err := ioutil.ReadFile(pbSrc)
+ if err != nil {
+ retErr = err
+ return
+ }
+ fSet := a.fileSet
+ f, err := parser.ParseFile(fSet, "", data, parser.ParseComments)
+ if err != nil {
+ retErr = err
+ return
+ }
commentMap := ast.NewCommentMap(fSet, f, f.Comments)
f.Comments = commentMap.Filter(f).Comments()
- var pbAst PbAst
- pbAst.Package = a.mustGetIndentName(f.Name)
- imports := make(map[string]string)
- for _, item := range f.Imports {
- if item == nil {
+ strucs, function := a.mustScope(f.Scope, a.mustGetIndentName(f.Name))
+ for k, v := range strucs {
+ if v == nil {
continue
}
- if item.Path == nil {
- continue
+ structure[k] = v
+ }
+ importList := f.Imports
+ for _, item := range importList {
+ name := a.mustGetIndentName(item.Name)
+ if item.Path != nil {
+ imports[name] = item.Path.Value
}
- key := a.mustGetIndentName(item.Name)
- value := item.Path.Value
- imports[key] = value
}
- structs, funcs := a.mustScope(f.Scope)
- pbAst.Imports = imports
- pbAst.Strcuts = structs
- pbAst.Service = funcs
- return &pbAst, nil
+ services = append(services, function...)
+ return
+}
+func (a *astParser) parseExternalDependency() (map[string]*Struct, error) {
+ m := make(map[string]*Struct)
+ for _, impo := range a.proto.Import {
+ ret, _, _, err := a.parse(impo.OriginalPbPath)
+ if err != nil {
+ return nil, err
+ }
+ for k, v := range ret {
+ m[k] = v
+ }
+ }
+ return m, nil
}
-func (a *astParser) mustScope(scope *ast.Scope) (map[string]*Struct, []*RpcService) {
+func (a *astParser) mustScope(scope *ast.Scope, sourcePackage string) (map[string]*Struct, []*RpcService) {
if scope == nil {
return nil, nil
}
@@ -140,7 +210,7 @@ func (a *astParser) mustScope(scope *ast.Scope) (map[string]*Struct, []*RpcServi
switch v := tp.(type) {
case *ast.StructType:
- st, err := a.parseObject(name, v)
+ st, err := a.parseObject(name, v, sourcePackage)
a.Must(err)
structs[st.Name.Lower()] = st
@@ -148,7 +218,7 @@ func (a *astParser) mustScope(scope *ast.Scope) (map[string]*Struct, []*RpcServi
if !strings.HasSuffix(name, suffixServer) {
continue
}
- list := a.mustServerFunctions(v)
+ list := a.mustServerFunctions(v, sourcePackage)
serviceList = append(serviceList, &RpcService{
Name: stringx.From(strings.TrimSuffix(name, suffixServer)),
Funcs: list,
@@ -163,7 +233,7 @@ func (a *astParser) mustScope(scope *ast.Scope) (map[string]*Struct, []*RpcServi
return targetStruct, serviceList
}
-func (a *astParser) mustServerFunctions(v *ast.InterfaceType) []*Func {
+func (a *astParser) mustServerFunctions(v *ast.InterfaceType, sourcePackage string) []*Func {
funcs := make([]*Func, 0)
methodObject := v.Methods
if methodObject == nil {
@@ -187,31 +257,27 @@ func (a *astParser) mustServerFunctions(v *ast.InterfaceType) []*Func {
}
params := v.Params
if params != nil {
- inList, err := a.parseFields(params.List, true)
+ inList, err := a.parseFields(params.List, true, sourcePackage)
a.Must(err)
for _, data := range inList {
- if strings.HasPrefix(data.TypeName, referenceContext) {
+ if data.Type.Package == referenceContext {
continue
}
- // currently,does not support external references
- item.InTypeName = data.TypeName
- item.InType = strings.TrimPrefix(data.TypeName, flagStar)
+ item.ParameterIn = data.Type
break
}
}
results := v.Results
if results != nil {
- outList, err := a.parseFields(results.List, true)
+ outList, err := a.parseFields(results.List, true, sourcePackage)
a.Must(err)
for _, data := range outList {
- if strings.HasPrefix(data.TypeName, referenceContext) {
+ if data.Type.Package == referenceContext {
continue
}
- // currently,does not support external references
- item.OutTypeName = data.TypeName
- item.OutType = strings.TrimPrefix(data.TypeName, flagStar)
+ item.ParameterOut = data.Type
break
}
}
@@ -220,7 +286,67 @@ func (a *astParser) mustServerFunctions(v *ast.InterfaceType) []*Func {
return funcs
}
-func (a *astParser) parseObject(structName string, tp *ast.StructType) (*Struct, error) {
+func (a *astParser) getFieldType(v string, sourcePackage string) Type {
+ var pkg, name, expression, starExpression, invokeTypeExpression string
+
+ if strings.Contains(v, ".") {
+ starExpression = v
+ if strings.Contains(v, "*") {
+ leftIndex := strings.Index(v, "*")
+ rightIndex := strings.Index(v, ".")
+ if leftIndex >= 0 {
+ invokeTypeExpression = v[0:leftIndex+1] + v[rightIndex+1:]
+ } else {
+ invokeTypeExpression = v[rightIndex+1:]
+ }
+ } else {
+ if strings.HasPrefix(v, "map[") || strings.HasPrefix(v, "[]") {
+ leftIndex := strings.Index(v, "]")
+ rightIndex := strings.Index(v, ".")
+ invokeTypeExpression = v[0:leftIndex+1] + v[rightIndex+1:]
+ } else {
+ rightIndex := strings.Index(v, ".")
+ invokeTypeExpression = v[rightIndex+1:]
+ }
+ }
+ } else {
+ expression = strings.TrimPrefix(v, flagStar)
+ switch v {
+ case "double", "float", "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64",
+ "bool", "string", "bytes":
+ invokeTypeExpression = v
+ break
+ default:
+ name = expression
+ invokeTypeExpression = v
+ if strings.HasPrefix(v, "map[") || strings.HasPrefix(v, "[]") {
+ starExpression = strings.ReplaceAll(v, flagStar, flagStar+sourcePackage+".")
+ } else {
+ starExpression = fmt.Sprintf("*%v.%v", sourcePackage, name)
+ invokeTypeExpression = v
+ }
+
+ }
+ }
+ expression = strings.TrimPrefix(starExpression, flagStar)
+ index := strings.LastIndex(expression, flagDot)
+ if index > 0 {
+ pkg = expression[0:index]
+ name = expression[index+1:]
+ } else {
+ pkg = sourcePackage
+ }
+
+ return Type{
+ Expression: expression,
+ StarExpression: starExpression,
+ InvokeTypeExpression: invokeTypeExpression,
+ Package: pkg,
+ Name: name,
+ }
+}
+
+func (a *astParser) parseObject(structName string, tp *ast.StructType, sourcePackage string) (*Struct, error) {
if data, ok := objectM[structName]; ok {
return data, nil
}
@@ -237,7 +363,7 @@ func (a *astParser) parseObject(structName string, tp *ast.StructType) (*Struct,
}
fieldList := fields.List
- members, err := a.parseFields(fieldList, false)
+ members, err := a.parseFields(fieldList, false, sourcePackage)
if err != nil {
return nil, err
}
@@ -245,7 +371,7 @@ func (a *astParser) parseObject(structName string, tp *ast.StructType) (*Struct,
for _, m := range members {
var field Field
field.Name = m.Name
- field.TypeName = m.TypeName
+ field.Type = m.Type
field.JsonTag = m.JsonTag
field.Document = m.Document
field.Comment = m.Comment
@@ -255,7 +381,7 @@ func (a *astParser) parseObject(structName string, tp *ast.StructType) (*Struct,
return &st, nil
}
-func (a *astParser) parseFields(fields []*ast.Field, onlyType bool) ([]*Field, error) {
+func (a *astParser) parseFields(fields []*ast.Field, onlyType bool, sourcePackage string) ([]*Field, error) {
ret := make([]*Field, 0)
for _, field := range fields {
var item Field
@@ -278,7 +404,7 @@ func (a *astParser) parseFields(fields []*ast.Field, onlyType bool) ([]*Field, e
return nil, err
}
- item.TypeName = typeName
+ item.Type = a.getFieldType(typeName, sourcePackage)
if onlyType {
ret = append(ret, &item)
continue
@@ -414,10 +540,30 @@ func (a *astParser) wrapError(pos token.Pos, format string, arg ...interface{})
return fmt.Errorf("line %v: %s", file.Line, fmt.Sprintf(format, arg...))
}
+func (f *Func) GetDoc() string {
+ return strings.Join(f.Document, util.NL)
+}
+
+func (f *Func) HaveDoc() bool {
+ return len(f.Document) > 0
+}
+
+func (a *PbAst) GenEnumCode() (string, error) {
+ var element []string
+ for _, item := range a.Enum {
+ code, err := item.GenEnumCode()
+ if err != nil {
+ return "", err
+ }
+ element = append(element, code)
+ }
+ return strings.Join(element, util.NL), nil
+}
+
func (a *PbAst) GenTypesCode() (string, error) {
types := make([]string, 0)
sts := make([]*Struct, 0)
- for _, item := range a.Strcuts {
+ for _, item := range a.Structure {
sts = append(sts, item)
}
sort.Slice(sts, func(i, j int) bool {
@@ -434,8 +580,17 @@ func (a *PbAst) GenTypesCode() (string, error) {
}
types = append(types, structCode)
}
+ types = append(types, a.genAnyCode())
+ for _, item := range a.Enum {
+ typeCode, err := item.GenEnumTypeCode()
+ if err != nil {
+ return "", err
+ }
+ types = append(types, typeCode)
+ }
+
buffer, err := util.With("type").Parse(typeTemplate).Execute(map[string]interface{}{
- "types": strings.Join(types, "\n\n"),
+ "types": strings.Join(types, util.NL+util.NL),
})
if err != nil {
return "", err
@@ -444,6 +599,13 @@ func (a *PbAst) GenTypesCode() (string, error) {
return buffer.String(), nil
}
+func (a *PbAst) genAnyCode() string {
+ if !a.ContainsAny {
+ return ""
+ }
+ return anyTypeTemplate
+}
+
func (s *Struct) genCode(containsTypeStatement bool) (string, error) {
fields := make([]string, 0)
for _, f := range s.Field {
@@ -451,10 +613,10 @@ func (s *Struct) genCode(containsTypeStatement bool) (string, error) {
if len(f.Comment) > 0 {
comment = f.Comment[0]
}
- doc = strings.Join(f.Document, "\n")
+ doc = strings.Join(f.Document, util.NL)
buffer, err := util.With(sx.Rand()).Parse(fieldTemplate).Execute(map[string]interface{}{
"name": f.Name.Title(),
- "type": f.TypeName,
+ "type": f.Type.InvokeTypeExpression,
"tag": f.JsonTag,
"hasDoc": len(f.Document) > 0,
"doc": doc,
@@ -470,7 +632,7 @@ func (s *Struct) genCode(containsTypeStatement bool) (string, error) {
buffer, err := util.With("struct").Parse(structTemplate).Execute(map[string]interface{}{
"type": containsTypeStatement,
"name": s.Name.Title(),
- "fields": strings.Join(fields, "\n"),
+ "fields": strings.Join(fields, util.NL),
})
if err != nil {
return "", err
diff --git a/tools/goctl/rpc/parser/proto.go b/tools/goctl/rpc/parser/proto.go
new file mode 100644
index 00000000..e0e6f947
--- /dev/null
+++ b/tools/goctl/rpc/parser/proto.go
@@ -0,0 +1,294 @@
+package parser
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/emicklei/proto"
+ "github.com/tal-tech/go-zero/core/collection"
+ "github.com/tal-tech/go-zero/core/lang"
+ "github.com/tal-tech/go-zero/tools/goctl/util"
+ "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
+)
+
+const (
+ AnyImport = "google/protobuf/any.proto"
+)
+
+var (
+ enumTypeTemplate = `{{.name}} int32`
+ enumTemplate = `const (
+ {{.element}}
+)`
+ enumFiledTemplate = `{{.key}} {{.name}} = {{.value}}`
+)
+
+type (
+ MessageField struct {
+ Type string
+ Name stringx.String
+ }
+ Message struct {
+ Name stringx.String
+ Element []*MessageField
+ *proto.Message
+ }
+ Enum struct {
+ Name stringx.String
+ Element []*EnumField
+ *proto.Enum
+ }
+ EnumField struct {
+ Key string
+ Value int
+ }
+
+ Proto struct {
+ Package string
+ Import []*Import
+ PbSrc string
+ ContainsAny bool
+ Message map[string]lang.PlaceholderType
+ Enum map[string]*Enum
+ }
+ Import struct {
+ ProtoImportName string
+ PbImportName string
+ OriginalDir string
+ OriginalProtoPath string
+ OriginalPbPath string
+ BridgeImport string
+ exists bool
+ //xx.proto
+ protoName string
+ // xx.pb.go
+ pbName string
+ }
+)
+
+func checkImport(src string) error {
+ r, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer r.Close()
+
+ parser := proto.NewParser(r)
+ parseRet, err := parser.Parse()
+ if err != nil {
+ return err
+ }
+ var base = filepath.Base(src)
+ proto.Walk(parseRet, proto.WithImport(func(i *proto.Import) {
+ if err != nil {
+ return
+ }
+ err = fmt.Errorf("%v:%v the external proto cannot import other proto files", base, i.Position.Line)
+ }))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+func ParseImport(src string) ([]*Import, bool, error) {
+ bridgeImportM := make(map[string]string)
+ r, err := os.Open(src)
+ if err != nil {
+ return nil, false, err
+ }
+ defer r.Close()
+
+ workDir := filepath.Dir(src)
+ parser := proto.NewParser(r)
+ parseRet, err := parser.Parse()
+ if err != nil {
+ return nil, false, err
+ }
+ protoImportSet := collection.NewSet()
+ var containsAny bool
+ proto.Walk(parseRet, proto.WithImport(func(i *proto.Import) {
+ if i.Filename == AnyImport {
+ containsAny = true
+ return
+ }
+ protoImportSet.AddStr(i.Filename)
+ if i.Comment != nil {
+ lines := i.Comment.Lines
+ for _, line := range lines {
+ line = strings.TrimSpace(line)
+ if !strings.HasPrefix(line, "@") {
+ continue
+ }
+ line = strings.TrimPrefix(line, "@")
+ bridgeImportM[i.Filename] = line
+ }
+ }
+ }))
+ var importList []*Import
+
+ for _, item := range protoImportSet.KeysStr() {
+ pb := strings.TrimSuffix(filepath.Base(item), filepath.Ext(item)) + ".pb.go"
+ var pbImportName, brideImport string
+ if v, ok := bridgeImportM[item]; ok {
+ pbImportName = v
+ brideImport = "M" + item + "=" + v
+ } else {
+ pbImportName = item
+ }
+ var impo = Import{
+ ProtoImportName: item,
+ PbImportName: pbImportName,
+ BridgeImport: brideImport,
+ }
+ protoSource := filepath.Join(workDir, item)
+ pbSource := filepath.Join(filepath.Dir(protoSource), pb)
+ if util.FileExists(protoSource) && util.FileExists(pbSource) {
+ impo.OriginalProtoPath = protoSource
+ impo.OriginalPbPath = pbSource
+ impo.OriginalDir = filepath.Dir(protoSource)
+ impo.exists = true
+ impo.protoName = filepath.Base(item)
+ impo.pbName = pb
+ } else {
+ return nil, false, fmt.Errorf("「%v」: import must be found in the relative directory of 「%v」", item, filepath.Base(src))
+ }
+ importList = append(importList, &impo)
+ }
+
+ return importList, containsAny, nil
+}
+
+func parseProto(src string, messageM map[string]lang.PlaceholderType, enumM map[string]*Enum) (*Proto, error) {
+ if !filepath.IsAbs(src) {
+ return nil, fmt.Errorf("expected absolute path,but found: %v", src)
+ }
+
+ r, err := os.Open(src)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+
+ parser := proto.NewParser(r)
+ parseRet, err := parser.Parse()
+ if err != nil {
+ return nil, err
+ }
+
+ // xx.proto
+ fileBase := filepath.Base(src)
+ var resp Proto
+
+ proto.Walk(parseRet, proto.WithPackage(func(p *proto.Package) {
+ if err != nil {
+ return
+ }
+
+ if len(resp.Package) != 0 {
+ err = fmt.Errorf("%v:%v duplicate package「%v」", fileBase, p.Position.Line, p.Name)
+ }
+
+ if len(p.Name) == 0 {
+ err = errors.New("package not found")
+ }
+
+ resp.Package = p.Name
+ }), proto.WithMessage(func(message *proto.Message) {
+ if err != nil {
+ return
+ }
+
+ for _, item := range message.Elements {
+ switch item.(type) {
+ case *proto.NormalField, *proto.MapField, *proto.Comment:
+ continue
+ default:
+ err = fmt.Errorf("%v: unsupport inline declaration", fileBase)
+ return
+ }
+ }
+ name := stringx.From(message.Name)
+ if _, ok := messageM[name.Lower()]; ok {
+ err = fmt.Errorf("%v:%v duplicate message 「%v」", fileBase, message.Position.Line, message.Name)
+ return
+ }
+
+ messageM[name.Lower()] = lang.Placeholder
+ }), proto.WithEnum(func(enum *proto.Enum) {
+ if err != nil {
+ return
+ }
+
+ var node Enum
+ node.Enum = enum
+ node.Name = stringx.From(enum.Name)
+ for _, item := range enum.Elements {
+ v, ok := item.(*proto.EnumField)
+ if !ok {
+ continue
+ }
+ node.Element = append(node.Element, &EnumField{
+ Key: v.Name,
+ Value: v.Integer,
+ })
+ }
+ if _, ok := enumM[node.Name.Lower()]; ok {
+ err = fmt.Errorf("%v:%v duplicate enum 「%v」", fileBase, node.Position.Line, node.Name.Source())
+ return
+ }
+
+ lower := stringx.From(enum.Name).Lower()
+ enumM[lower] = &node
+ }))
+
+ if err != nil {
+ return nil, err
+ }
+ resp.Message = messageM
+ resp.Enum = enumM
+
+ return &resp, nil
+}
+
+func (e *Enum) GenEnumCode() (string, error) {
+ var element []string
+ for _, item := range e.Element {
+ code, err := item.GenEnumFieldCode(e.Name.Source())
+ if err != nil {
+ return "", err
+ }
+ element = append(element, code)
+ }
+ buffer, err := util.With("enum").Parse(enumTemplate).Execute(map[string]interface{}{
+ "element": strings.Join(element, util.NL),
+ })
+ if err != nil {
+ return "", err
+ }
+ return buffer.String(), nil
+}
+
+func (e *Enum) GenEnumTypeCode() (string, error) {
+ buffer, err := util.With("enumAlias").Parse(enumTypeTemplate).Execute(map[string]interface{}{
+ "name": e.Name.Source(),
+ })
+ if err != nil {
+ return "", err
+ }
+ return buffer.String(), nil
+}
+
+func (e *EnumField) GenEnumFieldCode(parentName string) (string, error) {
+ buffer, err := util.With("enumField").Parse(enumFiledTemplate).Execute(map[string]interface{}{
+ "key": e.Key,
+ "name": parentName,
+ "value": e.Value,
+ })
+ if err != nil {
+ return "", err
+ }
+ return buffer.String(), nil
+}
diff --git a/tools/goctl/rpc/test.proto b/tools/goctl/rpc/test.proto
new file mode 100644
index 00000000..d98ec884
--- /dev/null
+++ b/tools/goctl/rpc/test.proto
@@ -0,0 +1,28 @@
+syntax = "proto3";
+// protoc -I=${GOPATH}/src -I=. test.proto --go_out=plugins=grpc,Mbase.proto=github.com/tal-tech/go-zero/tools/goctl/rpc:./test
+package test;
+// @github.com/tal-tech/go-zero/tools/goctl/rpc
+import "base.proto";
+import "google/protobuf/any.proto";
+
+message request {
+ string name = 1;
+}
+enum Gender{
+ UNKNOWN = 0;
+ MALE = 1;
+ FEMALE = 2;
+}
+message response {
+ string greet = 1;
+ google.protobuf.Any data = 2;
+
+}
+message map {
+ map m = 1;
+}
+
+service Greeter {
+ rpc greet(request) returns (response);
+ rpc idRequest(base.IdRequest)returns(base.EmptyResponse);
+}
\ No newline at end of file
diff --git a/tools/goctl/rpc/test/test.pb.go b/tools/goctl/rpc/test/test.pb.go
new file mode 100644
index 00000000..298ec08d
--- /dev/null
+++ b/tools/goctl/rpc/test/test.pb.go
@@ -0,0 +1,331 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: test.proto
+
+// protoc -I=${GOPATH}/src -I=. test.proto --go_out=plugins=grpc,Mbase.proto=github.com/tal-tech/go-zero/tools/goctl/rpc:./test
+
+package test
+
+import (
+ context "context"
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ rpc "github.com/tal-tech/go-zero/tools/goctl/rpc"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ anypb "google.golang.org/protobuf/types/known/anypb"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Gender int32
+
+const (
+ Gender_UNKNOWN Gender = 0
+ Gender_MALE Gender = 1
+ Gender_FEMALE Gender = 2
+)
+
+var Gender_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "MALE",
+ 2: "FEMALE",
+}
+
+var Gender_value = map[string]int32{
+ "UNKNOWN": 0,
+ "MALE": 1,
+ "FEMALE": 2,
+}
+
+func (x Gender) String() string {
+ return proto.EnumName(Gender_name, int32(x))
+}
+
+func (Gender) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_c161fcfdc0c3ff1e, []int{0}
+}
+
+type Request struct {
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Request) Reset() { *m = Request{} }
+func (m *Request) String() string { return proto.CompactTextString(m) }
+func (*Request) ProtoMessage() {}
+func (*Request) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c161fcfdc0c3ff1e, []int{0}
+}
+
+func (m *Request) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Request.Unmarshal(m, b)
+}
+func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Request.Marshal(b, m, deterministic)
+}
+func (m *Request) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Request.Merge(m, src)
+}
+func (m *Request) XXX_Size() int {
+ return xxx_messageInfo_Request.Size(m)
+}
+func (m *Request) XXX_DiscardUnknown() {
+ xxx_messageInfo_Request.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Request proto.InternalMessageInfo
+
+func (m *Request) GetName() string {
+ if m != nil {
+ return m.Name
+ }
+ return ""
+}
+
+type Response struct {
+ Greet string `protobuf:"bytes,1,opt,name=greet,proto3" json:"greet,omitempty"`
+ Data *anypb.Any `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Response) Reset() { *m = Response{} }
+func (m *Response) String() string { return proto.CompactTextString(m) }
+func (*Response) ProtoMessage() {}
+func (*Response) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c161fcfdc0c3ff1e, []int{1}
+}
+
+func (m *Response) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Response.Unmarshal(m, b)
+}
+func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Response.Marshal(b, m, deterministic)
+}
+func (m *Response) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Response.Merge(m, src)
+}
+func (m *Response) XXX_Size() int {
+ return xxx_messageInfo_Response.Size(m)
+}
+func (m *Response) XXX_DiscardUnknown() {
+ xxx_messageInfo_Response.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Response proto.InternalMessageInfo
+
+func (m *Response) GetGreet() string {
+ if m != nil {
+ return m.Greet
+ }
+ return ""
+}
+
+func (m *Response) GetData() *anypb.Any {
+ if m != nil {
+ return m.Data
+ }
+ return nil
+}
+
+type Map struct {
+ M map[string]string `protobuf:"bytes,1,rep,name=m,proto3" json:"m,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Map) Reset() { *m = Map{} }
+func (m *Map) String() string { return proto.CompactTextString(m) }
+func (*Map) ProtoMessage() {}
+func (*Map) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c161fcfdc0c3ff1e, []int{2}
+}
+
+func (m *Map) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Map.Unmarshal(m, b)
+}
+func (m *Map) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Map.Marshal(b, m, deterministic)
+}
+func (m *Map) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Map.Merge(m, src)
+}
+func (m *Map) XXX_Size() int {
+ return xxx_messageInfo_Map.Size(m)
+}
+func (m *Map) XXX_DiscardUnknown() {
+ xxx_messageInfo_Map.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Map proto.InternalMessageInfo
+
+func (m *Map) GetM() map[string]string {
+ if m != nil {
+ return m.M
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("test.Gender", Gender_name, Gender_value)
+ proto.RegisterType((*Request)(nil), "test.request")
+ proto.RegisterType((*Response)(nil), "test.response")
+ proto.RegisterType((*Map)(nil), "test.map")
+ proto.RegisterMapType((map[string]string)(nil), "test.map.MEntry")
+}
+
+func init() { proto.RegisterFile("test.proto", fileDescriptor_c161fcfdc0c3ff1e) }
+
+var fileDescriptor_c161fcfdc0c3ff1e = []byte{
+ // 301 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x34, 0x90, 0x4f, 0x4b, 0xc3, 0x40,
+ 0x10, 0xc5, 0xdd, 0x36, 0xa6, 0xed, 0x14, 0x35, 0x8c, 0x3d, 0xd4, 0x80, 0x52, 0x7a, 0x90, 0xa0,
+ 0xb0, 0xc5, 0xea, 0x41, 0xbc, 0xf5, 0x10, 0x8b, 0x7f, 0x5a, 0x21, 0x20, 0x1e, 0x3c, 0x6d, 0xc9,
+ 0x58, 0xc4, 0xee, 0x26, 0x6e, 0xb6, 0x42, 0xbe, 0xbd, 0x64, 0x77, 0x73, 0x7b, 0xbf, 0xd9, 0x59,
+ 0xde, 0x9b, 0x07, 0x60, 0xa8, 0x32, 0xbc, 0xd4, 0x85, 0x29, 0x30, 0x68, 0x74, 0x0c, 0x1b, 0x51,
+ 0x91, 0x9b, 0xc4, 0x67, 0xdb, 0xa2, 0xd8, 0xee, 0x68, 0x66, 0x69, 0xb3, 0xff, 0x9a, 0x09, 0x55,
+ 0xbb, 0xa7, 0xe9, 0x39, 0xf4, 0x34, 0xfd, 0xee, 0xa9, 0x32, 0x88, 0x10, 0x28, 0x21, 0x69, 0xcc,
+ 0x26, 0x2c, 0x19, 0x64, 0x56, 0x4f, 0x9f, 0xa1, 0xaf, 0xa9, 0x2a, 0x0b, 0x55, 0x11, 0x8e, 0xe0,
+ 0x70, 0xab, 0x89, 0x8c, 0x5f, 0x70, 0x80, 0x09, 0x04, 0xb9, 0x30, 0x62, 0xdc, 0x99, 0xb0, 0x64,
+ 0x38, 0x1f, 0x71, 0x67, 0xc5, 0x5b, 0x2b, 0xbe, 0x50, 0x75, 0x66, 0x37, 0xa6, 0x9f, 0xd0, 0x95,
+ 0xa2, 0xc4, 0x0b, 0x60, 0x72, 0xcc, 0x26, 0xdd, 0x64, 0x38, 0x8f, 0xb8, 0x8d, 0x2d, 0x45, 0xc9,
+ 0x57, 0xa9, 0x32, 0xba, 0xce, 0x98, 0x8c, 0xef, 0x20, 0x74, 0x80, 0x11, 0x74, 0x7f, 0xa8, 0xf6,
+ 0x76, 0x8d, 0x6c, 0x22, 0xfc, 0x89, 0xdd, 0x9e, 0xac, 0xdb, 0x20, 0x73, 0xf0, 0xd0, 0xb9, 0x67,
+ 0x57, 0xd7, 0x10, 0x2e, 0x49, 0xe5, 0xa4, 0x71, 0x08, 0xbd, 0xf7, 0xf5, 0xcb, 0xfa, 0xed, 0x63,
+ 0x1d, 0x1d, 0x60, 0x1f, 0x82, 0xd5, 0xe2, 0x35, 0x8d, 0x18, 0x02, 0x84, 0x8f, 0xa9, 0xd5, 0x9d,
+ 0x79, 0x0e, 0xbd, 0x65, 0x13, 0x9e, 0x34, 0x5e, 0xfa, 0xa3, 0xf0, 0xc8, 0x65, 0xf1, 0x65, 0xc4,
+ 0xc7, 0x2d, 0xfa, 0xe3, 0x6f, 0x60, 0xf0, 0x9d, 0x67, 0xbe, 0xa9, 0x13, 0x6e, 0xcb, 0x7d, 0x6a,
+ 0x07, 0xf1, 0xa9, 0x1b, 0xa4, 0xb2, 0x34, 0x75, 0xe6, 0xbf, 0x6c, 0x42, 0xdb, 0xc1, 0xed, 0x7f,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x48, 0x7a, 0x3b, 0x55, 0x9c, 0x01, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// GreeterClient is the client API for Greeter service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type GreeterClient interface {
+ Greet(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
+ IdRequest(ctx context.Context, in *rpc.IdRequest, opts ...grpc.CallOption) (*rpc.EmptyResponse, error)
+}
+
+type greeterClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
+ return &greeterClient{cc}
+}
+
+func (c *greeterClient) Greet(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
+ out := new(Response)
+ err := c.cc.Invoke(ctx, "/test.Greeter/greet", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *greeterClient) IdRequest(ctx context.Context, in *rpc.IdRequest, opts ...grpc.CallOption) (*rpc.EmptyResponse, error) {
+ out := new(rpc.EmptyResponse)
+ err := c.cc.Invoke(ctx, "/test.Greeter/idRequest", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// GreeterServer is the server API for Greeter service.
+type GreeterServer interface {
+ Greet(context.Context, *Request) (*Response, error)
+ IdRequest(context.Context, *rpc.IdRequest) (*rpc.EmptyResponse, error)
+}
+
+// UnimplementedGreeterServer can be embedded to have forward compatible implementations.
+type UnimplementedGreeterServer struct {
+}
+
+func (*UnimplementedGreeterServer) Greet(ctx context.Context, req *Request) (*Response, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method Greet not implemented")
+}
+func (*UnimplementedGreeterServer) IdRequest(ctx context.Context, req *rpc.IdRequest) (*rpc.EmptyResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method IdRequest not implemented")
+}
+
+func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
+ s.RegisterService(&_Greeter_serviceDesc, srv)
+}
+
+func _Greeter_Greet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Request)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(GreeterServer).Greet(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/test.Greeter/Greet",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(GreeterServer).Greet(ctx, req.(*Request))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Greeter_IdRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(rpc.IdRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(GreeterServer).IdRequest(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/test.Greeter/IdRequest",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(GreeterServer).IdRequest(ctx, req.(*rpc.IdRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+var _Greeter_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "test.Greeter",
+ HandlerType: (*GreeterServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "greet",
+ Handler: _Greeter_Greet_Handler,
+ },
+ {
+ MethodName: "idRequest",
+ Handler: _Greeter_IdRequest_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "test.proto",
+}
diff --git a/tools/goctl/util/console/console.go b/tools/goctl/util/console/console.go
index 73bd212f..e2fb09d5 100644
--- a/tools/goctl/util/console/console.go
+++ b/tools/goctl/util/console/console.go
@@ -11,9 +11,11 @@ type (
Console interface {
Success(format string, a ...interface{})
Info(format string, a ...interface{})
+ Debug(format string, a ...interface{})
Warning(format string, a ...interface{})
Error(format string, a ...interface{})
Fatalln(format string, a ...interface{})
+ MarkDone()
Must(err error)
}
colorConsole struct {
@@ -39,6 +41,11 @@ func (c *colorConsole) Info(format string, a ...interface{}) {
fmt.Println(msg)
}
+func (c *colorConsole) Debug(format string, a ...interface{}) {
+ msg := fmt.Sprintf(format, a...)
+ fmt.Println(aurora.Blue(msg))
+}
+
func (c *colorConsole) Success(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
fmt.Println(aurora.Green(msg))
@@ -59,6 +66,10 @@ func (c *colorConsole) Fatalln(format string, a ...interface{}) {
os.Exit(1)
}
+func (c *colorConsole) MarkDone() {
+ c.Success("Done.")
+}
+
func (c *colorConsole) Must(err error) {
if err != nil {
c.Fatalln("%+v", err)
@@ -74,6 +85,11 @@ func (i *ideaConsole) Info(format string, a ...interface{}) {
fmt.Println(msg)
}
+func (i *ideaConsole) Debug(format string, a ...interface{}) {
+ msg := fmt.Sprintf(format, a...)
+ fmt.Println(aurora.Blue(msg))
+}
+
func (i *ideaConsole) Success(format string, a ...interface{}) {
msg := fmt.Sprintf(format, a...)
fmt.Println("[SUCCESS]: ", msg)
@@ -94,6 +110,10 @@ func (i *ideaConsole) Fatalln(format string, a ...interface{}) {
os.Exit(1)
}
+func (i *ideaConsole) MarkDone() {
+ i.Success("Done.")
+}
+
func (i *ideaConsole) Must(err error) {
if err != nil {
i.Fatalln("%+v", err)
diff --git a/tools/goctl/util/file.go b/tools/goctl/util/file.go
index 3cfe604c..cadb5980 100644
--- a/tools/goctl/util/file.go
+++ b/tools/goctl/util/file.go
@@ -10,6 +10,10 @@ import (
"github.com/logrusorgru/aurora"
)
+const (
+ NL = "\n"
+)
+
func CreateIfNotExist(file string) (*os.File, error) {
_, err := os.Stat(file)
if !os.IsNotExist(err) {
diff --git a/tools/goctl/util/project/project.go b/tools/goctl/util/project/project.go
index b9f0b976..b363fa19 100644
--- a/tools/goctl/util/project/project.go
+++ b/tools/goctl/util/project/project.go
@@ -1,6 +1,7 @@
package project
import (
+ "fmt"
"io/ioutil"
"os"
"os/exec"
@@ -38,18 +39,18 @@ type (
func Prepare(projectDir string, checkGrpcEnv bool) (*Project, error) {
_, err := exec.LookPath(constGo)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("please install go first,reference documents:「https://golang.org/doc/install」")
}
if checkGrpcEnv {
_, err = exec.LookPath(constProtoC)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("please install protoc first,reference documents:「https://github.com/golang/protobuf」")
}
_, err = exec.LookPath(constProtoCGenGo)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("please install plugin protoc-gen-go first,reference documents:「https://github.com/golang/protobuf」")
}
}
diff --git a/tools/goctl/vars/settings.go b/tools/goctl/vars/settings.go
index c5c6a00f..9b11c60e 100644
--- a/tools/goctl/vars/settings.go
+++ b/tools/goctl/vars/settings.go
@@ -3,4 +3,7 @@ package vars
const (
ProjectName = "zero"
ProjectOpenSourceUrl = "github.com/tal-tech/go-zero"
+ OsWindows = "windows"
+ OsMac = "darwin"
+ OsLinux = "linux"
)