mirror of
https://github.com/zeromicro/go-zero.git
synced 2025-01-23 09:00:20 +08:00
Goctl rpc patch (#117)
* remove mock generation * add: proto project import * update document * remove mock generation * add: proto project import * update document * remove NL * update document * optimize code * add test * add test
This commit is contained in:
parent
c32759d735
commit
0a9c427443
@ -7,6 +7,9 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持prot
|
||||
* 简单易用
|
||||
* 快速提升开发效率
|
||||
* 出错率低
|
||||
* 支持基于main proto作为相对路径的import
|
||||
* 支持map、enum类型
|
||||
* 支持any类型
|
||||
|
||||
## 快速开始
|
||||
|
||||
@ -111,14 +114,12 @@ rpc一键生成常见问题解决见 <a href="#常见问题解决">常见问题
|
||||
│ └── usermodel.go
|
||||
├── user.go
|
||||
└── user.proto
|
||||
|
||||
```
|
||||
|
||||
## 准备工作
|
||||
|
||||
* 安装了go环境
|
||||
* 安装了protoc&protoc-gen-go,并且已经设置环境变量
|
||||
* mockgen(可选,将移除)
|
||||
* 更多问题请见 <a href="#注意事项">注意事项</a>
|
||||
|
||||
## 用法
|
||||
@ -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工程)
|
||||
|
||||
|
4
go.mod
4
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
|
||||
|
4
go.sum
4
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=
|
||||
|
@ -7,6 +7,9 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持prot
|
||||
* 简单易用
|
||||
* 快速提升开发效率
|
||||
* 出错率低
|
||||
* 支持基于main proto作为相对路径的import
|
||||
* 支持map、enum类型
|
||||
* 支持any类型
|
||||
|
||||
## 快速开始
|
||||
|
||||
@ -117,7 +120,6 @@ rpc一键生成常见问题解决见 <a href="#常见问题解决">常见问题
|
||||
|
||||
* 安装了go环境
|
||||
* 安装了protoc&protoc-gen-go,并且已经设置环境变量
|
||||
* mockgen(可选,将移除)
|
||||
* 更多问题请见 <a href="#注意事项">注意事项</a>
|
||||
|
||||
## 用法
|
||||
@ -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工程)
|
||||
|
||||
* 错误一:
|
||||
|
108
tools/goctl/rpc/base.pb.go
Normal file
108
tools/goctl/rpc/base.pb.go
Normal file
@ -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,
|
||||
}
|
11
tools/goctl/rpc/base.proto
Normal file
11
tools/goctl/rpc/base.proto
Normal file
@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package base;
|
||||
|
||||
message IdRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message EmptyResponse {
|
||||
|
||||
}
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"
|
||||
)
|
||||
|
||||
const (
|
||||
protocCmd = "protoc"
|
||||
grpcPluginCmd = "--go_out=plugins=grpc"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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{
|
||||
|
35
tools/goctl/rpc/gen_test.go
Normal file
35
tools/goctl/rpc/gen_test.go
Normal file
@ -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())
|
||||
}
|
46
tools/goctl/rpc/parser/parser.go
Normal file
46
tools/goctl/rpc/parser/parser.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
if item.Path == nil {
|
||||
continue
|
||||
}
|
||||
key := a.mustGetIndentName(item.Name)
|
||||
value := item.Path.Value
|
||||
imports[key] = value
|
||||
dependencyStructure, err := a.parseExternalDependency()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
structs, funcs := a.mustScope(f.Scope)
|
||||
pbAst.Imports = imports
|
||||
pbAst.Strcuts = structs
|
||||
pbAst.Service = funcs
|
||||
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) mustScope(scope *ast.Scope) (map[string]*Struct, []*RpcService) {
|
||||
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()
|
||||
strucs, function := a.mustScope(f.Scope, a.mustGetIndentName(f.Name))
|
||||
for k, v := range strucs {
|
||||
if v == 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
|
||||
}
|
||||
}
|
||||
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, 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
|
||||
|
294
tools/goctl/rpc/parser/proto.go
Normal file
294
tools/goctl/rpc/parser/proto.go
Normal file
@ -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
|
||||
}
|
28
tools/goctl/rpc/test.proto
Normal file
28
tools/goctl/rpc/test.proto
Normal file
@ -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<string, string> m = 1;
|
||||
}
|
||||
|
||||
service Greeter {
|
||||
rpc greet(request) returns (response);
|
||||
rpc idRequest(base.IdRequest)returns(base.EmptyResponse);
|
||||
}
|
331
tools/goctl/rpc/test/test.pb.go
Normal file
331
tools/goctl/rpc/test/test.pb.go
Normal file
@ -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",
|
||||
}
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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」")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,4 +3,7 @@ package vars
|
||||
const (
|
||||
ProjectName = "zero"
|
||||
ProjectOpenSourceUrl = "github.com/tal-tech/go-zero"
|
||||
OsWindows = "windows"
|
||||
OsMac = "darwin"
|
||||
OsLinux = "linux"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user