Skip to content

Commit 245c1a7

Browse files
committed
update gen go api code
1 parent 5ab7c00 commit 245c1a7

File tree

13 files changed

+2608
-0
lines changed

13 files changed

+2608
-0
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# protoc-gen-go_api
2+
一款用protobuf文件生成go的http调用代码。
3+
4+
## 安装
5+
6+
```bash
7+
go install github.com/dev-openapi/protoc-gen-go_api@latest
8+
```
9+
10+
## 使用
11+
12+
依赖googleapis的http.proto和annotations.proto。并且需要放到google/api/目录下
13+
14+
如果google/api是在工程文件目录下,执行以下脚本
15+
16+
```bash
17+
protoc --go_api_out=out={dir}:. *.proto
18+
```
19+
20+
如果google/api在其他文件目录下,执行以下脚本
21+
22+
```bash
23+
protoc --proto_path={yourpath}:. --go_api_out=:. *.proto
24+
```
25+
26+
## 注意
27+
28+
最新版本的protoc-gen-go要求go_package必须含有/,且会生成到$GOPATH/src目录下,所以建议把工程文件放到$GOPATH/src/git域名/git_group/目录下。
29+
30+
https://github.com/dev-openapi/wxmp 则该工程文件为 $GOPATH/src/github.com/dev-openapi/wxmp
31+
32+
另外,也可以指定protoc-gen-go的生成目录和设置source_relative相关配置,也可以生成到指定目录
33+
34+
35+
```shell
36+
protoc --go_out=.. --go_opt=paths=source_relative --go_api_out=out=..:. *.proto
37+
```
38+
39+
这样会生成到上一层目录

go.sum

Lines changed: 1392 additions & 0 deletions
Large diffs are not rendered by default.

internal/genapi/comment.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package genapi
2+
3+
import (
4+
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
5+
"google.golang.org/protobuf/runtime/protoiface"
6+
)
7+
8+
var (
9+
comments = make(map[protoiface.MessageV1]string)
10+
)
11+
12+
func initComment(req *plugin.CodeGeneratorRequest) {
13+
for _, f := range req.GetProtoFile() {
14+
for _, loc := range f.GetSourceCodeInfo().GetLocation() {
15+
if loc.LeadingComments == nil {
16+
continue
17+
}
18+
19+
// p is an array with format [f1, i1, f2, i2, ...]
20+
// - f1 refers to the protobuf field tag
21+
// - if field refer to by f1 is a slice, i1 refers to an element in that slice
22+
// - f2 and i2 works recursively.
23+
// So, [6, x] refers to the xth service defined in the file,
24+
// since the field tag of Service is 6.
25+
// [6, x, 2, y] refers to the yth method in that service,
26+
// since the field tag of Method is 2.
27+
p := loc.Path
28+
switch {
29+
case len(p) == 2 && p[0] == 6:
30+
comments[f.Service[p[1]]] = *loc.LeadingComments
31+
case len(p) == 4 && p[0] == 6 && p[2] == 2:
32+
comments[f.Service[p[1]].Method[p[3]]] = *loc.LeadingComments
33+
}
34+
}
35+
}
36+
}
37+
38+
func getComment(m protoiface.MessageV1) string {
39+
c, ok := comments[m]
40+
if !ok {
41+
return ""
42+
}
43+
if c[len(c)-1] == '\n' {
44+
return c[:len(c)-1]
45+
}
46+
return c
47+
}

