Kratos protobuf protoc (#71)

* add cross-platform protoc
* add protobuf generator
pull/75/head
Tony 6 years ago committed by Felix Hao
parent 8266a50f3d
commit e5fe1e4f63
  1. 2
      .travis.yml
  2. 2
      README.md
  3. 51
      doc/wiki-cn/kratos-tool.md
  4. 2
      go.mod
  5. 19
      tool/kprotoc/install_kprotoc.sh
  6. 118
      tool/kprotoc/kprotoc.sh
  7. 25
      tool/kratos-protoc/bm.go
  8. 25
      tool/kratos-protoc/grpc.go
  9. 37
      tool/kratos-protoc/main.go
  10. 160
      tool/kratos-protoc/protoc.go
  11. 25
      tool/kratos-protoc/swagger.go
  12. 14
      tool/kratos/main.go
  13. 8
      tool/kratos/project.go
  14. 39
      tool/kratos/template.go
  15. 6
      tool/kratos/tool.go
  16. 23
      tool/kratos/tool_index.go
  17. 9
      tool/protobuf/pkg/extensions/gogoproto/Makefile
  18. 818
      tool/protobuf/pkg/extensions/gogoproto/gogo.pb.go
  19. 45
      tool/protobuf/pkg/extensions/gogoproto/gogo.pb.golden
  20. 136
      tool/protobuf/pkg/extensions/gogoproto/gogo.proto
  21. 31
      tool/protobuf/pkg/extensions/google/api/annotations.proto
  22. 318
      tool/protobuf/pkg/extensions/google/api/http.proto
  23. 94
      tool/protobuf/pkg/gen/main.go
  24. 71
      tool/protobuf/pkg/generator/command_line.go
  25. 310
      tool/protobuf/pkg/generator/generator.go
  26. 136
      tool/protobuf/pkg/generator/helper.go
  27. 145
      tool/protobuf/pkg/generator/http.go
  28. 27
      tool/protobuf/pkg/naming/go_naming.go
  29. 101
      tool/protobuf/pkg/naming/naming.go
  30. 119
      tool/protobuf/pkg/project/project.go
  31. 20
      tool/protobuf/pkg/tag/ext_tags.go
  32. 55
      tool/protobuf/pkg/tag/tags.go
  33. 277
      tool/protobuf/pkg/typemap/typemap.go
  34. 98
      tool/protobuf/pkg/utils/stringutils.go
  35. 29
      tool/protobuf/pkg/utils/utils.go
  36. 355
      tool/protobuf/protoc-gen-bm/generator/generator.go
  37. 27
      tool/protobuf/protoc-gen-bm/generator/generator_test.go
  38. 23
      tool/protobuf/protoc-gen-bm/main.go
  39. 303
      tool/protobuf/protoc-gen-bswagger/generator.go
  40. 22
      tool/protobuf/protoc-gen-bswagger/main.go
  41. 218
      tool/protobuf/protoc-gen-bswagger/types.go

@ -1,7 +1,7 @@
language: go
go:
- 1.11.x
- 1.12.x
# Only clone the most recent commit.
git:

@ -1,6 +1,7 @@
![kratos](doc/img/kratos3.png)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg)](https://golang.org/)
[![Build Status](https://travis-ci.org/bilibili/kratos.svg?branch=master)](https://travis-ci.org/bilibili/kratos)
[![GoDoc](https://godoc.org/github.com/bilibili/kratos?status.svg)](https://godoc.org/github.com/bilibili/kratos)
[![Go Report Card](https://goreportcard.com/badge/github.com/bilibili/kratos)](https://goreportcard.com/report/github.com/bilibili/kratos)
@ -66,3 +67,4 @@ Kratos is under the MIT license. See the [LICENSE](./LICENSE) file for details.
-------------
*Please report bugs, concerns, suggestions by issues, or join QQ-group 716486124 to discuss problems around source code.*

@ -55,13 +55,13 @@ kratos new kratos-demo
kratos new kratos-demo -o YourName -d YourPath
```
注意,`kratos new`默认是不会生成`grpc`示例代码的,如需生成请加`--grpc`,如下:
注意,`kratos new`默认是不会生成通过protobuf定义的`grpc`和`bm`示例代码的,如需生成请加`--proto`,如下:
```shell
kratos new kratos-demo -o YourName -d YourPath --grpc
kratos new kratos-demo -o YourName -d YourPath --proto
```
特别注意,如果不是macos系统,生成的示例项目`api`目录下的`proto`文件并不会自动生成对应的`.pb.go`文件,需要参考以下说明进行生成。
特别注意,如果不是macos系统,生成的示例项目`api`目录下的`proto`文件并不会自动生成对应的`.pb.go`和`.bm.go`文件,需要参考以下说明进行生成。
[protoc说明](protoc.md)
@ -74,8 +74,9 @@ kratos new kratos-demo -o YourName -d YourPath --grpc
`kratos tool`是基于proto生成http&grpc代码,生成缓存回源代码,生成memcache执行代码,生成swagger文档等工具集,先看下`kratos tool`的执行效果:
```
swagger(已安装): swagger api文档 Author(goswagger.io) [2019/05/05]
protoc(已安装): 快速方便生成pb.go和bm.go的protoc封装,windows、Linux请先安装protoc工具 Author(kratos) [2019/05/04]
kratos(已安装): Kratos工具集本体 Author(kratos) [2019/04/02]
kprotoc(已安装): 快速方便生成pb.go的protoc封装,不支持Windows,Linux请先安装protoc工具 Author(kratos) [2019/04/02]
安装工具: kratos tool install demo
执行工具: kratos tool demo
@ -87,25 +88,49 @@ kprotoc(已安装): 快速方便生成pb.go的protoc封装,不支持Windows,
***小小说明:如未安装工具,第一次运行也可自动安装,不需要特别执行install***
目前已经集成的工具有:
* `kprotoc`用于快速生成`pb.go`文件,但目前不支持windows,Linux也需要先自己安装`protoc`工具。
* `protoc`用于快速生成`*.pb.go`和`*.bm.go`文件,但目前不支持windows,Linux也需要先自己安装`protoc`工具。
* `swagger`用于显示自动生成的BM API接口文档,通过 `kratos tool swagger serve api/api.swagger.json` 可以访问到文档。
* TODOs...
### kratos tool kprotoc
### kratos tool protoc
该命令运行没其他参数,直接`kratos tool kprotoc`运行即可。但使用前需特别说明:
该命令运行没其他参数,直接`kratos tool protoc`运行即可。但使用前需特别说明:
* 该工具不支持Windows用户,请安装`protoc`和`gogo protobuf`工具
* 该工具在Linux下运行,需提前安装好`protoc`工具
* 该工具在Windows/Linux下运行,需提前安装好`protoc`工具
该工具实际是一段`shell`脚本,其中自动将`protoc`命令进行了拼接,识别了需要`include`的目录和当前目录下的`proto`文件,最终会拼接为如下命令进行执行:
该工具实际是一段`shell`脚本,其中自动将`protoc`命令进行了拼接,识别了需要`*.proto`的目录和当前目录下的`proto`文件,最终会拼接为如下命令进行执行:
```shell
protoc -I/Users/felix/work/go/src:/usr/local/include --gogofast_out=plugins=grpc:/Users/felix/work/go/src /Users/felix/work/go/src/kratos-demo/api/api.proto
export $KRATOS_HOME = kratos路径
export $KRATOS_DEMO = 项目路径
// 生成:api.pb.go
protoc -I$GOPATH/src:$KRATOS_HOME/tool/protobuf/pkg/extensions:$KRATOS_DEMO/api --gogofast_out=plugins=grpc:$KRATOS_DEMO/api $KRATOS_DEMO/api/api.proto
// 生成:api.bm.go
protoc -I$GOPATH/src:$KRATOS_HOME/tool/protobuf/pkg/extensions:$KRATOS_DEMO/api --bm_out=$KRATOS_DEMO/api $KRATOS_DEMO/api/api.proto
// 生成:api.swagger.json
protoc -I$GOPATH/src:$KRATOS_HOME/tool/protobuf/pkg/extensions:$KRATOS_DEMO/api --bswagger_out=$KRATOS_DEMO/api $KRATOS_DEMO/api/api.proto
```
Windows和Linux用户可以参考该命令进行`proto`生成`pb.go`文件,也可以参考[protoc说明](protoc.md)。
大家也可以参考该命令进行`proto`生成`*.pb.go`和`*.bm.go`文件,也可以参考[protoc说明](protoc.md)。
### TODOs
### Tool examples
```shell
// new a project
cd $GOPATH/src
kratos new kratos-demo -o Tinker --proto
// build & run
cd kratos-demo
kratos run
// swagger docs
kratos tool swagger serve kratos-demo/api/api.swagger.json
// generate proto
cd kratos-demo/api
kratos tool protoc api.proto
```
-------------

@ -22,6 +22,7 @@ require (
github.com/prometheus/client_golang v0.9.2
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec // indirect
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726
github.com/sirupsen/logrus v1.4.1 // indirect
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a
github.com/stretchr/testify v1.3.0
@ -29,6 +30,7 @@ require (
github.com/urfave/cli v1.20.0
golang.org/x/net v0.0.0-20190311183353-d8887717615a
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8
google.golang.org/grpc v1.20.1
gopkg.in/AlecAivazis/survey.v1 v1.8.2
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect

@ -1,19 +0,0 @@
#!/bin/bash
set -e
if [[ -z $GOPATH ]]; then
GOPATH=${HOME}/go
fi
BIN_PATH=$( cut -d ':' -f 1 <<< "$GOPATH" )/bin
if [[ ! -z $GOBIN ]]; then
BIN_PATH=$GOBIN
fi
if [[ ! -z $INSTALL_PATH ]]; then
BIN_PATH=$INSTALL_PATH
fi
if [[ -f $BIN_PATH/kprotoc ]]; then
echo "kprotoc alreay install, remove $BIN_PATH/kprotoc first to reinstall."
exit 1;
fi
ln -s $GOPATH/src/github.com/bilibili/kratos/tool/kprotoc/kprotoc.sh $BIN_PATH/kprotoc
echo "install kprotoc to $BIN_PATH/kprotoc done!"

@ -1,118 +0,0 @@
#!/bin/bash
DEFAULT_PROTOC_GEN="gogofast"
DEFAULT_PROTOC="protoc"
KRATOS_DIR_NAME="github.com/bilibili/kratos"
USR_INCLUDE_DIR="/usr/local/include"
GOPATH=$GOPATH
if [[ -z $GOPATH ]]; then
GOPATH=${HOME}/go
fi
function _install_protoc() {
osname=$(uname -s)
echo "install protoc ..."
case $osname in
"Darwin" )
brew install protobuf
;;
*)
echo "unknown operating system, need install protobuf manual see: https://developers.google.com/protocol-buffers"
exit 1
;;
esac
}
function _install_protoc_gen() {
local protoc_gen=$1
case $protoc_gen in
"gofast" )
echo "install gofast from github.com/gogo/protobuf/protoc-gen-gofast"
go get github.com/gogo/protobuf/protoc-gen-gofast
;;
"gogofast" )
echo "install gogofast from github.com/gogo/protobuf/protoc-gen-gogofast"
go get github.com/gogo/protobuf/protoc-gen-gogofast
;;
"gogo" )
echo "install gogo from github.com/gogo/protobuf/protoc-gen-gogo"
go get github.com/gogo/protobuf/protoc-gen-gogo
;;
"go" )
echo "install protoc-gen-go from github.com/golang/protobuf"
go get github.com/golang/protobuf/{proto,protoc-gen-go}
;;
*)
echo "can't install protoc-gen-${protoc_gen} automatic !"
exit 1;
;;
esac
}
function _find_kratos_dir() {
local kratos_dir_name=$1
local current_dir="$GOPATH/src/$kratos_dir_name"
if [[ ! -d $current_dir ]]; then
go get -u $kratos_dir_name
fi
echo $current_dir
}
function _esc_string() {
echo $(echo "$1" | sed 's_/_\\/_g')
}
function _run_protoc() {
local proto_dir=$1
local proto_files=$(find $proto_dir -maxdepth 1 -name "*.proto")
if [[ -z $proto_files ]]; then
return
fi
local protoc_cmd="$PROTOC -I$PROTO_PATH --${PROTOC_GEN}_out=plugins=grpc:${GOPATH}/src ${proto_files}"
echo $protoc_cmd
$protoc_cmd
}
if [[ -z $PROTOC ]]; then
PROTOC=${DEFAULT_PROTOC}
which $PROTOC
if [[ "$?" -ne "0" ]]; then
_install_protoc
fi
fi
if [[ -z $PROTOC_GEN ]]; then
PROTOC_GEN=${DEFAULT_PROTOC_GEN}
which protoc-gen-$PROTOC_GEN
if [[ "$?" -ne "0" ]]; then
_install_protoc_gen $PROTOC_GEN
fi
fi
KRATOS_DIR=$(_find_kratos_dir $KRATOS_DIR_NAME)
if [[ "$?" != "0" ]]; then
echo "can't find kratos directoy"
exit 1
fi
KRATOS_PARENT=$(dirname $KRATOS_DIR)
if [[ -z $PROTO_PATH ]]; then
PROTO_PATH=$GOPATH/src:$KRATOS_PARENT:$USR_INCLUDE_DIR
else
PROTO_PATH=$GOPATH/src:$PROTO_PATH:$KRATOS_PARENT:$USR_INCLUDE_DIR
fi
if [[ ! -z $1 ]]; then
cd $1
fi
TARGET_DIR=$(pwd)
# switch to $GOPATH/src
cd $GOPATH/src
echo "switch workdir to $GOPATH/src"
DIRS=$(find $TARGET_DIR -type d)
for dir in $DIRS; do
echo "run protoc in $dir"
_run_protoc $dir
done

@ -0,0 +1,25 @@
package main
import (
"os/exec"
"github.com/urfave/cli"
)
const (
_getBMGen = "go get github.com/bilibili/kratos/tool/protobuf/protoc-gen-bm"
_bmProtoc = "protoc --proto_path=%s --proto_path=%s --proto_path=%s --bm_out=explicit_http=true:."
)
func installBMGen() error {
if _, err := exec.LookPath("protoc-gen-bm"); err != nil {
if err := goget(_getBMGen); err != nil {
return err
}
}
return nil
}
func genBM(ctx *cli.Context) error {
return generate(ctx, _bmProtoc)
}

@ -0,0 +1,25 @@
package main
import (
"os/exec"
"github.com/urfave/cli"
)
const (
_getGRPCGen = "go get github.com/gogo/protobuf/protoc-gen-gogofast"
_grpcProtoc = "protoc --proto_path=%s --proto_path=%s --proto_path=%s --gogofast_out=plugins=grpc:."
)
func installGRPCGen() error {
if _, err := exec.LookPath("protoc-gen-gogofast"); err != nil {
if err := goget(_getGRPCGen); err != nil {
return err
}
}
return nil
}
func genGRPC(ctx *cli.Context) error {
return generate(ctx, _grpcProtoc)
}

@ -0,0 +1,37 @@
package main
import (
"log"
"os"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "protc"
app.Usage = "protobuf生成工具"
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "bm",
Usage: "whether to use BM for generation",
Destination: &withBM,
},
cli.BoolFlag{
Name: "grpc",
Usage: "whether to use gRPC for generation",
Destination: &withGRPC,
},
cli.BoolFlag{
Name: "swagger",
Usage: "whether to use swagger for generation",
Destination: &withSwagger,
},
}
app.Action = func(c *cli.Context) error {
return protocAction(c)
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}

@ -0,0 +1,160 @@
package main
import (
"errors"
"fmt"
"go/build"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/urfave/cli"
)
var (
withBM bool
withGRPC bool
withSwagger bool
)
func protocAction(ctx *cli.Context) (err error) {
if err = checkProtoc(); err != nil {
return err
}
if !withGRPC && !withBM && !withSwagger {
withBM = true
withGRPC = true
withSwagger = true
}
if withBM {
if err = installBMGen(); err != nil {
return
}
if err = genBM(ctx); err != nil {
return
}
}
if withGRPC {
if err = installGRPCGen(); err != nil {
return err
}
if err = genGRPC(ctx); err != nil {
return
}
}
if withSwagger {
if err = installSwaggerGen(); err != nil {
return
}
if err = genSwagger(ctx); err != nil {
return
}
}
log.Printf("generate %v success.\n", ctx.Args())
return nil
}
func checkProtoc() error {
if _, err := exec.LookPath("protoc"); err != nil {
switch runtime.GOOS {
case "darwin":
fmt.Println("brew install protobuf")
cmd := exec.Command("brew", "install", "protobuf")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
return err
}
case "linux":
fmt.Println("snap install --classic protobuf")
cmd := exec.Command("snap", "install", "--classic", "protobuf")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
return err
}
default:
return errors.New("您还没安装protobuf,请进行手动安装:https://github.com/protocolbuffers/protobuf/releases")
}
}
return nil
}
func generate(ctx *cli.Context, protoc string) error {
pwd, _ := os.Getwd()
gosrc := path.Join(gopath(), "src")
ext, err := latestKratos()
if err != nil {
return err
}
line := fmt.Sprintf(protoc, gosrc, ext, pwd)
log.Println(line, strings.Join(ctx.Args(), " "))
args := strings.Split(line, " ")
args = append(args, ctx.Args()...)
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = pwd
cmd.Env = os.Environ()
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return err
}
return nil
}
func goget(url string) error {
args := strings.Split(url, " ")
cmd := exec.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Println(url)
return cmd.Run()
}
func latestKratos() (string, error) {
gopath := gopath()
ext := path.Join(gopath, "src/github.com/bilibili/kratos/tool/protobuf/pkg/extensions")
if _, err := os.Stat(ext); !os.IsNotExist(err) {
return ext, nil
}
ext = path.Join(gopath, "pkg/mod/github.com/bilibili")
files, err := ioutil.ReadDir(ext)
if err != nil {
return "", err
}
if len(files) == 0 {
return "", errors.New("not found kratos package")
}
return path.Join(ext, files[len(files)-1].Name(), "tool/protobuf/pkg/extensions"), nil
}
func gopath() (gp string) {
gopaths := strings.Split(os.Getenv("GOPATH"), ":")
if len(gopaths) == 1 {
return gopaths[0]
}
pwd, err := os.Getwd()
if err != nil {
return
}
abspwd, err := filepath.Abs(pwd)
if err != nil {
return
}
for _, gopath := range gopaths {
absgp, err := filepath.Abs(gopath)
if err != nil {
return
}
if strings.HasPrefix(abspwd, absgp) {
return absgp
}
}
return build.Default.GOPATH
}

