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
master
Keson 4 years ago committed by GitHub
parent c32759d735
commit 0a9c427443
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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)/sharedrpc client逻辑代码存放目录。
> 注意这里的shared文件夹名称将会是代码中的package名称。
* --idea 非必填是否为idea插件中执行保留字段终端执行可以忽略
## 开发人员需要做什么
### 开发人员需要做什么
关注业务代码编写将重复性、与业务无关的工作交给goctl生成好rpc服务代码后开饭人员仅需要修改
@ -173,11 +173,8 @@ 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.2see [https://github.com/grpc/grpc-go/issues/3347](https://github.com/grpc/grpc-go/issues/3347))即
@ -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工程)

@ -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

@ -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)/sharedrpc 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工程)
* 错误一:

@ -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,
}

@ -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
}
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"),
"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"
)
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
}

@ -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{

@ -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())
}

@ -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,12 +63,32 @@ 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
ParameterIn Type
ParameterOut Type
Document []string
}
RpcService struct {
@ -71,54 +97,98 @@ type (
}
// parsing for rpc
PbAst struct {
Package string
// external reference
ContainsAny bool
Imports map[string]string
Strcuts map[string]*Struct
// rpc server's functions,not all functions
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
}
key := a.mustGetIndentName(item.Name)
value := item.Path.Value
imports[key] = value
importList := f.Imports
for _, item := range importList {
name := a.mustGetIndentName(item.Name)
if item.Path != nil {
imports[name] = item.Path.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

@ -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
}

@ -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);
}

@ -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…
Cancel
Save