internal/genapi/const.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package genapi
2+
3+
import (
4+
"regexp"
5+
6+
"github.com/golang/protobuf/protoc-gen-go/descriptor"
7+
)
8+
9+
type restInfo struct {
10+
verb, route, body, typ string
11+
}
12+
13+
const (
14+
BODY_JSON = "json"
15+
BODY_FORM = "form"
16+
BODY_MULTI = "multi"
17+
)
18+
19+
const (
20+
emptyValue = "google.protobuf.Empty"
21+
// protoc puts a dot in front of name, signaling that the name is fully qualified.
22+
emptyType = "." + emptyValue
23+
lroType = ".google.longrunning.Operation"
24+
httpBodyType = ".google.api.HttpBody"
25+
alpha = "alpha"
26+
beta = "beta"
27+
disableDeadlinesVar = "GOOGLE_API_GO_EXPERIMENTAL_DISABLE_DEFAULT_DEADLINE"
28+
fieldTypeBool = descriptor.FieldDescriptorProto_TYPE_BOOL
29+
fieldTypeString = descriptor.FieldDescriptorProto_TYPE_STRING
30+
fieldTypeBytes = descriptor.FieldDescriptorProto_TYPE_BYTES
31+
fieldTypeMessage = descriptor.FieldDescriptorProto_TYPE_MESSAGE
32+
fieldLabelRepeated = descriptor.FieldDescriptorProto_LABEL_REPEATED
33+
defaultPollInitialDelay = "time.Second" // 1 second
34+
defaultPollMaxDelay = "time.Minute" // 1 minute
35+
)
36+
37+
var wellKnownTypes = []string{
38+
".google.protobuf.FieldMask",
39+
".google.protobuf.Timestamp",
40+
".google.protobuf.Duration",
41+
".google.protobuf.DoubleValue",
42+
".google.protobuf.FloatValue",
43+
".google.protobuf.Int64Value",
44+
".google.protobuf.UInt64Value",
45+
".google.protobuf.Int32Value",
46+
".google.protobuf.UInt32Value",
47+
".google.protobuf.BoolValue",
48+
".google.protobuf.StringValue",
49+
".google.protobuf.BytesValue",
50+
".google.protobuf.Value",
51+
".google.protobuf.ListValue",
52+
}
53+
54+
var httpPatternVarRegex = regexp.MustCompile(`{([a-zA-Z0-9_.]+?)(=[^{}]+)?}`)

internal/genapi/data.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package genapi
2+
3+
import "strings"
4+
5+
var (
6+
fn = map[string]interface{}{
7+
"unexport": unexport,
8+
"html": html,
9+
}
10+
)
11+
12+
var (
13+
noClientStream = `return nil, fmt.Errorf("%s not yet supported for REST clients")`
14+
noServerStream = `return nil, fmt.Errorf("%s not yet supported for REST servers")`
15+
noRestyOptions = `return nil, fmt.Errorf("%s has no resty options")`
16+
)
17+
18+
type FileData struct {
19+
Version string // 版本号
20+
Source string // 源文件
21+
GoPackage string // Go包名
22+
Services []*ServiceData // 服务数据
23+
}
24+
25+
type ServiceData struct {
26+
PkgName string // package name
27+
ServName string // 服务名,不带Service的
28+
Methods []*MethodData // 方法数据
29+
}
30+
31+
type MethodData struct {
32+
ServName string // 所属服务名
33+
MethName string // 方法名
34+
Comment string // 注释。只取头注释
35+
ReqTyp string // 请求类型名
36+
ResTyp string // 返回类型名
37+
ReqCode string // 请求代码
38+
}
39+
40+
type CodeData struct {
41+
Verb string
42+
RouteCode string
43+
BodyCode string
44+
QueryCode string
45+
}
46+
47+
type OptionData struct {
48+
GoPackage string
49+
}
50+
51+
// unexport 把首字母转小写
52+
func unexport(s string) string {
53+
if len(s) == 0 {
54+
return ""
55+
}
56+
return strings.ToLower(s[:1]) + s[1:]
57+
}
58+
59+
func html(s string) string {
60+
return s
61+
}