@ -0,0 +1,25 @@
package main
import (
"os/exec"
"github.com/urfave/cli"
)
const (
_getSwaggerGen = "go get github.com/bilibili/kratos/tool/protobuf/protoc-gen-bswagger"
_swaggerProtoc = "protoc --proto_path=%s --proto_path=%s --proto_path=%s --bswagger_out=explicit_http=true:."
)
func installSwaggerGen() error {
if _, err := exec.LookPath("protoc-gen-bswagger"); err != nil {
if err := goget(_getSwaggerGen); err != nil {
return err
}
}
return nil
}
func genSwagger(ctx *cli.Context) error {
return generate(ctx, _swaggerProtoc)
}

@ -31,8 +31,8 @@ func main() {
Destination: &p.Path,
},
cli.BoolFlag{
Name: "grpc",
Usage: "whether to use grpc for create project",
Name: "proto",
Usage: "whether to use protobuf for create project",
Destination: &p.WithGRPC,
},
},
@ -50,12 +50,12 @@ func main() {
Usage: "kratos run",
Action: runAction,
},
{
Name: "tool",
Aliases: []string{"t"},
Usage: "kratos tool",
Action: toolAction,
Name: "tool",
Aliases: []string{"t"},
Usage: "kratos tool",
Action: toolAction,
SkipFlagParsing: true,
},
{
Name: "version",

@ -2,11 +2,9 @@ package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"runtime"
"strings"
"text/template"
)
@ -115,16 +113,14 @@ func create() (err error) {
if err = genpb(); err != nil {
return
}
if runtime.GOOS != "darwin" {
fmt.Println("您的操作系统不是macos,kprotoc工具无法正常运行,请参看kratos tool文档!")
fmt.Println("地址:", toolDoc)
}
}
return
}
func genpb() error {
cmd := exec.Command("go", "generate", p.Path+"/api/generate.go")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

@ -98,7 +98,7 @@ func main() {
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
ctx, cancel := context.WithTimeout(context.Background(), 35*time.Second)
if err := grpcSrv.Shutdown(ctx); err != nil
if err := grpcSrv.Shutdown(ctx); err != nil {
log.Error("grpcSrv.Shutdown error(%v)", err)
} // grpc
if err := httpSrv.Shutdown(ctx); err != nil {
@ -302,6 +302,15 @@ func (s *Service) SayHello(ctx context.Context, req *pb.HelloReq) (reply *empty.
return
}
// SayHelloURL bm demo func.
func (s *Service) SayHelloURL(ctx context.Context, req *pb.HelloReq) (reply *pb.HelloResp, err error) {
reply = &pb.HelloResp{
Content: "hello " + req.Name,
}
fmt.Printf("hello url %s", req.Name)
return
}
// Ping ping the resource.
func (s *Service) Ping(ctx context.Context) (err error) {
return s.dao.Ping(ctx)
@ -317,6 +326,7 @@ func (s *Service) Close() {
import (
"net/http"
pb "{{.Name}}/api"
"{{.Name}}/internal/model"
"{{.Name}}/internal/service"
@ -343,6 +353,7 @@ func New(s *service.Service) (engine *bm.Engine) {
}
svc = s
engine = bm.DefaultServer(hc.Server)
pb.RegisterDemoBMServer(engine, svc)
initRouter(engine)
if err := engine.Start(); err != nil {
panic(err)
@ -377,21 +388,11 @@ func howToStart(c *bm.Context) {
_tplAPIProto = `// 定义项目 API 的 proto 文件 可以同时描述 gRPC 和 HTTP API
// protobuf 文件参考:
// - https://developers.google.com/protocol-buffers/
// - TODO:待补充文档URL
// protobuf 生成 HTTP 工具:
// - TODO:待补充文档URL
// gRPC Golang Model:
// - TODO:待补充文档URL
// gRPC Golang Warden Gen:
// - TODO:待补充文档URL
// gRPC http 调试工具(无需pb文件):
// - TODO:待补充文档URL
// grpc 命令行调试工具(无需pb文件):
// - TODO:待补充文档URL
syntax = "proto3";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "gogoproto/gogo.proto";
import "google/protobuf/empty.proto";
import "google/api/annotations.proto";
// package 命名使用 {appid}.{version} 的方式, version 形如 v1, v2 ..
package demo.service.v1;
@ -399,21 +400,29 @@ package demo.service.v1;
// NOTE: 最后请删除这些无用的注释 (゜-゜)つロ
option go_package = "api";
// do not generate getXXX() method
option (gogoproto.goproto_getters_all) = false;
service Demo {
rpc SayHello (HelloReq) returns (.google.protobuf.Empty);
rpc SayHelloURL(HelloReq) returns (HelloResp) {
option (google.api.http) = {
get:"/{{.Name}}/say_hello"
};
};
}
message HelloReq {
string name = 1 [(gogoproto.moretags)='form:"name" validate:"required"'];
}
message HelloResp {
string Content = 1 [(gogoproto.jsontag) = 'content'];
}
`
_tplAPIGenerate = `package api
// 生成 gRPC 代码
//go:generate kratos tool kprotoc
//go:generate kratos tool protoc api.proto
`
_tplModel = `package model

@ -130,6 +130,7 @@ func runTool(name, dir, cmd string, args []string) (err error) {
// Tool .
type Tool struct {
Name string `json:"name"`
Alias string `json:"alias"`
BuildTime time.Time `json:"build_time"`
Install string `json:"install"`
Summary string `json:"summary"`
@ -151,6 +152,7 @@ func (t Tool) install() {
fmt.Fprintf(os.Stderr, color.RedString("%s: 自动安装失败详情请查看文档:%s\n", t.Name, toolDoc))
return
}
fmt.Println(t.Install)
cmds := strings.Split(t.Install, " ")
if len(cmds) > 0 {
if err := runTool(t.Name, path.Dir(t.toolPath()), cmds[0], cmds[1:]); err == nil {
@ -172,7 +174,7 @@ func (t Tool) updated() bool {
}
func (t Tool) toolPath() string {
return filepath.Join(goPath(), "bin", t.Name)
return filepath.Join(gopath(), "bin", t.Alias)
}
func (t Tool) installed() bool {
@ -180,7 +182,7 @@ func (t Tool) installed() bool {
return err == nil
}
func goPath() (gp string) {
func gopath() (gp string) {
gopaths := strings.Split(os.Getenv("GOPATH"), ":")
if len(gopaths) == 1 {
return gopaths[0]

@ -5,6 +5,7 @@ import "time"
var toolIndexs = []*Tool{
&Tool{
Name: "kratos",
Alias: "kratos",
BuildTime: time.Date(2019, 4, 2, 0, 0, 0, 0, time.Local),
Install: "go get -u github.com/bilibili/kratos/tool/kratos",
Summary: "Kratos工具集本体",
@ -12,11 +13,21 @@ var toolIndexs = []*Tool{
Author: "kratos",
},
&Tool{
Name: "kprotoc",
BuildTime: time.Date(2019, 4, 2, 0, 0, 0, 0, time.Local),
Install: "bash -c ${GOPATH}/src/github.com/bilibili/kratos/tool/kprotoc/install_kprotoc.sh",
Summary: "快速方便生成pb.go的protoc封装,不支持windows,Linux请先安装protoc工具",
Platform: []string{"darwin", "linux"},
Author: "https://github.com/tomwei7",
Name: "protoc",
Alias: "kratos-protoc",
BuildTime: time.Date(2019, 5, 4, 0, 0, 0, 0, time.Local),
Install: "go get -u github.com/bilibili/kratos/tool/krotos-protoc",
Summary: "快速方便生成pb.go的protoc封装,windows、Linux请先安装protoc工具",
Platform: []string{"darwin", "linux", "windows"},
Author: "kratos",
},
&Tool{
Name: "swagger",
Alias: "swagger",
BuildTime: time.Date(2019, 5, 5, 0, 0, 0, 0, time.Local),
Install: "go get -u github.com/go-swagger/go-swagger/cmd/swagger",
Summary: "swagger api文档",
Platform: []string{"darwin", "linux", "windows"},
Author: "goswagger.io",
},
}

@ -0,0 +1,9 @@
regenerate:
go install go-common/vendor/github.com/golang/protobuf/protoc-gen-go
protoc --go_out=paths=source_relative:. gogo.proto
restore:
cp gogo.pb.golden gogo.pb.go
preserve:
cp gogo.pb.go gogo.pb.golden

@ -0,0 +1,818 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: gogo.proto
package gogoproto // import "github.com/bilibili/kratos/tool/protobuf/pkg/extensions/gogoproto"
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
// 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.ProtoPackageIsVersion2 // please upgrade the proto package
var E_GoprotoEnumPrefix = &proto.ExtensionDesc{
ExtendedType: (*descriptor.EnumOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 62001,
Name: "gogoproto.goproto_enum_prefix",
Tag: "varint,62001,opt,name=goproto_enum_prefix,json=goprotoEnumPrefix",
Filename: "gogo.proto",
}
var E_GoprotoEnumStringer = &proto.ExtensionDesc{
ExtendedType: (*descriptor.EnumOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 62021,
Name: "gogoproto.goproto_enum_stringer",
Tag: "varint,62021,opt,name=goproto_enum_stringer,json=goprotoEnumStringer",
Filename: "gogo.proto",
}
var E_EnumStringer = &proto.ExtensionDesc{
ExtendedType: (*descriptor.EnumOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 62022,
Name: "gogoproto.enum_stringer",
Tag: "varint,62022,opt,name=enum_stringer,json=enumStringer",
Filename: "gogo.proto",
}
var E_EnumCustomname = &proto.ExtensionDesc{
ExtendedType: (*descriptor.EnumOptions)(nil),
ExtensionType: (*string)(nil),
Field: 62023,
Name: "gogoproto.enum_customname",
Tag: "bytes,62023,opt,name=enum_customname,json=enumCustomname",
Filename: "gogo.proto",
}
var E_Enumdecl = &proto.ExtensionDesc{
ExtendedType: (*descriptor.EnumOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 62024,
Name: "gogoproto.enumdecl",
Tag: "varint,62024,opt,name=enumdecl",
Filename: "gogo.proto",
}
var E_EnumvalueCustomname = &proto.ExtensionDesc{
ExtendedType: (*descriptor.EnumValueOptions)(nil),
ExtensionType: (*string)(nil),
Field: 66001,
Name: "gogoproto.enumvalue_customname",
Tag: "bytes,66001,opt,name=enumvalue_customname,json=enumvalueCustomname",
Filename: "gogo.proto",
}
var E_GoprotoGettersAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63001,
Name: "gogoproto.goproto_getters_all",
Tag: "varint,63001,opt,name=goproto_getters_all,json=goprotoGettersAll",
Filename: "gogo.proto",
}
var E_GoprotoEnumPrefixAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63002,
Name: "gogoproto.goproto_enum_prefix_all",
Tag: "varint,63002,opt,name=goproto_enum_prefix_all,json=goprotoEnumPrefixAll",
Filename: "gogo.proto",
}
var E_GoprotoStringerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63003,
Name: "gogoproto.goproto_stringer_all",
Tag: "varint,63003,opt,name=goproto_stringer_all,json=goprotoStringerAll",
Filename: "gogo.proto",
}
var E_VerboseEqualAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63004,
Name: "gogoproto.verbose_equal_all",
Tag: "varint,63004,opt,name=verbose_equal_all,json=verboseEqualAll",
Filename: "gogo.proto",
}
var E_FaceAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63005,
Name: "gogoproto.face_all",
Tag: "varint,63005,opt,name=face_all,json=faceAll",
Filename: "gogo.proto",
}
var E_GostringAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63006,
Name: "gogoproto.gostring_all",
Tag: "varint,63006,opt,name=gostring_all,json=gostringAll",
Filename: "gogo.proto",
}
var E_PopulateAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63007,
Name: "gogoproto.populate_all",
Tag: "varint,63007,opt,name=populate_all,json=populateAll",
Filename: "gogo.proto",
}
var E_StringerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63008,
Name: "gogoproto.stringer_all",
Tag: "varint,63008,opt,name=stringer_all,json=stringerAll",
Filename: "gogo.proto",
}
var E_OnlyoneAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63009,
Name: "gogoproto.onlyone_all",
Tag: "varint,63009,opt,name=onlyone_all,json=onlyoneAll",
Filename: "gogo.proto",
}
var E_EqualAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63013,
Name: "gogoproto.equal_all",
Tag: "varint,63013,opt,name=equal_all,json=equalAll",
Filename: "gogo.proto",
}
var E_DescriptionAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63014,
Name: "gogoproto.description_all",
Tag: "varint,63014,opt,name=description_all,json=descriptionAll",
Filename: "gogo.proto",
}
var E_TestgenAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63015,
Name: "gogoproto.testgen_all",
Tag: "varint,63015,opt,name=testgen_all,json=testgenAll",
Filename: "gogo.proto",
}
var E_BenchgenAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63016,
Name: "gogoproto.benchgen_all",
Tag: "varint,63016,opt,name=benchgen_all,json=benchgenAll",
Filename: "gogo.proto",
}
var E_MarshalerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63017,
Name: "gogoproto.marshaler_all",
Tag: "varint,63017,opt,name=marshaler_all,json=marshalerAll",
Filename: "gogo.proto",
}
var E_UnmarshalerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63018,
Name: "gogoproto.unmarshaler_all",
Tag: "varint,63018,opt,name=unmarshaler_all,json=unmarshalerAll",
Filename: "gogo.proto",
}
var E_StableMarshalerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63019,
Name: "gogoproto.stable_marshaler_all",
Tag: "varint,63019,opt,name=stable_marshaler_all,json=stableMarshalerAll",
Filename: "gogo.proto",
}
var E_SizerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63020,
Name: "gogoproto.sizer_all",
Tag: "varint,63020,opt,name=sizer_all,json=sizerAll",
Filename: "gogo.proto",
}
var E_GoprotoEnumStringerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63021,
Name: "gogoproto.goproto_enum_stringer_all",
Tag: "varint,63021,opt,name=goproto_enum_stringer_all,json=goprotoEnumStringerAll",
Filename: "gogo.proto",
}
var E_EnumStringerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63022,
Name: "gogoproto.enum_stringer_all",
Tag: "varint,63022,opt,name=enum_stringer_all,json=enumStringerAll",
Filename: "gogo.proto",
}
var E_UnsafeMarshalerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63023,
Name: "gogoproto.unsafe_marshaler_all",
Tag: "varint,63023,opt,name=unsafe_marshaler_all,json=unsafeMarshalerAll",
Filename: "gogo.proto",
}
var E_UnsafeUnmarshalerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63024,
Name: "gogoproto.unsafe_unmarshaler_all",
Tag: "varint,63024,opt,name=unsafe_unmarshaler_all,json=unsafeUnmarshalerAll",
Filename: "gogo.proto",
}
var E_GoprotoExtensionsMapAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63025,
Name: "gogoproto.goproto_extensions_map_all",
Tag: "varint,63025,opt,name=goproto_extensions_map_all,json=goprotoExtensionsMapAll",
Filename: "gogo.proto",
}
var E_GoprotoUnrecognizedAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63026,
Name: "gogoproto.goproto_unrecognized_all",
Tag: "varint,63026,opt,name=goproto_unrecognized_all,json=goprotoUnrecognizedAll",
Filename: "gogo.proto",
}
var E_GogoprotoImport = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63027,
Name: "gogoproto.gogoproto_import",
Tag: "varint,63027,opt,name=gogoproto_import,json=gogoprotoImport",
Filename: "gogo.proto",
}
var E_ProtosizerAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63028,
Name: "gogoproto.protosizer_all",
Tag: "varint,63028,opt,name=protosizer_all,json=protosizerAll",
Filename: "gogo.proto",
}
var E_CompareAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63029,
Name: "gogoproto.compare_all",
Tag: "varint,63029,opt,name=compare_all,json=compareAll",
Filename: "gogo.proto",
}
var E_TypedeclAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63030,
Name: "gogoproto.typedecl_all",
Tag: "varint,63030,opt,name=typedecl_all,json=typedeclAll",
Filename: "gogo.proto",
}
var E_EnumdeclAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63031,
Name: "gogoproto.enumdecl_all",
Tag: "varint,63031,opt,name=enumdecl_all,json=enumdeclAll",
Filename: "gogo.proto",
}
var E_GoprotoRegistration = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63032,
Name: "gogoproto.goproto_registration",
Tag: "varint,63032,opt,name=goproto_registration,json=goprotoRegistration",
Filename: "gogo.proto",
}
var E_MessagenameAll = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FileOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 63033,
Name: "gogoproto.messagename_all",
Tag: "varint,63033,opt,name=messagename_all,json=messagenameAll",
Filename: "gogo.proto",
}
var E_GoprotoGetters = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64001,
Name: "gogoproto.goproto_getters",
Tag: "varint,64001,opt,name=goproto_getters,json=goprotoGetters",
Filename: "gogo.proto",
}
var E_GoprotoStringer = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64003,
Name: "gogoproto.goproto_stringer",
Tag: "varint,64003,opt,name=goproto_stringer,json=goprotoStringer",
Filename: "gogo.proto",
}
var E_VerboseEqual = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64004,
Name: "gogoproto.verbose_equal",
Tag: "varint,64004,opt,name=verbose_equal,json=verboseEqual",
Filename: "gogo.proto",
}
var E_Face = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64005,
Name: "gogoproto.face",
Tag: "varint,64005,opt,name=face",
Filename: "gogo.proto",
}
var E_Gostring = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64006,
Name: "gogoproto.gostring",
Tag: "varint,64006,opt,name=gostring",
Filename: "gogo.proto",
}
var E_Populate = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64007,
Name: "gogoproto.populate",
Tag: "varint,64007,opt,name=populate",
Filename: "gogo.proto",
}
var E_Stringer = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 67008,
Name: "gogoproto.stringer",
Tag: "varint,67008,opt,name=stringer",
Filename: "gogo.proto",
}
var E_Onlyone = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64009,
Name: "gogoproto.onlyone",
Tag: "varint,64009,opt,name=onlyone",
Filename: "gogo.proto",
}
var E_Equal = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64013,
Name: "gogoproto.equal",
Tag: "varint,64013,opt,name=equal",
Filename: "gogo.proto",
}
var E_Description = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64014,
Name: "gogoproto.description",
Tag: "varint,64014,opt,name=description",
Filename: "gogo.proto",
}
var E_Testgen = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64015,
Name: "gogoproto.testgen",
Tag: "varint,64015,opt,name=testgen",
Filename: "gogo.proto",
}
var E_Benchgen = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64016,
Name: "gogoproto.benchgen",
Tag: "varint,64016,opt,name=benchgen",
Filename: "gogo.proto",
}
var E_Marshaler = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64017,
Name: "gogoproto.marshaler",
Tag: "varint,64017,opt,name=marshaler",
Filename: "gogo.proto",
}
var E_Unmarshaler = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64018,
Name: "gogoproto.unmarshaler",
Tag: "varint,64018,opt,name=unmarshaler",
Filename: "gogo.proto",
}
var E_StableMarshaler = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64019,
Name: "gogoproto.stable_marshaler",
Tag: "varint,64019,opt,name=stable_marshaler,json=stableMarshaler",
Filename: "gogo.proto",
}
var E_Sizer = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64020,
Name: "gogoproto.sizer",
Tag: "varint,64020,opt,name=sizer",
Filename: "gogo.proto",
}
var E_UnsafeMarshaler = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64023,
Name: "gogoproto.unsafe_marshaler",
Tag: "varint,64023,opt,name=unsafe_marshaler,json=unsafeMarshaler",
Filename: "gogo.proto",
}
var E_UnsafeUnmarshaler = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64024,
Name: "gogoproto.unsafe_unmarshaler",
Tag: "varint,64024,opt,name=unsafe_unmarshaler,json=unsafeUnmarshaler",
Filename: "gogo.proto",
}
var E_GoprotoExtensionsMap = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64025,
Name: "gogoproto.goproto_extensions_map",
Tag: "varint,64025,opt,name=goproto_extensions_map,json=goprotoExtensionsMap",
Filename: "gogo.proto",
}
var E_GoprotoUnrecognized = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64026,
Name: "gogoproto.goproto_unrecognized",
Tag: "varint,64026,opt,name=goproto_unrecognized,json=goprotoUnrecognized",
Filename: "gogo.proto",
}
var E_Protosizer = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64028,
Name: "gogoproto.protosizer",
Tag: "varint,64028,opt,name=protosizer",
Filename: "gogo.proto",
}
var E_Compare = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64029,
Name: "gogoproto.compare",
Tag: "varint,64029,opt,name=compare",
Filename: "gogo.proto",
}
var E_Typedecl = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64030,
Name: "gogoproto.typedecl",
Tag: "varint,64030,opt,name=typedecl",
Filename: "gogo.proto",
}
var E_Messagename = &proto.ExtensionDesc{
ExtendedType: (*descriptor.MessageOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 64033,
Name: "gogoproto.messagename",
Tag: "varint,64033,opt,name=messagename",
Filename: "gogo.proto",
}
var E_Nullable = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 65001,
Name: "gogoproto.nullable",
Tag: "varint,65001,opt,name=nullable",
Filename: "gogo.proto",
}
var E_Embed = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 65002,
Name: "gogoproto.embed",
Tag: "varint,65002,opt,name=embed",
Filename: "gogo.proto",
}
var E_Customtype = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 65003,
Name: "gogoproto.customtype",
Tag: "bytes,65003,opt,name=customtype",
Filename: "gogo.proto",
}
var E_Customname = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 65004,
Name: "gogoproto.customname",
Tag: "bytes,65004,opt,name=customname",
Filename: "gogo.proto",
}
var E_Jsontag = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 65005,
Name: "gogoproto.jsontag",
Tag: "bytes,65005,opt,name=jsontag",
Filename: "gogo.proto",
}
var E_Moretags = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 65006,
Name: "gogoproto.moretags",
Tag: "bytes,65006,opt,name=moretags",
Filename: "gogo.proto",
}
var E_Casttype = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 65007,
Name: "gogoproto.casttype",
Tag: "bytes,65007,opt,name=casttype",
Filename: "gogo.proto",
}
var E_Castkey = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 65008,
Name: "gogoproto.castkey",
Tag: "bytes,65008,opt,name=castkey",
Filename: "gogo.proto",
}
var E_Castvalue = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 65009,
Name: "gogoproto.castvalue",
Tag: "bytes,65009,opt,name=castvalue",
Filename: "gogo.proto",
}
var E_Stdtime = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 65010,
Name: "gogoproto.stdtime",
Tag: "varint,65010,opt,name=stdtime",
Filename: "gogo.proto",
}
var E_Stdduration = &proto.ExtensionDesc{
ExtendedType: (*descriptor.FieldOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 65011,
Name: "gogoproto.stdduration",
Tag: "varint,65011,opt,name=stdduration",
Filename: "gogo.proto",
}
func init() {
proto.RegisterExtension(E_GoprotoEnumPrefix)
proto.RegisterExtension(E_GoprotoEnumStringer)
proto.RegisterExtension(E_EnumStringer)
proto.RegisterExtension(E_EnumCustomname)
proto.RegisterExtension(E_Enumdecl)
proto.RegisterExtension(E_EnumvalueCustomname)
proto.RegisterExtension(E_GoprotoGettersAll)
proto.RegisterExtension(E_GoprotoEnumPrefixAll)
proto.RegisterExtension(E_GoprotoStringerAll)
proto.RegisterExtension(E_VerboseEqualAll)
proto.RegisterExtension(E_FaceAll)
proto.RegisterExtension(E_GostringAll)
proto.RegisterExtension(E_PopulateAll)
proto.RegisterExtension(E_StringerAll)
proto.RegisterExtension(E_OnlyoneAll)
proto.RegisterExtension(E_EqualAll)
proto.RegisterExtension(E_DescriptionAll)
proto.RegisterExtension(E_TestgenAll)
proto.RegisterExtension(E_BenchgenAll)
proto.RegisterExtension(E_MarshalerAll)
proto.RegisterExtension(E_UnmarshalerAll)
proto.RegisterExtension(E_StableMarshalerAll)
proto.RegisterExtension(E_SizerAll)
proto.RegisterExtension(E_GoprotoEnumStringerAll)
proto.RegisterExtension(E_EnumStringerAll)
proto.RegisterExtension(E_UnsafeMarshalerAll)
proto.RegisterExtension(E_UnsafeUnmarshalerAll)
proto.RegisterExtension(E_GoprotoExtensionsMapAll)
proto.RegisterExtension(E_GoprotoUnrecognizedAll)
proto.RegisterExtension(E_GogoprotoImport)
proto.RegisterExtension(E_ProtosizerAll)
proto.RegisterExtension(E_CompareAll)
proto.RegisterExtension(E_TypedeclAll)
proto.RegisterExtension(E_EnumdeclAll)
proto.RegisterExtension(E_GoprotoRegistration)
proto.RegisterExtension(E_MessagenameAll)
proto.RegisterExtension(E_GoprotoGetters)
proto.RegisterExtension(E_GoprotoStringer)
proto.RegisterExtension(E_VerboseEqual)
proto.RegisterExtension(E_Face)
proto.RegisterExtension(E_Gostring)
proto.RegisterExtension(E_Populate)
proto.RegisterExtension(E_Stringer)
proto.RegisterExtension(E_Onlyone)
proto.RegisterExtension(E_Equal)
proto.RegisterExtension(E_Description)
proto.RegisterExtension(E_Testgen)
proto.RegisterExtension(E_Benchgen)
proto.RegisterExtension(E_Marshaler)
proto.RegisterExtension(E_Unmarshaler)
proto.RegisterExtension(E_StableMarshaler)
proto.RegisterExtension(E_Sizer)
proto.RegisterExtension(E_UnsafeMarshaler)
proto.RegisterExtension(E_UnsafeUnmarshaler)
proto.RegisterExtension(E_GoprotoExtensionsMap)
proto.RegisterExtension(E_GoprotoUnrecognized)
proto.RegisterExtension(E_Protosizer)
proto.RegisterExtension(E_Compare)
proto.RegisterExtension(E_Typedecl)
proto.RegisterExtension(E_Messagename)
proto.RegisterExtension(E_Nullable)
proto.RegisterExtension(E_Embed)
proto.RegisterExtension(E_Customtype)
proto.RegisterExtension(E_Customname)
proto.RegisterExtension(E_Jsontag)
proto.RegisterExtension(E_Moretags)
proto.RegisterExtension(E_Casttype)
proto.RegisterExtension(E_Castkey)
proto.RegisterExtension(E_Castvalue)
proto.RegisterExtension(E_Stdtime)
proto.RegisterExtension(E_Stdduration)
}
func init() { proto.RegisterFile("gogo.proto", fileDescriptor_gogo_e935c22a8aa82c87) }
var fileDescriptor_gogo_e935c22a8aa82c87 = []byte{
// 1260 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x98, 0xc9, 0x6f, 0x1c, 0x45,
0x17, 0xc0, 0xf5, 0xe9, 0x4b, 0x14, 0xcf, 0xf3, 0x86, 0xc7, 0xc6, 0x84, 0x08, 0x44, 0xb8, 0x71,
0xc1, 0x73, 0x40, 0x11, 0x4a, 0x59, 0x91, 0xe5, 0x58, 0x8e, 0x15, 0x84, 0xc1, 0x98, 0xd8, 0x6c,
0x87, 0x51, 0xcf, 0x4c, 0xb9, 0x33, 0xa4, 0xbb, 0xab, 0xe9, 0xae, 0x8e, 0xe2, 0xdc, 0x50, 0x58,
0x84, 0x10, 0x3b, 0x12, 0x24, 0x24, 0x81, 0x1c, 0xd8, 0xd7, 0xb0, 0x73, 0xe3, 0xc2, 0x72, 0xe5,
0x7f, 0xe0, 0x02, 0x98, 0xdd, 0x37, 0x5f, 0xd0, 0xeb, 0x7e, 0xaf, 0xa7, 0x66, 0x3c, 0x52, 0xd5,
0xdc, 0xda, 0x76, 0xfd, 0x7e, 0xae, 0x7e, 0xaf, 0xea, 0xbd, 0x37, 0x03, 0xe0, 0x2b, 0x5f, 0xcd,
0xc4, 0x89, 0xd2, 0xaa, 0x5a, 0xc1, 0xe7, 0xfc, 0xf1, 0xc0, 0x41, 0x5f, 0x29, 0x3f, 0x90, 0xb5,
0xfc, 0xa7, 0x46, 0xb6, 0x51, 0x6b, 0xc9, 0xb4, 0x99, 0xb4, 0x63, 0xad, 0x92, 0x62, 0xb1, 0xb8,
0x0b, 0x26, 0x69, 0x71, 0x5d, 0x46, 0x59, 0x58, 0x8f, 0x13, 0xb9, 0xd1, 0x3e, 0x53, 0xbd, 0x61,
0xa6, 0x20, 0x67, 0x98, 0x9c, 0x59, 0x8c, 0xb2, 0xf0, 0xee, 0x58, 0xb7, 0x55, 0x94, 0xee, 0xbf,
0xfa, 0xf3, 0xff, 0x0f, 0xfe, 0xef, 0x96, 0xa1, 0xd5, 0x09, 0x42, 0xf1, 0x6f, 0x2b, 0x39, 0x28,
0x56, 0xe1, 0xda, 0x2e, 0x5f, 0xaa, 0x93, 0x76, 0xe4, 0xcb, 0xc4, 0x62, 0xfc, 0x8e, 0x8c, 0x93,
0x86, 0xf1, 0x5e, 0x42, 0xc5, 0x02, 0x8c, 0x0e, 0xe2, 0xfa, 0x9e, 0x5c, 0x23, 0xd2, 0x94, 0x2c,
0xc1, 0x78, 0x2e, 0x69, 0x66, 0xa9, 0x56, 0x61, 0xe4, 0x85, 0xd2, 0xa2, 0xf9, 0x21, 0xd7, 0x54,
0x56, 0xc7, 0x10, 0x5b, 0x28, 0x29, 0x21, 0x60, 0x08, 0x7f, 0xd3, 0x92, 0xcd, 0xc0, 0x62, 0xf8,
0x91, 0x36, 0x52, 0xae, 0x17, 0xeb, 0x30, 0x85, 0xcf, 0xa7, 0xbd, 0x20, 0x93, 0xe6, 0x4e, 0x6e,
0xee, 0xeb, 0x59, 0xc7, 0x65, 0x2c, 0xfb, 0xe9, 0xdc, 0x9e, 0x7c, 0x3b, 0x93, 0xa5, 0xc0, 0xd8,
0x93, 0x91, 0x45, 0x5f, 0x6a, 0x2d, 0x93, 0xb4, 0xee, 0x05, 0xfd, 0xb6, 0x77, 0xac, 0x1d, 0x94,
0xc6, 0xf3, 0x5b, 0xdd, 0x59, 0x5c, 0x2a, 0xc8, 0xf9, 0x20, 0x10, 0x6b, 0x70, 0x5d, 0x9f, 0x53,
0xe1, 0xe0, 0xbc, 0x40, 0xce, 0xa9, 0x5d, 0x27, 0x03, 0xb5, 0x2b, 0xc0, 0xbf, 0x2f, 0x73, 0xe9,
0xe0, 0x7c, 0x8d, 0x9c, 0x55, 0x62, 0x39, 0xa5, 0x68, 0xbc, 0x03, 0x26, 0x4e, 0xcb, 0xa4, 0xa1,
0x52, 0x59, 0x97, 0x8f, 0x64, 0x5e, 0xe0, 0xa0, 0xbb, 0x48, 0xba, 0x71, 0x02, 0x17, 0x91, 0x43,
0xd7, 0x61, 0x18, 0xda, 0xf0, 0x9a, 0xd2, 0x41, 0x71, 0x89, 0x14, 0xfb, 0x70, 0x3d, 0xa2, 0xf3,
0x30, 0xe2, 0xab, 0xe2, 0x95, 0x1c, 0xf0, 0xcb, 0x84, 0x0f, 0x33, 0x43, 0x8a, 0x58, 0xc5, 0x59,
0xe0, 0x69, 0x97, 0x1d, 0xbc, 0xce, 0x0a, 0x66, 0x48, 0x31, 0x40, 0x58, 0xdf, 0x60, 0x45, 0x6a,
0xc4, 0x73, 0x0e, 0x86, 0x55, 0x14, 0x6c, 0xaa, 0xc8, 0x65, 0x13, 0x57, 0xc8, 0x00, 0x84, 0xa0,
0x60, 0x16, 0x2a, 0xae, 0x89, 0x78, 0x73, 0x8b, 0xaf, 0x07, 0x67, 0x60, 0x09, 0xc6, 0xb9, 0x40,
0xb5, 0x55, 0xe4, 0xa0, 0x78, 0x8b, 0x14, 0x63, 0x06, 0x46, 0xaf, 0xa1, 0x65, 0xaa, 0x7d, 0xe9,
0x22, 0x79, 0x9b, 0x5f, 0x83, 0x10, 0x0a, 0x65, 0x43, 0x46, 0xcd, 0x93, 0x6e, 0x86, 0x77, 0x38,
0x94, 0xcc, 0xa0, 0x62, 0x01, 0x46, 0x43, 0x2f, 0x49, 0x4f, 0x7a, 0x81, 0x53, 0x3a, 0xde, 0x25,
0xc7, 0x48, 0x09, 0x51, 0x44, 0xb2, 0x68, 0x10, 0xcd, 0x7b, 0x1c, 0x11, 0x03, 0xa3, 0xab, 0x97,
0x6a, 0xaf, 0x11, 0xc8, 0xfa, 0x20, 0xb6, 0xf7, 0xf9, 0xea, 0x15, 0xec, 0xb2, 0x69, 0x9c, 0x85,
0x4a, 0xda, 0x3e, 0xeb, 0xa4, 0xf9, 0x80, 0x33, 0x9d, 0x03, 0x08, 0x3f, 0x00, 0xd7, 0xf7, 0x6d,
0x13, 0x0e, 0xb2, 0x0f, 0x49, 0x36, 0xdd, 0xa7, 0x55, 0x50, 0x49, 0x18, 0x54, 0xf9, 0x11, 0x97,
0x04, 0xd9, 0xe3, 0x5a, 0x81, 0xa9, 0x2c, 0x4a, 0xbd, 0x8d, 0xc1, 0xa2, 0xf6, 0x31, 0x47, 0xad,
0x60, 0xbb, 0xa2, 0x76, 0x02, 0xa6, 0xc9, 0x38, 0x58, 0x5e, 0x3f, 0xe1, 0xc2, 0x5a, 0xd0, 0x6b,
0xdd, 0xd9, 0x7d, 0x08, 0x0e, 0x94, 0xe1, 0x3c, 0xa3, 0x65, 0x94, 0x22, 0x53, 0x0f, 0xbd, 0xd8,
0xc1, 0x7c, 0x95, 0xcc, 0x5c, 0xf1, 0x17, 0x4b, 0xc1, 0xb2, 0x17, 0xa3, 0xfc, 0x7e, 0xd8, 0xcf,
0xf2, 0x2c, 0x4a, 0x64, 0x53, 0xf9, 0x51, 0xfb, 0xac, 0x6c, 0x39, 0xa8, 0x3f, 0xed, 0x49, 0xd5,
0x9a, 0x81, 0xa3, 0xf9, 0x38, 0x5c, 0x53, 0xce, 0x2a, 0xf5, 0x76, 0x18, 0xab, 0x44, 0x5b, 0x8c,
0x9f, 0x71, 0xa6, 0x4a, 0xee, 0x78, 0x8e, 0x89, 0x45, 0x18, 0xcb, 0x7f, 0x74, 0x3d, 0x92, 0x9f,
0x93, 0x68, 0xb4, 0x43, 0x51, 0xe1, 0x68, 0xaa, 0x30, 0xf6, 0x12, 0x97, 0xfa, 0xf7, 0x05, 0x17,
0x0e, 0x42, 0xa8, 0x70, 0xe8, 0xcd, 0x58, 0x62, 0xb7, 0x77, 0x30, 0x7c, 0xc9, 0x85, 0x83, 0x19,
0x52, 0xf0, 0xc0, 0xe0, 0xa0, 0xf8, 0x8a, 0x15, 0xcc, 0xa0, 0xe2, 0x9e, 0x4e, 0xa3, 0x4d, 0xa4,
0xdf, 0x4e, 0x75, 0xe2, 0xe1, 0x6a, 0x8b, 0xea, 0xeb, 0xad, 0xee, 0x21, 0x6c, 0xd5, 0x40, 0xb1,
0x12, 0x85, 0x32, 0x4d, 0x3d, 0x5f, 0xe2, 0xc4, 0xe1, 0xb0, 0xb1, 0x6f, 0xb8, 0x12, 0x19, 0x58,
0x71, 0x3f, 0xc7, 0x7b, 0x66, 0x95, 0xea, 0x4d, 0xbb, 0x44, 0xcb, 0x05, 0xc3, 0xae, 0x47, 0xb7,
0xc9, 0xd5, 0x3d, 0xaa, 0x88, 0x3b, 0xf1, 0x00, 0x75, 0x0f, 0x14, 0x76, 0xd9, 0xb9, 0xed, 0xf2,
0x0c, 0x75, 0xcd, 0x13, 0xe2, 0x18, 0x8c, 0x76, 0x0d, 0x13, 0x76, 0xd5, 0x63, 0xa4, 0x1a, 0x31,
0x67, 0x09, 0x71, 0x08, 0xf6, 0xe0, 0x60, 0x60, 0xc7, 0x1f, 0x27, 0x3c, 0x5f, 0x2e, 0x8e, 0xc0,
0x10, 0x0f, 0x04, 0x76, 0xf4, 0x09, 0x42, 0x4b, 0x04, 0x71, 0x1e, 0x06, 0xec, 0xf8, 0x93, 0x8c,
0x33, 0x82, 0xb8, 0x7b, 0x08, 0xbf, 0x7d, 0x7a, 0x0f, 0x15, 0x74, 0x8e, 0xdd, 0x2c, 0xec, 0xa3,
0x29, 0xc0, 0x4e, 0x3f, 0x45, 0xff, 0x9c, 0x09, 0x71, 0x3b, 0xec, 0x75, 0x0c, 0xf8, 0x33, 0x84,
0x16, 0xeb, 0xc5, 0x02, 0x0c, 0x1b, 0x9d, 0xdf, 0x8e, 0x3f, 0x4b, 0xb8, 0x49, 0xe1, 0xd6, 0xa9,
0xf3, 0xdb, 0x05, 0xcf, 0xf1, 0xd6, 0x89, 0xc0, 0xb0, 0x71, 0xd3, 0xb7, 0xd3, 0xcf, 0x73, 0xd4,
0x19, 0x11, 0x73, 0x50, 0x29, 0x0b, 0xb9, 0x9d, 0x7f, 0x81, 0xf8, 0x0e, 0x83, 0x11, 0x30, 0x1a,
0x89, 0x5d, 0xf1, 0x22, 0x47, 0xc0, 0xa0, 0xf0, 0x1a, 0xf5, 0x0e, 0x07, 0x76, 0xd3, 0x4b, 0x7c,
0x8d, 0x7a, 0x66, 0x03, 0xcc, 0x66, 0x5e, 0x4f, 0xed, 0x8a, 0x97, 0x39, 0x9b, 0xf9, 0x7a, 0xdc,
0x46, 0x6f, 0xb7, 0xb5, 0x3b, 0x5e, 0xe1, 0x6d, 0xf4, 0x34, 0x5b, 0xb1, 0x02, 0xd5, 0xdd, 0x9d,
0xd6, 0xee, 0x7b, 0x95, 0x7c, 0x13, 0xbb, 0x1a, 0xad, 0xb8, 0x0f, 0xa6, 0xfb, 0x77, 0x59, 0xbb,
0xf5, 0xfc, 0x76, 0xcf, 0xe7, 0x22, 0xb3, 0xc9, 0x8a, 0x13, 0x9d, 0x72, 0x6d, 0x76, 0x58, 0xbb,
0xf6, 0xc2, 0x76, 0x77, 0xc5, 0x36, 0x1b, 0xac, 0x98, 0x07, 0xe8, 0x34, 0x37, 0xbb, 0xeb, 0x22,
0xb9, 0x0c, 0x08, 0xaf, 0x06, 0xf5, 0x36, 0x3b, 0x7f, 0x89, 0xaf, 0x06, 0x11, 0x78, 0x35, 0xb8,
0xad, 0xd9, 0xe9, 0xcb, 0x7c, 0x35, 0x18, 0xc1, 0x93, 0x6d, 0x74, 0x0e, 0xbb, 0xe1, 0x0a, 0x9f,
0x6c, 0x83, 0x12, 0xb3, 0x30, 0x14, 0x65, 0x41, 0x80, 0x07, 0xb4, 0x7a, 0x63, 0x9f, 0x76, 0x25,
0x83, 0x16, 0xf3, 0xbf, 0xec, 0xd0, 0x0e, 0x18, 0x10, 0x87, 0x60, 0xaf, 0x0c, 0x1b, 0xb2, 0x65,
0x23, 0x7f, 0xdd, 0xe1, 0xa2, 0x84, 0xab, 0xc5, 0x1c, 0x40, 0xf1, 0xd1, 0x1e, 0x5f, 0xc5, 0xc6,
0xfe, 0xb6, 0x53, 0x7c, 0xcb, 0x60, 0x20, 0x1d, 0x41, 0xfe, 0xe2, 0x16, 0xc1, 0x56, 0xb7, 0x20,
0x7f, 0xeb, 0xc3, 0xb0, 0xef, 0xe1, 0x54, 0x45, 0xda, 0xf3, 0x6d, 0xf4, 0xef, 0x44, 0xf3, 0x7a,
0x0c, 0x58, 0xa8, 0x12, 0xa9, 0x3d, 0x3f, 0xb5, 0xb1, 0x7f, 0x10, 0x5b, 0x02, 0x08, 0x37, 0xbd,
0x54, 0xbb, 0xbc, 0xf7, 0x9f, 0x0c, 0x33, 0x80, 0x9b, 0xc6, 0xe7, 0x53, 0x72, 0xd3, 0xc6, 0xfe,
0xc5, 0x9b, 0xa6, 0xf5, 0xe2, 0x08, 0x54, 0xf0, 0x31, 0xff, 0x56, 0xc4, 0x06, 0xff, 0x4d, 0x70,
0x87, 0xc0, 0xff, 0x9c, 0xea, 0x96, 0x6e, 0xdb, 0x83, 0xfd, 0x0f, 0x65, 0x9a, 0xd7, 0x8b, 0x79,
0x18, 0x4e, 0x75, 0xab, 0x95, 0xd1, 0x7c, 0x65, 0xc1, 0xff, 0xdd, 0x29, 0x3f, 0x72, 0x97, 0xcc,
0xd1, 0x75, 0x98, 0x6c, 0xaa, 0xb0, 0x17, 0x3c, 0x0a, 0x4b, 0x6a, 0x49, 0xad, 0xe4, 0x57, 0xf1,
0xc1, 0xdb, 0x7c, 0x75, 0x6b, 0x53, 0x85, 0xa1, 0x8a, 0x6a, 0x5e, 0x1c, 0xd7, 0xb4, 0x52, 0x41,
0xad, 0x11, 0xe6, 0x4b, 0x6b, 0xf1, 0x29, 0xbf, 0xd6, 0xa9, 0x46, 0xb5, 0x72, 0x2e, 0xfe, 0x2f,
0x00, 0x00, 0xff, 0xff, 0x97, 0xb1, 0x98, 0x88, 0x13, 0x14, 0x00, 0x00,
}

@ -0,0 +1,45 @@
// Code generated by protoc-gen-go.
// source: gogo.proto
// DO NOT EDIT!
package gogoproto
import proto "github.com/gogo/protobuf/proto"
import json "encoding/json"
import math "math"
import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
// Reference proto, json, and math imports to suppress error if they are not otherwise used.
var _ = proto.Marshal
var _ = &json.SyntaxError{}
var _ = math.Inf
var E_Nullable = &proto.ExtensionDesc{
ExtendedType: (*google_protobuf.FieldOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 51235,
Name: "gogoproto.nullable",
Tag: "varint,51235,opt,name=nullable",
}
var E_Embed = &proto.ExtensionDesc{
ExtendedType: (*google_protobuf.FieldOptions)(nil),
ExtensionType: (*bool)(nil),
Field: 51236,
Name: "gogoproto.embed",
Tag: "varint,51236,opt,name=embed",
}
var E_Customtype = &proto.ExtensionDesc{
ExtendedType: (*google_protobuf.FieldOptions)(nil),
ExtensionType: (*string)(nil),
Field: 51237,
Name: "gogoproto.customtype",
Tag: "bytes,51237,opt,name=customtype",
}
func init() {
proto.RegisterExtension(E_Nullable)
proto.RegisterExtension(E_Embed)
proto.RegisterExtension(E_Customtype)
}

@ -0,0 +1,136 @@
// Protocol Buffers for Go with Gadgets
//
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
// http://github.com/gogo/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto2";
package gogoproto;
import "google/protobuf/descriptor.proto";
option java_package = "com.google.protobuf";
option java_outer_classname = "GoGoProtos";
option go_package = "github.com/bilibili/kratos/tool/protobuf/pkg/extensions/gogoproto";
extend google.protobuf.EnumOptions {
optional bool goproto_enum_prefix = 62001;
optional bool goproto_enum_stringer = 62021;
optional bool enum_stringer = 62022;
optional string enum_customname = 62023;
optional bool enumdecl = 62024;
}
extend google.protobuf.EnumValueOptions {
optional string enumvalue_customname = 66001;
}
extend google.protobuf.FileOptions {
optional bool goproto_getters_all = 63001;
optional bool goproto_enum_prefix_all = 63002;
optional bool goproto_stringer_all = 63003;
optional bool verbose_equal_all = 63004;
optional bool face_all = 63005;
optional bool gostring_all = 63006;
optional bool populate_all = 63007;
optional bool stringer_all = 63008;
optional bool onlyone_all = 63009;
optional bool equal_all = 63013;
optional bool description_all = 63014;
optional bool testgen_all = 63015;
optional bool benchgen_all = 63016;
optional bool marshaler_all = 63017;
optional bool unmarshaler_all = 63018;
optional bool stable_marshaler_all = 63019;
optional bool sizer_all = 63020;
optional bool goproto_enum_stringer_all = 63021;
optional bool enum_stringer_all = 63022;
optional bool unsafe_marshaler_all = 63023;
optional bool unsafe_unmarshaler_all = 63024;
optional bool goproto_extensions_map_all = 63025;
optional bool goproto_unrecognized_all = 63026;
optional bool gogoproto_import = 63027;
optional bool protosizer_all = 63028;
optional bool compare_all = 63029;
optional bool typedecl_all = 63030;
optional bool enumdecl_all = 63031;
optional bool goproto_registration = 63032;
optional bool messagename_all = 63033;
}
extend google.protobuf.MessageOptions {
optional bool goproto_getters = 64001;
optional bool goproto_stringer = 64003;
optional bool verbose_equal = 64004;
optional bool face = 64005;
optional bool gostring = 64006;
optional bool populate = 64007;
optional bool stringer = 67008;
optional bool onlyone = 64009;
optional bool equal = 64013;
optional bool description = 64014;
optional bool testgen = 64015;
optional bool benchgen = 64016;
optional bool marshaler = 64017;
optional bool unmarshaler = 64018;
optional bool stable_marshaler = 64019;
optional bool sizer = 64020;
optional bool unsafe_marshaler = 64023;
optional bool unsafe_unmarshaler = 64024;
optional bool goproto_extensions_map = 64025;
optional bool goproto_unrecognized = 64026;
optional bool protosizer = 64028;
optional bool compare = 64029;
optional bool typedecl = 64030;
optional bool messagename = 64033;
}
extend google.protobuf.FieldOptions {
optional bool nullable = 65001;
optional bool embed = 65002;
optional string customtype = 65003;
optional string customname = 65004;
optional string jsontag = 65005;
optional string moretags = 65006;
optional string casttype = 65007;
optional string castkey = 65008;
optional string castvalue = 65009;
optional bool stdtime = 65010;
optional bool stdduration = 65011;
}

@ -0,0 +1,31 @@
// Copyright (c) 2015, Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package google.api;
import "google/api/http.proto";
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "AnnotationsProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.MethodOptions {
// See `HttpRule`.
HttpRule http = 72295728;
}

@ -0,0 +1,318 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package google.api;
option cc_enable_arenas = true;
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "HttpProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Defines the HTTP configuration for an API service. It contains a list of
// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
// to one or more HTTP REST API methods.
message Http {
// A list of HTTP configuration rules that apply to individual API methods.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
repeated HttpRule rules = 1;
// When set to true, URL path parmeters will be fully URI-decoded except in
// cases of single segment matches in reserved expansion, where "%2F" will be
// left encoded.
//
// The default behavior is to not decode RFC 6570 reserved characters in multi
// segment matches.
bool fully_decode_reserved_expansion = 2;
}
// `HttpRule` defines the mapping of an RPC method to one or more HTTP
// REST API methods. The mapping specifies how different portions of the RPC
// request message are mapped to URL path, URL query parameters, and
// HTTP request body. The mapping is typically specified as an
// `google.api.http` annotation on the RPC method,
// see "google/api/annotations.proto" for details.
//
// The mapping consists of a field specifying the path template and
// method kind. The path template can refer to fields in the request
// message, as in the example below which describes a REST GET
// operation on a resource collection of messages:
//
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}";
// }
// }
// message GetMessageRequest {
// message SubMessage {
// string subfield = 1;
// }
// string message_id = 1; // mapped to the URL
// SubMessage sub = 2; // `sub.subfield` is url-mapped
// }
// message Message {
// string text = 1; // content of the resource
// }
//
// The same http annotation can alternatively be expressed inside the
// `GRPC API Configuration` YAML file.
//
// http:
// rules:
// - selector: <proto_package_name>.Messaging.GetMessage
// get: /v1/messages/{message_id}/{sub.subfield}
//
// This definition enables an automatic, bidrectional mapping of HTTP
// JSON to RPC. Example:
//
// HTTP | RPC
// -----|-----
// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))`
//
// In general, not only fields but also field paths can be referenced
// from a path pattern. Fields mapped to the path pattern cannot be
// repeated and must have a primitive (non-message) type.
//
// Any fields in the request message which are not bound by the path
// pattern automatically become (optional) HTTP query
// parameters. Assume the following definition of the request message:
//
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http).get = "/v1/messages/{message_id}";
// }
// }
// message GetMessageRequest {
// message SubMessage {
// string subfield = 1;
// }
// string message_id = 1; // mapped to the URL
// int64 revision = 2; // becomes a parameter
// SubMessage sub = 3; // `sub.subfield` becomes a parameter
// }
//
//
// This enables a HTTP JSON to RPC mapping as below:
//
// HTTP | RPC
// -----|-----
// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))`
//
// Note that fields which are mapped to HTTP parameters must have a
// primitive type or a repeated primitive type. Message types are not
// allowed. In the case of a repeated type, the parameter can be
// repeated in the URL, as in `...?param=A&param=B`.
//
// For HTTP method kinds which allow a request body, the `body` field
// specifies the mapping. Consider a REST update method on the
// message resource collection:
//
//
// service Messaging {
// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
// option (google.api.http) = {
// put: "/v1/messages/{message_id}"
// body: "message"
// };
// }
// }
// message UpdateMessageRequest {
// string message_id = 1; // mapped to the URL
// Message message = 2; // mapped to the body
// }
//
//
// The following HTTP JSON to RPC mapping is enabled, where the
// representation of the JSON in the request body is determined by
// protos JSON encoding:
//
// HTTP | RPC
// -----|-----
// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })`
//
// The special name `*` can be used in the body mapping to define that
// every field not bound by the path template should be mapped to the
// request body. This enables the following alternative definition of
// the update method:
//
// service Messaging {
// rpc UpdateMessage(Message) returns (Message) {
// option (google.api.http) = {
// put: "/v1/messages/{message_id}"
// body: "*"
// };
// }
// }
// message Message {
// string message_id = 1;
// string text = 2;
// }
//
//
// The following HTTP JSON to RPC mapping is enabled:
//
// HTTP | RPC
// -----|-----
// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")`
//
// Note that when using `*` in the body mapping, it is not possible to
// have HTTP parameters, as all fields not bound by the path end in
// the body. This makes this option more rarely used in practice of
// defining REST APIs. The common usage of `*` is in custom methods
// which don't use the URL at all for transferring data.
//
// It is possible to define multiple HTTP methods for one RPC by using
// the `additional_bindings` option. Example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get: "/v1/messages/{message_id}"
// additional_bindings {
// get: "/v1/users/{user_id}/messages/{message_id}"
// }
// };
// }
// }
// message GetMessageRequest {
// string message_id = 1;
// string user_id = 2;
// }
//
//
// This enables the following two alternative HTTP JSON to RPC
// mappings:
//
// HTTP | RPC
// -----|-----
// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")`
//
// # Rules for HTTP mapping
//
// The rules for mapping HTTP path, query parameters, and body fields
// to the request message are as follows:
//
// 1. The `body` field specifies either `*` or a field path, or is
// omitted. If omitted, it indicates there is no HTTP request body.
// 2. Leaf fields (recursive expansion of nested messages in the
// request) can be classified into three types:
// (a) Matched in the URL template.
// (b) Covered by body (if body is `*`, everything except (a) fields;
// else everything under the body field)
// (c) All other fields.
// 3. URL query parameters found in the HTTP request are mapped to (c) fields.
// 4. Any body sent with an HTTP request can contain only (b) fields.
//
// The syntax of the path template is as follows:
//
// Template = "/" Segments [ Verb ] ;
// Segments = Segment { "/" Segment } ;
// Segment = "*" | "**" | LITERAL | Variable ;
// Variable = "{" FieldPath [ "=" Segments ] "}" ;
// FieldPath = IDENT { "." IDENT } ;
// Verb = ":" LITERAL ;
//
// The syntax `*` matches a single path segment. The syntax `**` matches zero
// or more path segments, which must be the last part of the path except the
// `Verb`. The syntax `LITERAL` matches literal text in the path.
//
// The syntax `Variable` matches part of the URL path as specified by its
// template. A variable template must not contain other variables. If a variable
// matches a single path segment, its template may be omitted, e.g. `{var}`
// is equivalent to `{var=*}`.
//
// If a variable contains exactly one path segment, such as `"{var}"` or
// `"{var=*}"`, when such a variable is expanded into a URL path, all characters
// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the
// Discovery Document as `{var}`.
//
// If a variable contains one or more path segments, such as `"{var=foo/*}"`
// or `"{var=**}"`, when such a variable is expanded into a URL path, all
// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables
// show up in the Discovery Document as `{+var}`.
//
// NOTE: While the single segment variable matches the semantics of
// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2
// Simple String Expansion, the multi segment variable **does not** match
// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion
// does not expand special characters like `?` and `#`, which would lead
// to invalid URLs.
//
// NOTE: the field paths in variables and in the `body` must not refer to
// repeated fields or map fields.
message HttpRule {
// Selects methods to which this rule applies.
//
// Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
string selector = 1;
// Determines the URL pattern is matched by this rules. This pattern can be
// used with any of the {get|put|post|delete|patch} methods. A custom method
// can be defined using the 'custom' field.
oneof pattern {
// Used for listing and getting information about resources.
string get = 2;
// Used for updating a resource.
string put = 3;
// Used for creating a resource.
string post = 4;
// Used for deleting a resource.
string delete = 5;
// Used for updating a resource.
string patch = 6;
// The custom pattern is used for specifying an HTTP method that is not
// included in the `pattern` field, such as HEAD, or "*" to leave the
// HTTP method unspecified for this rule. The wild-card rule is useful
// for services that provide content to Web (HTML) clients.
CustomHttpPattern custom = 8;
}
// The name of the request field whose value is mapped to the HTTP body, or
// `*` for mapping all fields not captured by the path pattern to the HTTP
// body. NOTE: the referred field must not be a repeated field and must be
// present at the top-level of request message type.
string body = 7;
// Optional. The name of the response field whose value is mapped to the HTTP
// body of response. Other response fields are ignored. When
// not set, the response message will be used as HTTP body of response.
string response_body = 12;
// Additional HTTP bindings for the selector. Nested bindings must
// not contain an `additional_bindings` field themselves (that is,
// the nesting may only be one level deep).
repeated HttpRule additional_bindings = 11;
}
// A custom pattern is used for defining custom HTTP verb.
message CustomHttpPattern {
// The name of this custom HTTP verb.
string kind = 1;
// The path matched by this custom verb.
string path = 2;
}

@ -0,0 +1,94 @@
package gen
import (
"io"
"io/ioutil"
"os"
"strings"
"log"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
// Generator ...
type Generator interface {
Generate(in *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse
}
// Main ...
func Main(g Generator) {
req := readGenRequest()
resp := g.Generate(req)
writeResponse(os.Stdout, resp)
}
// FilesToGenerate ...
func FilesToGenerate(req *plugin.CodeGeneratorRequest) []*descriptor.FileDescriptorProto {
genFiles := make([]*descriptor.FileDescriptorProto, 0)
Outer:
for _, name := range req.FileToGenerate {
for _, f := range req.ProtoFile {
if f.GetName() == name {
genFiles = append(genFiles, f)
continue Outer
}
}
Fail("could not find file named", name)
}
return genFiles
}
func readGenRequest() *plugin.CodeGeneratorRequest {
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
Error(err, "reading input")
}
req := new(plugin.CodeGeneratorRequest)
if err = proto.Unmarshal(data, req); err != nil {
Error(err, "parsing input proto")
}
if len(req.FileToGenerate) == 0 {
Fail("no files to generate")
}
return req
}
func writeResponse(w io.Writer, resp *plugin.CodeGeneratorResponse) {
data, err := proto.Marshal(resp)
if err != nil {
Error(err, "marshaling response")
}
_, err = w.Write(data)
if err != nil {
Error(err, "writing response")
}
}
// Fail log and exit
func Fail(msgs ...string) {
s := strings.Join(msgs, " ")
log.Print("error:", s)
os.Exit(1)
}
// Fail log and exit
func Info(msgs ...string) {
s := strings.Join(msgs, " ")
log.Print("info:", s)
os.Exit(1)
}
// Error log and exit
func Error(err error, msgs ...string) {
s := strings.Join(msgs, " ") + ":" + err.Error()
log.Print("error:", s)
os.Exit(1)
}

@ -0,0 +1,71 @@
package generator
import (
"fmt"
"strings"
)
type ParamsBase struct {
ImportPrefix string // String to prefix to imported package file names.
ImportMap map[string]string // Mapping from .proto file name to import path.
//Tpl bool // generate service implementation template
ExplicitHTTP bool // Only generate for method that add http option
}
type GeneratorParamsInterface interface {
GetBase() *ParamsBase
SetParam(key string, value string) error
}
type BasicParam struct{ ParamsBase }
func (b *BasicParam) GetBase() *ParamsBase {
return &b.ParamsBase
}
func (b *BasicParam) SetParam(key string, value string) error {
return nil
}
func ParseGeneratorParams(parameter string, result GeneratorParamsInterface) error {
ps := make(map[string]string)
for _, p := range strings.Split(parameter, ",") {
if p == "" {
continue
}
i := strings.Index(p, "=")
if i < 0 {
return fmt.Errorf("invalid parameter %q: expected format of parameter to be k=v", p)
}
k := p[0:i]
v := p[i+1:]
if v == "" {
return fmt.Errorf("invalid parameter %q: expected format of parameter to be k=v", k)
}
ps[k] = v
}
if result.GetBase().ImportMap == nil {
result.GetBase().ImportMap = map[string]string{}
}
for k, v := range ps {
switch {
case k == "explicit_http":
if v == "true" || v == "1" {
result.GetBase().ExplicitHTTP = true
}
case k == "import_prefix":
result.GetBase().ImportPrefix = v
// Support import map 'M' prefix per https://github.com/golang/protobuf/blob/6fb5325/protoc-gen-go/generator/generator.go#L497.
case len(k) > 0 && k[0] == 'M':
result.GetBase().ImportMap[k[1:]] = v // 1 is the length of 'M'.
case len(k) > 0 && strings.HasPrefix(k, "go_import_mapping@"):
result.GetBase().ImportMap[k[18:]] = v // 18 is the length of 'go_import_mapping@'.
default:
e := result.SetParam(k, v)
if e != nil {
return e
}
}
}
return nil
}

@ -0,0 +1,310 @@
package generator
import (
"bufio"
"bytes"
"fmt"
"go/parser"
"go/printer"
"go/token"
"path"
"strconv"
"strings"
"github.com/bilibili/kratos/tool/protobuf/pkg/gen"
"github.com/bilibili/kratos/tool/protobuf/pkg/naming"
"github.com/bilibili/kratos/tool/protobuf/pkg/typemap"
"github.com/bilibili/kratos/tool/protobuf/pkg/utils"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/pkg/errors"
)
const Version = "v0.1"
var GoModuleImportPath = "github.com/bilibili/kratos"
var GoModuleDirName = "github.com/bilibili/kratos"
type Base struct {
Reg *typemap.Registry
// Map to record whether we've built each package
// pkgName => alias name
pkgs map[string]string
pkgNamesInUse map[string]bool
ImportPrefix string // String to prefix to imported package file names.
importMap map[string]string // Mapping from .proto file name to import path.
// Package naming:
GenPkgName string // Name of the package that we're generating
PackageName string // Name of the proto file package
fileToGoPackageName map[*descriptor.FileDescriptorProto]string
// List of files that were inputs to the generator. We need to hold this in
// the struct so we can write a header for the file that lists its inputs.
GenFiles []*descriptor.FileDescriptorProto
// Output buffer that holds the bytes we want to write out for a single file.
// Gets reset after working on a file.
Output *bytes.Buffer
// key: pkgName
// value: importPath
Deps map[string]string
Params *ParamsBase
httpInfoCache map[string]*HTTPInfo
}
// RegisterPackageName name is the go package name or proto pkg name
// return go pkg alias
func (t *Base) RegisterPackageName(name string) (alias string) {
alias = name
i := 1
for t.pkgNamesInUse[alias] {
alias = name + strconv.Itoa(i)
i++
}
t.pkgNamesInUse[alias] = true
t.pkgs[name] = alias
return alias
}
func (t *Base) Setup(in *plugin.CodeGeneratorRequest, paramsOpt ...GeneratorParamsInterface) {
t.httpInfoCache = make(map[string]*HTTPInfo)
t.pkgs = make(map[string]string)
t.pkgNamesInUse = make(map[string]bool)
t.importMap = make(map[string]string)
t.Deps = make(map[string]string)
t.fileToGoPackageName = make(map[*descriptor.FileDescriptorProto]string)
t.Output = bytes.NewBuffer(nil)
var params GeneratorParamsInterface
if len(paramsOpt) > 0 {
params = paramsOpt[0]
} else {
params = &BasicParam{}
}
err := ParseGeneratorParams(in.GetParameter(), params)
if err != nil {
gen.Fail("could not parse parameters", err.Error())
}
t.Params = params.GetBase()
t.ImportPrefix = params.GetBase().ImportPrefix
t.importMap = params.GetBase().ImportMap
t.GenFiles = gen.FilesToGenerate(in)
// Collect information on types.
t.Reg = typemap.New(in.ProtoFile)
t.RegisterPackageName("context")
t.RegisterPackageName("ioutil")
t.RegisterPackageName("proto")
// Time to figure out package names of objects defined in protobuf. First,
// we'll figure out the name for the package we're generating.
genPkgName, err := DeduceGenPkgName(t.GenFiles)
if err != nil {
gen.Fail(err.Error())
}
t.GenPkgName = genPkgName
// Next, we need to pick names for all the files that are dependencies.
if len(in.ProtoFile) > 0 {
t.PackageName = t.GenFiles[0].GetPackage()
}
for _, f := range in.ProtoFile {
if fileDescSliceContains(t.GenFiles, f) {
// This is a file we are generating. It gets the shared package name.
t.fileToGoPackageName[f] = t.GenPkgName
} else {
// This is a dependency. Use its package name.
name := f.GetPackage()
if name == "" {
name = utils.BaseName(f.GetName())
}
name = utils.CleanIdentifier(name)
alias := t.RegisterPackageName(name)
t.fileToGoPackageName[f] = alias
}
}
for _, f := range t.GenFiles {
deps := t.DeduceDeps(f)
for k, v := range deps {
t.Deps[k] = v
}
}
}
func (t *Base) DeduceDeps(file *descriptor.FileDescriptorProto) map[string]string {
deps := make(map[string]string) // Map of package name to quoted import path.
ourImportPath := path.Dir(naming.GoFileName(file, ""))
for _, s := range file.Service {
for _, m := range s.Method {
defs := []*typemap.MessageDefinition{
t.Reg.MethodInputDefinition(m),
t.Reg.MethodOutputDefinition(m),
}
for _, def := range defs {
if def.File.GetPackage() == t.PackageName {
continue
}
// By default, import path is the dirname of the Go filename.
importPath := path.Dir(naming.GoFileName(def.File, ""))
if importPath == ourImportPath {
continue
}
importPath = t.SubstituteImportPath(importPath, def.File.GetName())
importPath = t.ImportPrefix + importPath
pkg := t.GoPackageNameForProtoFile(def.File)
deps[pkg] = strconv.Quote(importPath)
}
}
}
return deps
}
// DeduceGenPkgName figures out the go package name to use for generated code.
// Will try to use the explicit go_package setting in a file (if set, must be
// consistent in all files). If no files have go_package set, then use the
// protobuf package name (must be consistent in all files)
func DeduceGenPkgName(genFiles []*descriptor.FileDescriptorProto) (string, error) {
var genPkgName string
for _, f := range genFiles {
name, explicit := naming.GoPackageName(f)
if explicit {
name = utils.CleanIdentifier(name)
if genPkgName != "" && genPkgName != name {
// Make sure they're all set consistently.
return "", errors.Errorf("files have conflicting go_package settings, must be the same: %q and %q", genPkgName, name)
}
genPkgName = name
}
}
if genPkgName != "" {
return genPkgName, nil
}
// If there is no explicit setting, then check the implicit package name
// (derived from the protobuf package name) of the files and make sure it's
// consistent.
for _, f := range genFiles {
name, _ := naming.GoPackageName(f)
name = utils.CleanIdentifier(name)
if genPkgName != "" && genPkgName != name {
return "", errors.Errorf("files have conflicting package names, must be the same or overridden with go_package: %q and %q", genPkgName, name)
}
genPkgName = name
}
// All the files have the same name, so we're good.
return genPkgName, nil
}
func (t *Base) GoPackageNameForProtoFile(file *descriptor.FileDescriptorProto) string {
return t.fileToGoPackageName[file]
}
func fileDescSliceContains(slice []*descriptor.FileDescriptorProto, f *descriptor.FileDescriptorProto) bool {
for _, sf := range slice {
if f == sf {
return true
}
}
return false
}
// P forwards to g.gen.P, which prints output.
func (t *Base) P(args ...string) {
for _, v := range args {
t.Output.WriteString(v)
}
t.Output.WriteByte('\n')
}
func (t *Base) FormattedOutput() string {
// Reformat generated code.
fset := token.NewFileSet()
raw := t.Output.Bytes()
ast, err := parser.ParseFile(fset, "", raw, parser.ParseComments)
if err != nil {
// Print out the bad code with line numbers.
// This should never happen in practice, but it can while changing generated code,
// so consider this a debugging aid.
var src bytes.Buffer
s := bufio.NewScanner(bytes.NewReader(raw))
for line := 1; s.Scan(); line++ {
fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes())
}
gen.Fail("bad Go source code was generated:", err.Error(), "\n"+src.String())
}
out := bytes.NewBuffer(nil)
err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(out, fset, ast)
if err != nil {
gen.Fail("generated Go source code could not be reformatted:", err.Error())
}
return out.String()
}
func (t *Base) PrintComments(comments typemap.DefinitionComments) bool {
text := strings.TrimSuffix(comments.Leading, "\n")
if len(strings.TrimSpace(text)) == 0 {
return false
}
split := strings.Split(text, "\n")
for _, line := range split {
t.P("// ", strings.TrimPrefix(line, " "))
}
return len(split) > 0
}
// IsOwnPackage ...
// protoName is fully qualified name of a type
func (t *Base) IsOwnPackage(protoName string) bool {
def := t.Reg.MessageDefinition(protoName)
if def == nil {
gen.Fail("could not find message for", protoName)
}
return def.File.GetPackage() == t.PackageName
}
// Given a protobuf name for a Message, return the Go name we will use for that
// type, including its package prefix.
func (t *Base) GoTypeName(protoName string) string {
def := t.Reg.MessageDefinition(protoName)
if def == nil {
gen.Fail("could not find message for", protoName)
}
var prefix string
if def.File.GetPackage() != t.PackageName {
prefix = t.GoPackageNameForProtoFile(def.File) + "."
}
var name string
for _, parent := range def.Lineage() {
name += parent.Descriptor.GetName() + "_"
}
name += def.Descriptor.GetName()
return prefix + name
}
func (t *Base) ShouldGenForMethod(file *descriptor.FileDescriptorProto,
service *descriptor.ServiceDescriptorProto,
method *descriptor.MethodDescriptorProto) bool {
if !t.Params.ExplicitHTTP {
return true
}
httpInfo := t.GetHttpInfoCached(file, service, method)
return httpInfo.HasExplicitHTTPPath
}
func (t *Base) SubstituteImportPath(importPath string, importFile string) string {
if substitution, ok := t.importMap[importFile]; ok {
importPath = substitution
}
return importPath
}

@ -0,0 +1,136 @@
package generator
import (
"reflect"
"strings"
"github.com/bilibili/kratos/tool/protobuf/pkg/extensions/gogoproto"
"github.com/bilibili/kratos/tool/protobuf/pkg/tag"
"github.com/bilibili/kratos/tool/protobuf/pkg/typemap"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
)
// GetJSONFieldName get name from gogoproto.jsontag
// else the original name
func GetJSONFieldName(field *descriptor.FieldDescriptorProto) string {
if field == nil {
return ""
}
if field.Options != nil {
v, err := proto.GetExtension(field.Options, gogoproto.E_Jsontag)
if err == nil && v.(*string) != nil {
ret := *(v.(*string))
i := strings.Index(ret, ",")
if i != -1 {
ret = ret[:i]
}
return ret
}
}
return field.GetName()
}
// GetFormOrJSONName get name from form tag, then json tag
// then original name
func GetFormOrJSONName(field *descriptor.FieldDescriptorProto) string {
if field == nil {
return ""
}
tags := tag.GetMoreTags(field)
if tags != nil {
tag := reflect.StructTag(*tags)
fName := tag.Get("form")
if fName != "" {
i := strings.Index(fName, ",")
if i != -1 {
fName = fName[:i]
}
return fName
}
}
return GetJSONFieldName(field)
}
// IsScalar Is this field a scalar numeric type?
func IsScalar(field *descriptor.FieldDescriptorProto) bool {
if field.Type == nil {
return false
}
switch *field.Type {
case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
descriptor.FieldDescriptorProto_TYPE_FLOAT,
descriptor.FieldDescriptorProto_TYPE_INT64,
descriptor.FieldDescriptorProto_TYPE_UINT64,
descriptor.FieldDescriptorProto_TYPE_INT32,
descriptor.FieldDescriptorProto_TYPE_FIXED64,
descriptor.FieldDescriptorProto_TYPE_FIXED32,
descriptor.FieldDescriptorProto_TYPE_BOOL,
descriptor.FieldDescriptorProto_TYPE_UINT32,
descriptor.FieldDescriptorProto_TYPE_ENUM,
descriptor.FieldDescriptorProto_TYPE_SFIXED32,
descriptor.FieldDescriptorProto_TYPE_SFIXED64,
descriptor.FieldDescriptorProto_TYPE_SINT32,
descriptor.FieldDescriptorProto_TYPE_SINT64,
descriptor.FieldDescriptorProto_TYPE_BYTES,
descriptor.FieldDescriptorProto_TYPE_STRING:
return true
default:
return false
}
}
// IsMap is protocol buffer map
func IsMap(field *descriptor.FieldDescriptorProto, reg *typemap.Registry) bool {
if field.GetType() != descriptor.FieldDescriptorProto_TYPE_MESSAGE {
return false
}
md := reg.MessageDefinition(field.GetTypeName())
if md == nil || !md.Descriptor.GetOptions().GetMapEntry() {
return false
}
return true
}
// IsRepeated Is this field repeated?
func IsRepeated(field *descriptor.FieldDescriptorProto) bool {
return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
}
// GetFieldRequired is field required?
// eg. validate="required"
func GetFieldRequired(
f *descriptor.FieldDescriptorProto,
reg *typemap.Registry,
md *typemap.MessageDefinition,
) bool {
fComment, _ := reg.FieldComments(md, f)
var tags []reflect.StructTag
{
//get required info from gogoproto.moretags
moretags := tag.GetMoreTags(f)
if moretags != nil {
tags = []reflect.StructTag{reflect.StructTag(*moretags)}
}
}
if len(tags) == 0 {
tags = tag.GetTagsInComment(fComment.Leading)
}
validateTag := tag.GetTagValue("validate", tags)
var validateRules []string
if validateTag != "" {
validateRules = strings.Split(validateTag, ",")
}
required := false
for _, rule := range validateRules {
if rule == "required" {
required = true
}
}
return required
}
func MakeIndentStr(i int) string {
return strings.Repeat(" ", i)
}

@ -0,0 +1,145 @@
package generator
import (
"fmt"
"net/http"
"strings"
"github.com/bilibili/kratos/tool/protobuf/pkg/tag"
"github.com/bilibili/kratos/tool/protobuf/pkg/typemap"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"google.golang.org/genproto/googleapis/api/annotations"
)
// HTTPInfo http info for method
type HTTPInfo struct {
HttpMethod string
Path string
LegacyPath string
NewPath string
IsLegacyPath bool
Title string
Description string
// is http path added in the google.api.http option ?
HasExplicitHTTPPath bool
}
type googleMethodOptionInfo struct {
Method string
PathPattern string
HTTPRule *annotations.HttpRule
}
// GetHTTPInfo http info of method
func GetHTTPInfo(
file *descriptor.FileDescriptorProto,
service *descriptor.ServiceDescriptorProto,
method *descriptor.MethodDescriptorProto,
reg *typemap.Registry) *HTTPInfo {
var (
title string
desc string
httpMethod string
newPath string
explicitHTTPPath bool
)
comment, _ := reg.MethodComments(file, service, method)
tags := tag.GetTagsInComment(comment.Leading)
cleanComments := tag.GetCommentWithoutTag(comment.Leading)
if len(cleanComments) > 0 {
title = strings.Trim(cleanComments[0], "\n\r ")
if len(cleanComments) > 1 {
descLines := cleanComments[1:]
desc = strings.Trim(strings.Join(descLines, "\n"), "\r\n ")
} else {
desc = ""
}
} else {
title = ""
}
googleOptionInfo, err := ParseBMMethod(method)
if err == nil {
httpMethod = strings.ToUpper(googleOptionInfo.Method)
p := googleOptionInfo.PathPattern
if p != "" {
explicitHTTPPath = true
newPath = p
goto END
}
}
if httpMethod == "" {
// resolve http method
httpMethod = tag.GetTagValue("method", tags)
if httpMethod == "" {
httpMethod = "GET"
} else {
httpMethod = strings.ToUpper(httpMethod)
}
}
newPath = "/" + file.GetPackage() + "." + service.GetName() + "/" + method.GetName()
END:
var p = newPath
param := &HTTPInfo{HttpMethod: httpMethod,
Path: p,
NewPath: newPath,
IsLegacyPath: false,
Title: title,
Description: desc,
HasExplicitHTTPPath: explicitHTTPPath,
}
if title == "" {
param.Title = param.Path
}
return param
}
func (t *Base) GetHttpInfoCached(file *descriptor.FileDescriptorProto,
service *descriptor.ServiceDescriptorProto,
method *descriptor.MethodDescriptorProto) *HTTPInfo {
key := file.GetPackage() + service.GetName() + method.GetName()
httpInfo, ok := t.httpInfoCache[key]
if !ok {
httpInfo = GetHTTPInfo(file, service, method, t.Reg)
t.httpInfoCache[key] = httpInfo
}
return httpInfo
}
// ParseBMMethod parse BMMethodDescriptor form method descriptor proto
func ParseBMMethod(method *descriptor.MethodDescriptorProto) (*googleMethodOptionInfo, error) {
ext, err := proto.GetExtension(method.GetOptions(), annotations.E_Http)
if err != nil {
return nil, fmt.Errorf("get extension error: %s", err)
}
rule := ext.(*annotations.HttpRule)
var httpMethod string
var pathPattern string
switch pattern := rule.Pattern.(type) {
case *annotations.HttpRule_Get:
pathPattern = pattern.Get
httpMethod = http.MethodGet
case *annotations.HttpRule_Put:
pathPattern = pattern.Put
httpMethod = http.MethodPut
case *annotations.HttpRule_Post:
pathPattern = pattern.Post
httpMethod = http.MethodPost
case *annotations.HttpRule_Patch:
pathPattern = pattern.Patch
httpMethod = http.MethodPatch
case *annotations.HttpRule_Delete:
pathPattern = pattern.Delete
httpMethod = http.MethodDelete
default:
return nil, fmt.Errorf("unsupport http pattern %s", rule.Pattern)
}
bmMethod := &googleMethodOptionInfo{
Method: httpMethod,
PathPattern: pathPattern,
HTTPRule: rule,
}
return bmMethod, nil
}

@ -0,0 +1,27 @@
package naming
import (
"path"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
)
// GoFileName returns the output name for the generated Go file.
func GoFileName(f *descriptor.FileDescriptorProto, suffix string) string {
name := *f.Name
if ext := path.Ext(name); ext == ".pb" || ext == ".proto" || ext == ".protodevel" {
name = name[:len(name)-len(ext)]
}
name += suffix
// Does the file have a "go_package" option? If it does, it may override the
// filename.
if impPath, _, ok := goPackageOption(f); ok && impPath != "" {
// Replace the existing dirname with the declared import path.
_, name = path.Split(name)
name = path.Join(impPath, name)
return name
}
return name
}

@ -0,0 +1,101 @@
package naming
import (
"os"
"path/filepath"
"strings"
"github.com/bilibili/kratos/tool/protobuf/pkg/utils"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/pkg/errors"
"github.com/siddontang/go/ioutil2"
)
// GetVersionPrefix 根据go包名获取api版本前缀
// @param pkg 从proto获取到的对应的go报名
// @return 如果是v*开始的 返回v*
// 否则返回空
func GetVersionPrefix(pkg string) string {
if pkg == "" {
return ""
}
if pkg[:1] == "v" {
return pkg
}
return ""
}
func ServiceName(service *descriptor.ServiceDescriptorProto) string {
return utils.CamelCase(service.GetName())
}
// MethodName ...
func MethodName(method *descriptor.MethodDescriptorProto) string {
return utils.CamelCase(method.GetName())
}
// GetGoImportPathForPb 得到 proto 文件对应的 go import路径
// protoFilename is the proto file name
// 可能根本无法得到proto文件的具体路径, 只能假设 proto 的filename 是相对当前目录的
// 假设 protoAbsolutePath = wd/protoFilename
func GetGoImportPathForPb(protoFilename string, moduleImportPath string, moduleDirName string) (importPath string, err error) {
wd, err := os.Getwd()
if err != nil {
panic("cannot get working directory")
}
absPath := wd + "/" + protoFilename
if !ioutil2.FileExists(absPath) {
err = errors.New("Cannot find proto file path of " + protoFilename)
return "", err
}
index := strings.Index(absPath, moduleDirName)
if index == -1 {
return "", errors.Errorf("proto file %s is not inside project %s", protoFilename, moduleDirName)
}
relativePath := absPath[index:]
importPath = filepath.Dir(relativePath)
return importPath, nil
}
// GoPackageNameForProtoFile returns the Go package name to use in the generated Go file.
// The result explicitly reports whether the name came from an option go_package
// statement. If explicit is false, the name was derived from the protocol
// buffer's package statement or the input file name.
func GoPackageName(f *descriptor.FileDescriptorProto) (name string, explicit bool) {
// Does the file have a "go_package" option?
if _, pkg, ok := goPackageOption(f); ok {
return pkg, true
}
// Does the file have a package clause?
if pkg := f.GetPackage(); pkg != "" {
return pkg, false
}
// Use the file base name.
return utils.BaseName(f.GetName()), false
}
// goPackageOption interprets the file's go_package option.
// If there is no go_package, it returns ("", "", false).
// If there's a simple name, it returns ("", pkg, true).
// If the option implies an import path, it returns (impPath, pkg, true).
func goPackageOption(f *descriptor.FileDescriptorProto) (impPath, pkg string, ok bool) {
pkg = f.GetOptions().GetGoPackage()
if pkg == "" {
return
}
ok = true
// The presence of a slash implies there's an import path.
slash := strings.LastIndex(pkg, "/")
if slash < 0 {
return
}
impPath, pkg = pkg, pkg[slash+1:]
// A semicolon-delimited suffix overrides the package name.
sc := strings.IndexByte(impPath, ';')
if sc < 0 {
return
}
impPath, pkg = impPath[:sc], impPath[sc+1:]
return
}

@ -0,0 +1,119 @@
package project
import (
"os"
"path/filepath"
"strings"
"github.com/bilibili/kratos/tool/protobuf/pkg/utils"
"github.com/pkg/errors"
"github.com/siddontang/go/ioutil2"
)
// if proto file is inside a project (that has a /api directory)
// this present a project info
// 必须假设proto文件的路径就是相对work-dir的路径,否则无法找到proto文件以及对应的project
type ProjectInfo struct {
// AbsolutePath of the project
AbsolutePath string
// ImportPath of project
ImportPath string
// dir name of project
Name string
// parent dir of project, maybe empty
Department string
// grandma dir of project , maybe empty
Typ string
HasInternalPkg bool
// 从工作目录(working directory)到project目录的相对路径 比如a/b .. ../a
// 作用是什么?
// 假设目录结构是
// -project
// - api/api.proto
// - internal/service
// 我想在 internal/service下生成一个文件 service.go
// work-dir 为 project/api
// proto 生成命令为 protoc --xx_out=. api.proto
// 那么在 protoc plugin 中的文件输出路径就得是 ../internal/service/ => {pathRefToProj}internal/service
//
PathRefToProj string
}
func NewProjInfo(file string, modDirName string, modImportPath string) (projInfo *ProjectInfo, err error) {
projInfo = &ProjectInfo{}
wd, err := os.Getwd()
if err != nil {
panic("cannot get working directory")
}
protoAbs := wd + "/" + file
protoAbs, _ = filepath.Abs(protoAbs)
if !ioutil2.FileExists(protoAbs) {
return nil, errors.Errorf("Cannot find proto file in current dir %s, file: %s ", wd, file)
}
//appIndex := strings.Index(wd, modDirName)
//if appIndex == -1 {
// err = errors.New("not in " + modDirName)
// return nil, err
//}
projPath := LookupProjPath(protoAbs)
if projPath == "" {
err = errors.New("not in project")
return nil, err
}
rel, _ := filepath.Rel(wd, projPath)
projInfo.PathRefToProj = rel
projInfo.AbsolutePath = projPath
if ioutil2.FileExists(projPath + "/internal") {
projInfo.HasInternalPkg = true
}
i := strings.Index(projInfo.AbsolutePath, modDirName)
if i == -1 {
err = errors.Errorf("project is not inside module, project=%s, module=%s", projPath, modDirName)
}
relativePath := projInfo.AbsolutePath[i+len(modDirName):]
projInfo.ImportPath = modImportPath + relativePath
projInfo.Name = filepath.Base(projPath)
if p := filepath.Dir(projPath); p != "/" {
projInfo.Department = filepath.Base(p)
if p = filepath.Dir(p); p != "/" {
projInfo.Typ = filepath.Base(p)
}
}
return projInfo, nil
}
// LookupProjPath get project path by proto absolute path
// assume that proto is in the project's api directory
func LookupProjPath(protoAbs string) (result string) {
f := func(protoAbs string, dirs []string) string {
lastIndex := len(protoAbs)
curPath := protoAbs
for lastIndex > 0 {
found := true
for _, d := range dirs {
if !utils.IsDir(curPath + "/" + d) {
found = false
break
}
}
if found {
return curPath
}
lastIndex = strings.LastIndex(curPath, string(os.PathSeparator))
curPath = protoAbs[:lastIndex]
}
result = ""
return result
}
firstStep := f(protoAbs, []string{"cmd", "api"})
if firstStep != "" {
return firstStep
}
return f(protoAbs, []string{"api"})
}

@ -0,0 +1,20 @@
package tag
import (
"github.com/bilibili/kratos/tool/protobuf/pkg/extensions/gogoproto"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
)
func GetMoreTags(field *descriptor.FieldDescriptorProto) *string {
if field == nil {
return nil
}
if field.Options != nil {
v, err := proto.GetExtension(field.Options, gogoproto.E_Moretags)
if err == nil && v.(*string) != nil {
return v.(*string)
}
}
return nil
}

@ -0,0 +1,55 @@
package tag
import (
"reflect"
"strings"
)
// GetCommentWithoutTag strip tags in comment
func GetCommentWithoutTag(comment string) []string {
var lines []string
if comment == "" {
return lines
}
split := strings.Split(strings.TrimRight(comment, "\n\r"), "\n")
for _, line := range split {
tag, _, _ := GetLineTag(line)
if tag == "" {
lines = append(lines, line)
}
}
return lines
}
func GetTagsInComment(comment string) []reflect.StructTag {
split := strings.Split(comment, "\n")
var tagsInComment []reflect.StructTag
for _, line := range split {
tag, _, _ := GetLineTag(line)
if tag != "" {
tagsInComment = append(tagsInComment, tag)
}
}
return tagsInComment
}
func GetTagValue(key string, tags []reflect.StructTag) string {
for _, t := range tags {
val := t.Get(key)
if val != "" {
return val
}
}
return ""
}
// find tag between backtick, start & end is the position of backtick
func GetLineTag(line string) (tag reflect.StructTag, start int, end int) {
start = strings.Index(line, "`")
end = strings.LastIndex(line, "`")
if end <= start {
return
}
tag = reflect.StructTag(line[start+1 : end])
return
}

@ -0,0 +1,277 @@
package typemap
// Copyright 2018 Twitch Interactive, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the License is
// located at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// or in the "license" file accompanying this file. This file is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
import (
"strings"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/pkg/errors"
)
// Registry is the place of descriptors resolving
type Registry struct {
allFiles []*descriptor.FileDescriptorProto
filesByName map[string]*descriptor.FileDescriptorProto
// Mapping of fully-qualified names to their definitions
messagesByProtoName map[string]*MessageDefinition
}
// New Registry
func New(files []*descriptor.FileDescriptorProto) *Registry {
r := &Registry{
allFiles: files,
filesByName: make(map[string]*descriptor.FileDescriptorProto),
messagesByProtoName: make(map[string]*MessageDefinition),
}
// First, index the file descriptors by name. We need this so
// messageDefsForFile can correctly scan imports.
for _, f := range files {
r.filesByName[f.GetName()] = f
}
// Next, index all the message definitions by their fully-qualified proto
// names.
for _, f := range files {
defs := messageDefsForFile(f, r.filesByName)
for name, def := range defs {
r.messagesByProtoName[name] = def
}
}
return r
}
// FileComments comment of file
func (r *Registry) FileComments(file *descriptor.FileDescriptorProto) (DefinitionComments, error) {
return commentsAtPath([]int32{packagePath}, file), nil
}
// ServiceComments comments of service
func (r *Registry) ServiceComments(file *descriptor.FileDescriptorProto, svc *descriptor.ServiceDescriptorProto) (DefinitionComments, error) {
for i, s := range file.Service {
if s == svc {
path := []int32{servicePath, int32(i)}
return commentsAtPath(path, file), nil
}
}
return DefinitionComments{}, errors.Errorf("service not found in file")
}
// FieldComments ...
func (r *Registry) FieldComments(message *MessageDefinition, field *descriptor.FieldDescriptorProto) (DefinitionComments, error) {
file := message.File
mpath := message.path
for i, f := range message.Descriptor.Field {
if f == field {
path := append(mpath, messageFieldPath, int32(i))
return commentsAtPath(path, file), nil
}
}
return DefinitionComments{}, errors.Errorf("field not found in msg")
}
// MethodComments comment of method
func (r *Registry) MethodComments(file *descriptor.FileDescriptorProto, svc *descriptor.ServiceDescriptorProto, method *descriptor.MethodDescriptorProto) (DefinitionComments, error) {
for i, s := range file.Service {
if s == svc {
path := []int32{servicePath, int32(i)}
for j, m := range s.Method {
if m == method {
path = append(path, serviceMethodPath, int32(j))
return commentsAtPath(path, file), nil
}
}
}
}
return DefinitionComments{}, errors.Errorf("service not found in file")
}
// MethodInputDefinition returns MethodInputDefinition
func (r *Registry) MethodInputDefinition(method *descriptor.MethodDescriptorProto) *MessageDefinition {
return r.messagesByProtoName[method.GetInputType()]
}
// MethodOutputDefinition returns MethodOutputDefinition
func (r *Registry) MethodOutputDefinition(method *descriptor.MethodDescriptorProto) *MessageDefinition {
return r.messagesByProtoName[method.GetOutputType()]
}
// MessageDefinition by name
func (r *Registry) MessageDefinition(name string) *MessageDefinition {
return r.messagesByProtoName[name]
}
// MessageDefinition msg info
type MessageDefinition struct {
// Descriptor is is the DescriptorProto defining the message.
Descriptor *descriptor.DescriptorProto
// File is the File that the message was defined in. Or, if it has been
// publicly imported, what File was that import performed in?
File *descriptor.FileDescriptorProto
// Parent is the parent message, if this was defined as a nested message. If
// this was defiend at the top level, parent is nil.
Parent *MessageDefinition
// Comments describes the comments surrounding a message's definition. If it
// was publicly imported, then these comments are from the actual source file,
// not the file that the import was performed in.
Comments DefinitionComments
// path is the 'SourceCodeInfo' path. See the documentation for
// github.com/golang/protobuf/protoc-gen-go/descriptor.SourceCodeInfo for an
// explanation of its format.
path []int32
}
// ProtoName returns the dot-delimited, fully-qualified protobuf name of the
// message.
func (m *MessageDefinition) ProtoName() string {
prefix := "."
if pkg := m.File.GetPackage(); pkg != "" {
prefix += pkg + "."
}
if lineage := m.Lineage(); len(lineage) > 0 {
for _, parent := range lineage {
prefix += parent.Descriptor.GetName() + "."
}
}
return prefix + m.Descriptor.GetName()
}
// Lineage returns m's parental chain all the way back up to a top-level message
// definition. The first element of the returned slice is the highest-level
// parent.
func (m *MessageDefinition) Lineage() []*MessageDefinition {
var parents []*MessageDefinition
for p := m.Parent; p != nil; p = p.Parent {
parents = append([]*MessageDefinition{p}, parents...)
}
return parents
}
// descendants returns all the submessages defined within m, and all the
// descendants of those, recursively.
func (m *MessageDefinition) descendants() []*MessageDefinition {
descendants := make([]*MessageDefinition, 0)
for i, child := range m.Descriptor.NestedType {
path := append(m.path, []int32{messageMessagePath, int32(i)}...)
childDef := &MessageDefinition{
Descriptor: child,
File: m.File,
Parent: m,
Comments: commentsAtPath(path, m.File),
path: path,
}
descendants = append(descendants, childDef)
descendants = append(descendants, childDef.descendants()...)
}
return descendants
}
// messageDefsForFile gathers a mapping of fully-qualified protobuf names to
// their definitions. It scans a singles file at a time. It requires a mapping
// of .proto file names to their definitions in order to correctly handle
// 'import public' declarations; this mapping should include all files
// transitively imported by f.
func messageDefsForFile(f *descriptor.FileDescriptorProto, filesByName map[string]*descriptor.FileDescriptorProto) map[string]*MessageDefinition {
byProtoName := make(map[string]*MessageDefinition)
// First, gather all the messages defined at the top level.
for i, d := range f.MessageType {
path := []int32{messagePath, int32(i)}
def := &MessageDefinition{
Descriptor: d,
File: f,
Parent: nil,
Comments: commentsAtPath(path, f),
path: path,
}
byProtoName[def.ProtoName()] = def
// Next, all nested message definitions.
for _, child := range def.descendants() {
byProtoName[child.ProtoName()] = child
}
}
// Finally, all messages imported publicly.
for _, depIdx := range f.PublicDependency {
depFileName := f.Dependency[depIdx]
depFile := filesByName[depFileName]
depDefs := messageDefsForFile(depFile, filesByName)
for _, def := range depDefs {
imported := &MessageDefinition{
Descriptor: def.Descriptor,
File: f,
Parent: def.Parent,
Comments: commentsAtPath(def.path, depFile),
path: def.path,
}
byProtoName[imported.ProtoName()] = imported
}
}
return byProtoName
}
// // ignored detached comments.
type DefinitionComments struct {
Leading string
Trailing string
LeadingDetached []string
}
func commentsAtPath(path []int32, sourceFile *descriptor.FileDescriptorProto) DefinitionComments {
if sourceFile.SourceCodeInfo == nil {
// The compiler didn't provide us with comments.
return DefinitionComments{}
}
for _, loc := range sourceFile.SourceCodeInfo.Location {
if pathEqual(path, loc.Path) {
return DefinitionComments{
Leading: strings.TrimSuffix(loc.GetLeadingComments(), "\n"),
LeadingDetached: loc.GetLeadingDetachedComments(),
Trailing: loc.GetTrailingComments(),
}
}
}
return DefinitionComments{}
}
func pathEqual(path1, path2 []int32) bool {
if len(path1) != len(path2) {
return false
}
for i, v := range path1 {
if path2[i] != v {
return false
}
}
return true
}
const (
// tag numbers in FileDescriptorProto
packagePath = 2 // package
messagePath = 4 // message_type
servicePath = 6 // service
// tag numbers in DescriptorProto
messageFieldPath = 2 // field
messageMessagePath = 3 // nested_type
// tag numbers in ServiceDescriptorProto
serviceMethodPath = 2 // method
)

@ -0,0 +1,98 @@
package utils
import (
"strings"
"unicode"
)
// Is c an ASCII lower-case letter?
func isASCIILower(c byte) bool {
return 'a' <= c && c <= 'z'
}
// Is c an ASCII digit?
func isASCIIDigit(c byte) bool {
return '0' <= c && c <= '9'
}
// CamelCase converts a string from snake_case to CamelCased.
//
// If there is an interior underscore followed by a lower case letter, drop the
// underscore and convert the letter to upper case. There is a remote
// possibility of this rewrite causing a name collision, but it's so remote
// we're prepared to pretend it's nonexistent - since the C++ generator
// lowercases names, it's extremely unlikely to have two fields with different
// capitalizations. In short, _my_field_name_2 becomes XMyFieldName_2.
func CamelCase(s string) string {
if s == "" {
return ""
}
t := make([]byte, 0, 32)
i := 0
if s[0] == '_' {
// Need a capital letter; drop the '_'.
t = append(t, 'X')
i++
}
// Invariant: if the next letter is lower case, it must be converted
// to upper case.
//
// That is, we process a word at a time, where words are marked by _ or upper
// case letter. Digits are treated as words.
for ; i < len(s); i++ {
c := s[i]
if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
continue // Skip the underscore in s.
}
if isASCIIDigit(c) {
t = append(t, c)
continue
}
// Assume we have a letter now - if not, it's a bogus identifier. The next
// word is a sequence of characters that must start upper case.
if isASCIILower(c) {
c ^= ' ' // Make it a capital letter.
}
t = append(t, c) // Guaranteed not lower case.
// Accept lower case sequence that follows.
for i+1 < len(s) && isASCIILower(s[i+1]) {
i++
t = append(t, s[i])
}
}
return string(t)
}
// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
// be joined with "_" and then camelcased.
func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
// BaseName the last path element of a slash-delimited name, with the last
// dotted suffix removed.
func BaseName(name string) string {
// First, find the last element
if i := strings.LastIndex(name, "/"); i >= 0 {
name = name[i+1:]
}
// Now drop the suffix
if i := strings.LastIndex(name, "."); i >= 0 {
name = name[0:i]
}
return name
}
// AlphaDigitize replaces non-letter, non-digit, non-underscore characters with
// underscore.
func AlphaDigitize(r rune) rune {
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
return r
}
return '_'
}
// CleanIdentifier makes sure s is a valid 'identifier' string: it contains only
// letters, numbers, and underscore.
func CleanIdentifier(s string) string {
return strings.Map(AlphaDigitize, s)
}

@ -0,0 +1,29 @@
package utils
import (
"os"
"unicode"
)
// LcFirst lower the first letter
func LcFirst(str string) string {
for i, v := range str {
return string(unicode.ToLower(v)) + str[i+1:]
}
return ""
}
func IsDir(name string) bool {
file, err := os.Open(name)
if err != nil {
return false
}
defer file.Close()
fi, err := file.Stat()
if err != nil {
return false
}
return fi.IsDir()
}

@ -0,0 +1,355 @@
package generator
import (
"fmt"
"reflect"
"sort"
"strings"
"github.com/bilibili/kratos/tool/protobuf/pkg/generator"
"github.com/bilibili/kratos/tool/protobuf/pkg/naming"
"github.com/bilibili/kratos/tool/protobuf/pkg/tag"
"github.com/bilibili/kratos/tool/protobuf/pkg/typemap"
"github.com/bilibili/kratos/tool/protobuf/pkg/utils"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
type bm struct {
generator.Base
filesHandled int
}
// BmGenerator BM generator.
func BmGenerator() *bm {
t := &bm{}
return t
}
// Generate ...
func (t *bm) Generate(in *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse {
t.Setup(in)
// Showtime! Generate the response.
resp := new(plugin.CodeGeneratorResponse)
for _, f := range t.GenFiles {
respFile := t.generateForFile(f)
if respFile != nil {
resp.File = append(resp.File, respFile)
}
}
return resp
}
func (t *bm) generateForFile(file *descriptor.FileDescriptorProto) *plugin.CodeGeneratorResponse_File {
resp := new(plugin.CodeGeneratorResponse_File)
//if len(file.Service) == 0 {
// return nil
//}
t.generateFileHeader(file, t.GenPkgName)
t.generateImports(file)
t.generatePathConstants(file)
count := 0
for i, service := range file.Service {
count += t.generateBMInterface(file, service)
t.generateBMRoute(file, service, i)
}
//if count == 0 {
// return nil
//}
resp.Name = proto.String(naming.GoFileName(file, ".bm.go"))
resp.Content = proto.String(t.FormattedOutput())
t.Output.Reset()
t.filesHandled++
return resp
}
func (t *bm) generatePathConstants(file *descriptor.FileDescriptorProto) {
t.P()
for _, service := range file.Service {
name := naming.ServiceName(service)
for _, method := range service.Method {
if !t.ShouldGenForMethod(file, service, method) {
continue
}
apiInfo := t.GetHttpInfoCached(file, service, method)
t.P(`var Path`, name, naming.MethodName(method), ` = "`, apiInfo.Path, `"`)
}
t.P()
}
}
func (t *bm) generateFileHeader(file *descriptor.FileDescriptorProto, pkgName string) {
t.P("// Code generated by protoc-gen-bm ", generator.Version, ", DO NOT EDIT.")
t.P("// source: ", file.GetName())
t.P()
if t.filesHandled == 0 {
// doc for the first file
t.P("/*")
t.P("Package ", t.GenPkgName, " is a generated blademaster stub package.")
t.P("This code was generated with kratos/tool/bmgen/protoc-gen-bm ", generator.Version, ".")
t.P()
comment, err := t.Reg.FileComments(file)
if err == nil && comment.Leading != "" {
for _, line := range strings.Split(comment.Leading, "\n") {
line = strings.TrimPrefix(line, " ")
// ensure we don't escape from the block comment
line = strings.Replace(line, "*/", "* /", -1)
t.P(line)
}
t.P()
}
t.P("It is generated from these files:")
for _, f := range t.GenFiles {
t.P("\t", f.GetName())
}
t.P("*/")
}
t.P(`package `, pkgName)
t.P()
}
func (t *bm) generateImports(file *descriptor.FileDescriptorProto) {
//if len(file.Service) == 0 {
// return
//}
t.P(`import (`)
//t.P(` `,t.pkgs["context"], ` "context"`)
t.P(` "context"`)
t.P()
t.P(` bm "github.com/bilibili/kratos/pkg/net/http/blademaster"`)
t.P(` "github.com/bilibili/kratos/pkg/net/http/blademaster/binding"`)
t.P(`)`)
// It's legal to import a message and use it as an input or output for a
// method. Make sure to import the package of any such message. First, dedupe
// them.
deps := make(map[string]string) // Map of package name to quoted import path.
deps = t.DeduceDeps(file)
for pkg, importPath := range deps {
for _, service := range file.Service {
for _, method := range service.Method {
inputType := t.GoTypeName(method.GetInputType())
outputType := t.GoTypeName(method.GetOutputType())
if strings.HasPrefix(pkg, outputType) || strings.HasPrefix(pkg, inputType) {
t.P(`import `, pkg, ` `, importPath)
}
}
}
}
if len(deps) > 0 {
t.P()
}
t.P()
t.P(`// to suppressed 'imported but not used warning'`)
t.P(`var _ *bm.Context`)
t.P(`var _ context.Context`)
t.P(`var _ binding.StructValidator`)
}
// Big header comments to makes it easier to visually parse a generated file.
func (t *bm) sectionComment(sectionTitle string) {
t.P()
t.P(`// `, strings.Repeat("=", len(sectionTitle)))
t.P(`// `, sectionTitle)
t.P(`// `, strings.Repeat("=", len(sectionTitle)))
t.P()
}
func (t *bm) generateBMRoute(
file *descriptor.FileDescriptorProto,
service *descriptor.ServiceDescriptorProto,
index int) {
// old mode is generate xx.route.go in the http pkg
// new mode is generate route code in the same .bm.go
// route rule /x{department}/{project-name}/{path_prefix}/method_name
// generate each route method
servName := naming.ServiceName(service)
versionPrefix := naming.GetVersionPrefix(t.GenPkgName)
svcName := utils.LcFirst(utils.CamelCase(versionPrefix)) + servName + "Svc"
t.P(`var `, svcName, ` `, servName, `BMServer`)
type methodInfo struct {
midwares []string
routeFuncName string
apiInfo *generator.HTTPInfo
methodName string
}
var methList []methodInfo
var allMidwareMap = make(map[string]bool)
var isLegacyPkg = false
for _, method := range service.Method {
if !t.ShouldGenForMethod(file, service, method) {
continue
}
var midwares []string
comments, _ := t.Reg.MethodComments(file, service, method)
tags := tag.GetTagsInComment(comments.Leading)
if tag.GetTagValue("dynamic", tags) == "true" {
continue
}
apiInfo := t.GetHttpInfoCached(file, service, method)
isLegacyPkg = apiInfo.IsLegacyPath
//httpMethod, legacyPath, path := getHttpInfo(file, service, method, t.reg)
//if legacyPath != "" {
// isLegacyPkg = true
//}
midStr := tag.GetTagValue("midware", tags)
if midStr != "" {
midwares = strings.Split(midStr, ",")
for _, m := range midwares {
allMidwareMap[m] = true
}
}
methName := naming.MethodName(method)
inputType := t.GoTypeName(method.GetInputType())
routeName := utils.LcFirst(utils.CamelCase(servName) +
utils.CamelCase(methName))
methList = append(methList, methodInfo{
apiInfo: apiInfo,
midwares: midwares,
routeFuncName: routeName,
methodName: method.GetName(),
})
t.P(fmt.Sprintf("func %s (c *bm.Context) {", routeName))
t.P(` p := new(`, inputType, `)`)
requestBinding := ""
if t.hasHeaderTag(t.Reg.MessageDefinition(method.GetInputType())) {
requestBinding = ", binding.Request"
}
t.P(` if err := c.BindWith(p, binding.Default(c.Request.Method, c.Request.Header.Get("Content-Type"))` +
requestBinding + `); err != nil {`)
t.P(` return`)
t.P(` }`)
t.P(` resp, err := `, svcName, `.`, methName, `(c, p)`)
t.P(` c.JSON(resp, err)`)
t.P(`}`)
t.P(``)
}
// generate route group
var midList []string
for m := range allMidwareMap {
midList = append(midList, m+" bm.HandlerFunc")
}
sort.Strings(midList)
// 注册老的路由的方法
if isLegacyPkg {
funcName := `Register` + utils.CamelCase(versionPrefix) + servName + `Service`
t.P(`// `, funcName, ` Register the blademaster route with middleware map`)
t.P(`// midMap is the middleware map, the key is defined in proto`)
t.P(`func `, funcName, `(e *bm.Engine, svc `, servName, "BMServer, midMap map[string]bm.HandlerFunc)", ` {`)
var keys []string
for m := range allMidwareMap {
keys = append(keys, m)
}
// to keep generated code consistent
sort.Strings(keys)
for _, m := range keys {
t.P(m, ` := midMap["`, m, `"]`)
}
t.P(svcName, ` = svc`)
for _, methInfo := range methList {
var midArgStr string
if len(methInfo.midwares) == 0 {
midArgStr = ""
} else {
midArgStr = strings.Join(methInfo.midwares, ", ") + ", "
}
t.P(`e.`, methInfo.apiInfo.HttpMethod, `("`, methInfo.apiInfo.LegacyPath, `", `, midArgStr, methInfo.routeFuncName, `)`)
}
t.P(` }`)
} else {
// 新的注册路由的方法
var bmFuncName = fmt.Sprintf("Register%sBMServer", servName)
t.P(`// `, bmFuncName, ` Register the blademaster route`)
t.P(`func `, bmFuncName, `(e *bm.Engine, server `, servName, `BMServer) {`)
t.P(svcName, ` = server`)
for _, methInfo := range methList {
t.P(`e.`, methInfo.apiInfo.HttpMethod, `("`, methInfo.apiInfo.NewPath, `",`, methInfo.routeFuncName, ` )`)
}
t.P(` }`)
}
}
func (t *bm) hasHeaderTag(md *typemap.MessageDefinition) bool {
if md.Descriptor.Field == nil {
return false
}
for _, f := range md.Descriptor.Field {
t := tag.GetMoreTags(f)
if t != nil {
st := reflect.StructTag(*t)
if st.Get("request") != "" {
return true
}
if st.Get("header") != "" {
return true
}
}
}
return false
}
func (t *bm) generateBMInterface(file *descriptor.FileDescriptorProto, service *descriptor.ServiceDescriptorProto) int {
count := 0
servName := naming.ServiceName(service)
t.P("// " + servName + "BMServer is the server API for " + servName + " service.")
comments, err := t.Reg.ServiceComments(file, service)
if err == nil {
t.PrintComments(comments)
}
t.P(`type `, servName, `BMServer interface {`)
for _, method := range service.Method {
if !t.ShouldGenForMethod(file, service, method) {
continue
}
count++
t.generateInterfaceMethod(file, service, method, comments)
t.P()
}
t.P(`}`)
return count
}
func (t *bm) generateInterfaceMethod(file *descriptor.FileDescriptorProto,
service *descriptor.ServiceDescriptorProto,
method *descriptor.MethodDescriptorProto,
comments typemap.DefinitionComments) {
comments, err := t.Reg.MethodComments(file, service, method)
methName := naming.MethodName(method)
outputType := t.GoTypeName(method.GetOutputType())
inputType := t.GoTypeName(method.GetInputType())
tags := tag.GetTagsInComment(comments.Leading)
if tag.GetTagValue("dynamic", tags) == "true" {
return
}
if err == nil {
t.PrintComments(comments)
}
respDynamic := tag.GetTagValue("dynamic_resp", tags) == "true"
if respDynamic {
t.P(fmt.Sprintf(` %s(ctx context.Context, req *%s) (resp interface{}, err error)`,
methName, inputType))
} else {
t.P(fmt.Sprintf(` %s(ctx context.Context, req *%s) (resp *%s, err error)`,
methName, inputType, outputType))
}
}

@ -0,0 +1,27 @@
package generator
import (
"os"
"os/exec"
"testing"
"github.com/golang/protobuf/proto"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
func TestGenerateParseCommandLineParamsError(t *testing.T) {
if os.Getenv("BE_CRASHER") == "1" {
g := &bm{}
g.Generate(&plugin.CodeGeneratorRequest{
Parameter: proto.String("invalid"),
})
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestGenerateParseCommandLineParamsError")
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
err := cmd.Run()
if e, ok := err.(*exec.ExitError); ok && !e.Success() {
return
}
t.Fatalf("process ran with err %v, want exit status 1", err)
}

@ -0,0 +1,23 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/bilibili/kratos/tool/protobuf/pkg/gen"
"github.com/bilibili/kratos/tool/protobuf/pkg/generator"
bmgen "github.com/bilibili/kratos/tool/protobuf/protoc-gen-bm/generator"
)
func main() {
versionFlag := flag.Bool("version", false, "print version and exit")
flag.Parse()
if *versionFlag {
fmt.Println(generator.Version)
os.Exit(0)
}
g := bmgen.BmGenerator()
gen.Main(g)
}

@ -0,0 +1,303 @@
package main
import (
"encoding/json"
"net/http"
"regexp"
"strings"
"github.com/bilibili/kratos/tool/protobuf/pkg/gen"
"github.com/bilibili/kratos/tool/protobuf/pkg/generator"
"github.com/bilibili/kratos/tool/protobuf/pkg/naming"
"github.com/bilibili/kratos/tool/protobuf/pkg/tag"
"github.com/bilibili/kratos/tool/protobuf/pkg/typemap"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
type swaggerGen struct {
generator.Base
// defsMap will fill into swagger's definitions
// key is full qualified proto name
defsMap map[string]*typemap.MessageDefinition
}
// NewSwaggerGenerator a swagger generator
func NewSwaggerGenerator() *swaggerGen {
return &swaggerGen{}
}
func (t *swaggerGen) Generate(in *plugin.CodeGeneratorRequest) *plugin.CodeGeneratorResponse {
t.Setup(in)
resp := &plugin.CodeGeneratorResponse{}
for _, f := range t.GenFiles {
if len(f.Service) == 0 {
continue
}
respFile := t.generateSwagger(f)
if respFile != nil {
resp.File = append(resp.File, respFile)
}
}
return resp
}
func (t *swaggerGen) generateSwagger(file *descriptor.FileDescriptorProto) *plugin.CodeGeneratorResponse_File {
var pkg = file.GetPackage()
r := regexp.MustCompile("v(\\d+)$")
strs := r.FindStringSubmatch(pkg)
var vStr string
if len(strs) >= 2 {
vStr = strs[1]
} else {
vStr = ""
}
var swaggerObj = &swaggerObject{
Paths: swaggerPathsObject{},
Swagger: "2.0",
Info: swaggerInfoObject{
Title: file.GetName(),
Version: vStr,
},
Schemes: []string{"http", "https"},
Consumes: []string{"application/json", "multipart/form-data"},
Produces: []string{"application/json"},
}
t.defsMap = map[string]*typemap.MessageDefinition{}
out := &plugin.CodeGeneratorResponse_File{}
name := naming.GoFileName(file, ".swagger.json")
for _, svc := range file.Service {
for _, meth := range svc.Method {
if !t.ShouldGenForMethod(file, svc, meth) {
continue
}
apiInfo := t.GetHttpInfoCached(file, svc, meth)
pathItem := swaggerPathItemObject{}
op := t.getOperationByHTTPMethod(apiInfo.HttpMethod, &pathItem)
op.Summary = apiInfo.Title
op.Description = apiInfo.Description
swaggerObj.Paths[apiInfo.Path] = pathItem
op.Tags = []string{pkg + "." + svc.GetName()}
// request
request := t.Reg.MessageDefinition(meth.GetInputType())
// request cannot represent by simple form
isComplexRequest := false
for _, field := range request.Descriptor.Field {
if !generator.IsScalar(field) {
isComplexRequest = true
break
}
}
if !isComplexRequest && apiInfo.HttpMethod == "GET" {
for _, field := range request.Descriptor.Field {
if !generator.IsScalar(field) {
continue
}
p := t.getQueryParameter(file, request, field)
op.Parameters = append(op.Parameters, p)
}
} else {
p := swaggerParameterObject{}
p.In = "body"
p.Required = true
p.Name = "body"
p.Schema = &swaggerSchemaObject{}
p.Schema.Ref = "#/definitions/" + meth.GetInputType()
op.Parameters = []swaggerParameterObject{p}
}
// response
resp := swaggerResponseObject{}
resp.Description = "A successful response."
// proto 里面的response只定义data里面的
// 所以需要把code msg data 这一级加上
resp.Schema.Type = "object"
resp.Schema.Properties = &swaggerSchemaObjectProperties{}
p := keyVal{Key: "code", Value: &schemaCore{Type: "integer"}}
*resp.Schema.Properties = append(*resp.Schema.Properties, p)
p = keyVal{Key: "message", Value: &schemaCore{Type: "string"}}
*resp.Schema.Properties = append(*resp.Schema.Properties, p)
p = keyVal{Key: "data", Value: schemaCore{Ref: "#/definitions/" + meth.GetOutputType()}}
*resp.Schema.Properties = append(*resp.Schema.Properties, p)
op.Responses = swaggerResponsesObject{"200": resp}
}
}
// walk though definitions
t.walkThroughFileDefinition(file)
defs := swaggerDefinitionsObject{}
swaggerObj.Definitions = defs
for typ, msg := range t.defsMap {
def := swaggerSchemaObject{}
def.Properties = new(swaggerSchemaObjectProperties)
def.Description = strings.Trim(msg.Comments.Leading, "\n\r ")
for _, field := range msg.Descriptor.Field {
p := keyVal{Key: generator.GetFormOrJSONName(field)}
schema := t.schemaForField(file, msg, field)
if generator.GetFieldRequired(field, t.Reg, msg) {
def.Required = append(def.Required, p.Key)
}
p.Value = schema
*def.Properties = append(*def.Properties, p)
}
def.Type = "object"
defs[typ] = def
}
b, _ := json.MarshalIndent(swaggerObj, "", " ")
str := string(b)
out.Name = &name
out.Content = &str
return out
}
func (t *swaggerGen) getOperationByHTTPMethod(httpMethod string, pathItem *swaggerPathItemObject) *swaggerOperationObject {
var op = &swaggerOperationObject{}
switch httpMethod {
case http.MethodGet:
pathItem.Get = op
case http.MethodPost:
pathItem.Post = op
case http.MethodPut:
pathItem.Put = op
case http.MethodDelete:
pathItem.Put = op
case http.MethodPatch:
pathItem.Patch = op
default:
pathItem.Get = op
}
return op
}
func (t *swaggerGen) getQueryParameter(file *descriptor.FileDescriptorProto,
input *typemap.MessageDefinition,
field *descriptor.FieldDescriptorProto) swaggerParameterObject {
p := swaggerParameterObject{}
p.Name = generator.GetFormOrJSONName(field)
fComment, _ := t.Reg.FieldComments(input, field)
cleanComment := tag.GetCommentWithoutTag(fComment.Leading)
p.Description = strings.Trim(strings.Join(cleanComment, "\n"), "\n\r ")
p.In = "query"
p.Required = generator.GetFieldRequired(field, t.Reg, input)
typ, isArray, format := getFieldSwaggerType(field)
if isArray {
p.Items = &swaggerItemsObject{}
p.Type = "array"
p.Items.Type = typ
p.Items.Format = format
} else {
p.Type = typ
p.Format = format
}
return p
}
func (t *swaggerGen) schemaForField(file *descriptor.FileDescriptorProto,
msg *typemap.MessageDefinition,
field *descriptor.FieldDescriptorProto) swaggerSchemaObject {
schema := swaggerSchemaObject{}
fComment, err := t.Reg.FieldComments(msg, field)
if err != nil {
gen.Error(err, "comment not found err %+v")
}
schema.Description = strings.Trim(fComment.Leading, "\n\r ")
typ, isArray, format := getFieldSwaggerType(field)
if !generator.IsScalar(field) {
if generator.IsMap(field, t.Reg) {
schema.Type = "object"
mapMsg := t.Reg.MessageDefinition(field.GetTypeName())
mapValueField := mapMsg.Descriptor.Field[1]
valSchema := t.schemaForField(file, mapMsg, mapValueField)
schema.AdditionalProperties = &valSchema
} else {
if isArray {
schema.Items = &swaggerItemsObject{}
schema.Type = "array"
schema.Items.Ref = "#/definitions/" + field.GetTypeName()
} else {
schema.Ref = "#/definitions/" + field.GetTypeName()
}
}
} else {
if isArray {
schema.Items = &swaggerItemsObject{}
schema.Type = "array"
schema.Items.Type = typ
schema.Items.Format = format
} else {
schema.Type = typ
schema.Format = format
}
}
return schema
}
func (t *swaggerGen) walkThroughFileDefinition(file *descriptor.FileDescriptorProto) {
for _, svc := range file.Service {
for _, meth := range svc.Method {
shouldGen := t.ShouldGenForMethod(file, svc, meth)
if !shouldGen {
continue
}
t.walkThroughMessages(t.Reg.MessageDefinition(meth.GetOutputType()))
t.walkThroughMessages(t.Reg.MessageDefinition(meth.GetInputType()))
}
}
}
func (t *swaggerGen) walkThroughMessages(msg *typemap.MessageDefinition) {
_, ok := t.defsMap[msg.ProtoName()]
if ok {
return
}
if !msg.Descriptor.GetOptions().GetMapEntry() {
t.defsMap[msg.ProtoName()] = msg
}
for _, field := range msg.Descriptor.Field {
if field.GetType() == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
t.walkThroughMessages(t.Reg.MessageDefinition(field.GetTypeName()))
}
}
}
func getFieldSwaggerType(field *descriptor.FieldDescriptorProto) (typeName string, isArray bool, formatName string) {
typeName = "unknown"
switch field.GetType() {
case descriptor.FieldDescriptorProto_TYPE_BOOL:
typeName = "boolean"
case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
typeName = "number"
formatName = "double"
case descriptor.FieldDescriptorProto_TYPE_FLOAT:
typeName = "number"
formatName = "float"
case
descriptor.FieldDescriptorProto_TYPE_INT64,
descriptor.FieldDescriptorProto_TYPE_UINT64,
descriptor.FieldDescriptorProto_TYPE_INT32,
descriptor.FieldDescriptorProto_TYPE_FIXED64,
descriptor.FieldDescriptorProto_TYPE_FIXED32,
descriptor.FieldDescriptorProto_TYPE_ENUM,
descriptor.FieldDescriptorProto_TYPE_UINT32,
descriptor.FieldDescriptorProto_TYPE_SFIXED32,
descriptor.FieldDescriptorProto_TYPE_SFIXED64,
descriptor.FieldDescriptorProto_TYPE_SINT32,
descriptor.FieldDescriptorProto_TYPE_SINT64:
typeName = "integer"
case
descriptor.FieldDescriptorProto_TYPE_STRING,
descriptor.FieldDescriptorProto_TYPE_BYTES:
typeName = "string"
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
typeName = "object"
}
if field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
isArray = true
}
return
}

@ -0,0 +1,22 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/bilibili/kratos/tool/protobuf/pkg/gen"
"github.com/bilibili/kratos/tool/protobuf/pkg/generator"
)
func main() {
versionFlag := flag.Bool("version", false, "print version and exit")
flag.Parse()
if *versionFlag {
fmt.Println(generator.Version)
os.Exit(0)
}
g := NewSwaggerGenerator()
gen.Main(g)
}

@ -0,0 +1,218 @@
package main
import (
"bytes"
"encoding/json"
)
// http://swagger.io/specification/#infoObject
type swaggerInfoObject struct {
Title string `json:"title"`
Description string `json:"description,omitempty"`
TermsOfService string `json:"termsOfService,omitempty"`
Version string `json:"version"`
Contact *swaggerContactObject `json:"contact,omitempty"`
License *swaggerLicenseObject `json:"license,omitempty"`
}
// http://swagger.io/specification/#contactObject
type swaggerContactObject struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Email string `json:"email,omitempty"`
}
// http://swagger.io/specification/#licenseObject
type swaggerLicenseObject struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
}
// http://swagger.io/specification/#externalDocumentationObject
type swaggerExternalDocumentationObject struct {
Description string `json:"description,omitempty"`
URL string `json:"url,omitempty"`
}
// http://swagger.io/specification/#swaggerObject
type swaggerObject struct {
Swagger string `json:"swagger"`
Info swaggerInfoObject `json:"info"`
Host string `json:"host,omitempty"`
BasePath string `json:"basePath,omitempty"`
Schemes []string `json:"schemes"`
Consumes []string `json:"consumes"`
Produces []string `json:"produces"`
Paths swaggerPathsObject `json:"paths"`
Definitions swaggerDefinitionsObject `json:"definitions"`
StreamDefinitions swaggerDefinitionsObject `json:"x-stream-definitions,omitempty"`
SecurityDefinitions swaggerSecurityDefinitionsObject `json:"securityDefinitions,omitempty"`
Security []swaggerSecurityRequirementObject `json:"security,omitempty"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
}
// http://swagger.io/specification/#securityDefinitionsObject
type swaggerSecurityDefinitionsObject map[string]swaggerSecuritySchemeObject
// http://swagger.io/specification/#securitySchemeObject
type swaggerSecuritySchemeObject struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
In string `json:"in,omitempty"`
Flow string `json:"flow,omitempty"`
AuthorizationURL string `json:"authorizationUrl,omitempty"`
TokenURL string `json:"tokenUrl,omitempty"`
Scopes swaggerScopesObject `json:"scopes,omitempty"`
}
// http://swagger.io/specification/#scopesObject
type swaggerScopesObject map[string]string
// http://swagger.io/specification/#securityRequirementObject
type swaggerSecurityRequirementObject map[string][]string
// http://swagger.io/specification/#pathsObject
type swaggerPathsObject map[string]swaggerPathItemObject
// http://swagger.io/specification/#pathItemObject
type swaggerPathItemObject struct {
Get *swaggerOperationObject `json:"get,omitempty"`
Delete *swaggerOperationObject `json:"delete,omitempty"`
Post *swaggerOperationObject `json:"post,omitempty"`
Put *swaggerOperationObject `json:"put,omitempty"`
Patch *swaggerOperationObject `json:"patch,omitempty"`
}
// http://swagger.io/specification/#operationObject
type swaggerOperationObject struct {
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
OperationID string `json:"operationId,omitempty"`
Responses swaggerResponsesObject `json:"responses"`
Parameters swaggerParametersObject `json:"parameters,omitempty"`
Tags []string `json:"tags,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
Security *[]swaggerSecurityRequirementObject `json:"security,omitempty"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
}
type swaggerParametersObject []swaggerParameterObject
// http://swagger.io/specification/#parameterObject
type swaggerParameterObject struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
In string `json:"in,omitempty"`
Required bool `json:"required"`
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Items *swaggerItemsObject `json:"items,omitempty"`
Enum []string `json:"enum,omitempty"`
CollectionFormat string `json:"collectionFormat,omitempty"`
Default string `json:"default,omitempty"`
MinItems *int `json:"minItems,omitempty"`
// Or you can explicitly refer to another type. If this is defined all
// other fields should be empty
Schema *swaggerSchemaObject `json:"schema,omitempty"`
}
// core part of schema, which is common to itemsObject and schemaObject.
// http://swagger.io/specification/#itemsObject
type schemaCore struct {
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Ref string `json:"$ref,omitempty"`
Example json.RawMessage `json:"example,omitempty"`
Items *swaggerItemsObject `json:"items,omitempty"`
// If the item is an enumeration include a list of all the *NAMES* of the
// enum values. I'm not sure how well this will work but assuming all enums
// start from 0 index it will be great. I don't think that is a good assumption.
Enum []string `json:"enum,omitempty"`
Default string `json:"default,omitempty"`
}
type swaggerItemsObject schemaCore
func (o *swaggerItemsObject) getType() string {
if o == nil {
return ""
}
return o.Type
}
// http://swagger.io/specification/#responsesObject
type swaggerResponsesObject map[string]swaggerResponseObject
// http://swagger.io/specification/#responseObject
type swaggerResponseObject struct {
Description string `json:"description"`
Schema swaggerSchemaObject `json:"schema"`
}
type keyVal struct {
Key string
Value interface{}
}
type swaggerSchemaObjectProperties []keyVal
func (op swaggerSchemaObjectProperties) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("{")
for i, kv := range op {
if i != 0 {
buf.WriteString(",")
}
key, err := json.Marshal(kv.Key)
if err != nil {
return nil, err
}
buf.Write(key)
buf.WriteString(":")
val, err := json.Marshal(kv.Value)
if err != nil {
return nil, err
}
buf.Write(val)
}
buf.WriteString("}")
return buf.Bytes(), nil
}
// http://swagger.io/specification/#schemaObject
type swaggerSchemaObject struct {
schemaCore
// Properties can be recursively defined
Properties *swaggerSchemaObjectProperties `json:"properties,omitempty"`
AdditionalProperties *swaggerSchemaObject `json:"additionalProperties,omitempty"`
Description string `json:"description,omitempty"`
Title string `json:"title,omitempty"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
MultipleOf float64 `json:"multipleOf,omitempty"`
Maximum float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
Minimum float64 `json:"minimum,omitempty"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"`
MaxLength uint64 `json:"maxLength,omitempty"`
MinLength uint64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems uint64 `json:"maxItems,omitempty"`
MinItems uint64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
MaxProperties uint64 `json:"maxProperties,omitempty"`
MinProperties uint64 `json:"minProperties,omitempty"`
Required []string `json:"required,omitempty"`
}
// http://swagger.io/specification/#definitionsObject
type swaggerDefinitionsObject map[string]swaggerSchemaObject
Loading…
Cancel
Save