mirror of
https://github.com/go-kratos/kratos.git
synced 2025-01-20 03:29:45 +02:00
Kratos protobuf protoc (#71)
* add cross-platform protoc * add protobuf generator
This commit is contained in:
parent
8266a50f3d
commit
e5fe1e4f63
@ -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
|
||||
```
|
||||
|
||||
-------------
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -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
|
25
tool/kratos-protoc/bm.go
Normal file
25
tool/kratos-protoc/bm.go
Normal file
@ -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)
|
||||
}
|
25
tool/kratos-protoc/grpc.go
Normal file
25
tool/kratos-protoc/grpc.go
Normal file
@ -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)
|
||||
}
|
37
tool/kratos-protoc/main.go
Normal file
37
tool/kratos-protoc/main.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
160
tool/kratos-protoc/protoc.go
Normal file
160
tool/kratos-protoc/protoc.go
Normal file
@ -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
|
||||
}
|
25
tool/kratos-protoc/swagger.go
Normal file
25
tool/kratos-protoc/swagger.go
Normal file
@ -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",
|
||||
},
|
||||
}
|
||||
|
9
tool/protobuf/pkg/extensions/gogoproto/Makefile
Normal file
9
tool/protobuf/pkg/extensions/gogoproto/Makefile
Normal file
@ -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
|
818
tool/protobuf/pkg/extensions/gogoproto/gogo.pb.go
Normal file
818
tool/protobuf/pkg/extensions/gogoproto/gogo.pb.go
Normal file
@ -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,
|
||||
}
|
45
tool/protobuf/pkg/extensions/gogoproto/gogo.pb.golden
Normal file
45
tool/protobuf/pkg/extensions/gogoproto/gogo.pb.golden
Normal file
@ -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)
|
||||
}
|
136
tool/protobuf/pkg/extensions/gogoproto/gogo.proto
Normal file
136
tool/protobuf/pkg/extensions/gogoproto/gogo.proto
Normal file
@ -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;
|
||||
}
|
31
tool/protobuf/pkg/extensions/google/api/annotations.proto
Normal file
31
tool/protobuf/pkg/extensions/google/api/annotations.proto
Normal file
@ -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;
|
||||
}
|
318
tool/protobuf/pkg/extensions/google/api/http.proto
Normal file
318
tool/protobuf/pkg/extensions/google/api/http.proto
Normal file
@ -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¶m=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;
|
||||
}
|
94
tool/protobuf/pkg/gen/main.go
Normal file
94
tool/protobuf/pkg/gen/main.go
Normal file
@ -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)
|
||||
}
|
71
tool/protobuf/pkg/generator/command_line.go
Normal file
71
tool/protobuf/pkg/generator/command_line.go
Normal file
@ -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
|
||||
}
|
310
tool/protobuf/pkg/generator/generator.go
Normal file
310
tool/protobuf/pkg/generator/generator.go
Normal file
@ -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
|
||||
}
|
136
tool/protobuf/pkg/generator/helper.go
Normal file
136
tool/protobuf/pkg/generator/helper.go
Normal file
@ -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)
|
||||
}
|
145
tool/protobuf/pkg/generator/http.go
Normal file
145
tool/protobuf/pkg/generator/http.go
Normal file
@ -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
|
||||
}
|
27
tool/protobuf/pkg/naming/go_naming.go
Normal file
27
tool/protobuf/pkg/naming/go_naming.go
Normal file
@ -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
|
||||
}
|
101
tool/protobuf/pkg/naming/naming.go
Normal file
101
tool/protobuf/pkg/naming/naming.go
Normal file
@ -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
|
||||
}
|
119
tool/protobuf/pkg/project/project.go
Normal file
119
tool/protobuf/pkg/project/project.go
Normal file
@ -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"})
|
||||
}
|
20
tool/protobuf/pkg/tag/ext_tags.go
Normal file
20
tool/protobuf/pkg/tag/ext_tags.go
Normal file
@ -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
|
||||
}
|
55
tool/protobuf/pkg/tag/tags.go
Normal file
55
tool/protobuf/pkg/tag/tags.go
Normal file
@ -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
|
||||
}
|
277
tool/protobuf/pkg/typemap/typemap.go
Normal file
277
tool/protobuf/pkg/typemap/typemap.go
Normal file
@ -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
|
||||
)
|
98
tool/protobuf/pkg/utils/stringutils.go
Normal file
98
tool/protobuf/pkg/utils/stringutils.go
Normal file
@ -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)
|
||||
}
|
29
tool/protobuf/pkg/utils/utils.go
Normal file
29
tool/protobuf/pkg/utils/utils.go
Normal file
@ -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()
|
||||
}
|
355
tool/protobuf/protoc-gen-bm/generator/generator.go
Normal file
355
tool/protobuf/protoc-gen-bm/generator/generator.go
Normal file
@ -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))
|
||||
}
|
||||
}
|
27
tool/protobuf/protoc-gen-bm/generator/generator_test.go
Normal file
27
tool/protobuf/protoc-gen-bm/generator/generator_test.go
Normal file
@ -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)
|
||||
}
|
23
tool/protobuf/protoc-gen-bm/main.go
Normal file
23
tool/protobuf/protoc-gen-bm/main.go
Normal file
@ -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)
|
||||
}
|
303
tool/protobuf/protoc-gen-bswagger/generator.go
Normal file
303
tool/protobuf/protoc-gen-bswagger/generator.go
Normal file
@ -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
|
||||
}
|
22
tool/protobuf/protoc-gen-bswagger/main.go
Normal file
22
tool/protobuf/protoc-gen-bswagger/main.go
Normal file
@ -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)
|
||||
}
|
218
tool/protobuf/protoc-gen-bswagger/types.go
Normal file
218
tool/protobuf/protoc-gen-bswagger/types.go
Normal file
@ -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…
x
Reference in New Issue
Block a user