internal/genapi/gen.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package genapi
2+
3+
import (
4+
"fmt"
5+
"path"
6+
"strings"
7+
8+
"github.com/golang/protobuf/protoc-gen-go/descriptor"
9+
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
10+
"google.golang.org/protobuf/proto"
11+
)
12+
13+
func Gen(req *plugin.CodeGeneratorRequest) (*plugin.CodeGeneratorResponse, error) {
14+
initDescInfo(req)
15+
initComment(req)
16+
opts, err := parseOptions(req.Parameter)
17+
if err != nil {
18+
return nil, err
19+
}
20+
var resp plugin.CodeGeneratorResponse
21+
optdata := &OptionData{}
22+
for _, f := range req.GetProtoFile() {
23+
if !strContains(req.GetFileToGenerate(), f.GetName()) {
24+
continue
25+
}
26+
data, err := parseRestFile(f)
27+
if err != nil {
28+
return nil, err
29+
}
30+
if len(optdata.GoPackage) == 0 {
31+
optdata.GoPackage = data.GoPackage
32+
}
33+
bs, err := buildFrame(data)
34+
if err != nil {
35+
return nil, err
36+
}
37+
name := fmt.Sprintf("%s.api.go", strings.ReplaceAll(f.GetName(), ".proto", ""))
38+
if len(opts.out) > 0 {
39+
name = path.Join(opts.out, name)
40+
}
41+
resp.File = append(resp.File, &plugin.CodeGeneratorResponse_File{
42+
Name: proto.String(name),
43+
Content: proto.String(bs),
44+
})
45+
}
46+
bs, err := buildOptionsCode(optdata)
47+
if err != nil {
48+
return nil, err
49+
}
50+
resp.File = append(resp.File, &plugin.CodeGeneratorResponse_File{
51+
Name: proto.String("option.go"),
52+
Content: proto.String(bs),
53+
})
54+
return &resp, nil
55+
}
56+
57+
func parseRestFile(fd *descriptor.FileDescriptorProto) (*FileData, error) {
58+
pkg := fd.Options.GetGoPackage()
59+
ps := strings.Split(pkg, "/")
60+
data := &FileData{
61+
Version: Version,
62+
Source: fd.GetName(),
63+
GoPackage: strings.ReplaceAll(ps[len(ps)-1], "-", "_"),
64+
}
65+
servs := fd.GetService()
66+
67+
for _, serv := range servs {
68+
srv, err := parseRestService(fd, serv)
69+
if err != nil {
70+
return nil, err
71+
}
72+
data.Services = append(data.Services, srv)
73+
}
74+
75+
return data, nil
76+
}
77+
78+
func parseRestService(fd *descriptor.FileDescriptorProto, serv *descriptor.ServiceDescriptorProto) (*ServiceData, error) {
79+
data := &ServiceData{
80+
PkgName: fd.GetPackage(),
81+
ServName: strings.ReplaceAll(serv.GetName(), "Service", ""),
82+
}
83+
84+
meths := serv.GetMethod()
85+
for _, meth := range meths {
86+
mth, err := parseRestMethod(fd, serv, meth)
87+
if err != nil {
88+
return nil, err
89+
}
90+
data.Methods = append(data.Methods, mth)
91+
}
92+
93+
return data, nil
94+
}
95+
96+
func parseRestMethod(fd *descriptor.FileDescriptorProto, serv *descriptor.ServiceDescriptorProto, meth *descriptor.MethodDescriptorProto) (*MethodData, error) {
97+
data := &MethodData{
98+
ServName: strings.ReplaceAll(serv.GetName(), "Service", ""),
99+
MethName: meth.GetName(),
100+
Comment: getComment(meth),
101+
ReqTyp: typeName(meth.GetInputType()),
102+
ResTyp: typeName(meth.GetOutputType()),
103+
}
104+
switch {
105+
case meth.GetClientStreaming():
106+
data.ReqCode = fmt.Sprintf(noClientStream, meth.GetName())
107+
case meth.GetServerStreaming():
108+
data.ReqCode = fmt.Sprintf(noServerStream, meth.GetName())
109+
default:
110+
code, err := genRestMethodCode(fd, serv, meth)
111+
if err != nil {
112+
return nil, err
113+
}
114+
data.ReqCode = code
115+
}
116+
117+
return data, nil
118+
}

0 commit comments

Comments
 (0)