├── .gitignore ├── .vscode └── settings.json ├── example ├── readme.md ├── go.mod ├── Makefile ├── server │ ├── common │ │ └── common.go │ └── demo.go ├── client │ └── client.go ├── service │ └── service.go ├── go.sum └── util │ └── parameterUtil.go ├── go.mod ├── proto.go ├── template.go ├── readme.md ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.bak 3 | proto 4 | go2proto -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.inferGopath": false 3 | } -------------------------------------------------------------------------------- /example/readme.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | 这是一个简单的GRPC开发的项目 4 | 5 | 提前安装 protoc 等开发GRPC相关工具 6 | 7 | - 执行 `make proto`生成proto文件 ,并且编译proto文件 8 | - 启动service目录下的服务端项目 9 | - 启动client目录下的客户端项目,如果能接收到返回数据,说明项目执行正确 -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/akkagao/go2proto 2 | 3 | go 1.13 4 | 5 | require ( 6 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478 // indirect 7 | google.golang.org/grpc v1.23.1 // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /example/go.mod: -------------------------------------------------------------------------------- 1 | module go2protoexample 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/golang/protobuf v1.3.2 7 | github.com/google/uuid v1.1.1 8 | golang.org/x/net v0.0.0-20190918130420-a8b05e9114ab 9 | google.golang.org/grpc v1.23.1 10 | ) 11 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: proto 2 | proto: 3 | 4 | go2proto -t proto -f server/common/common.go 5 | go2proto -t proto -f server/demo.go 6 | 7 | protoc --go_out=plugins=grpc:. proto/common.proto 8 | protoc --go_out=plugins=grpc:. proto/demo.proto 9 | # protoc --micro_out=. --go_out=. proto/common.proto 10 | # protoc --micro_out=. --go_out=. proto/demo.proto 11 | -------------------------------------------------------------------------------- /example/server/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | /** 4 | 请求公共参数 5 | */ 6 | type CommonRequest struct { 7 | Token string // 请求权限校验参数 8 | Device Device // 设备标识 9 | } 10 | 11 | type Device struct { 12 | Channel string // 来源渠道 13 | } 14 | 15 | /* 16 | 返回公共参数 17 | */ 18 | type CommonResponse struct { 19 | RequestId string // 请求唯一id 20 | Code string // 请求结果状态码 21 | Message string // 请求结果描述信息 22 | } 23 | -------------------------------------------------------------------------------- /example/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | "golang.org/x/net/context" 8 | "google.golang.org/grpc" 9 | 10 | "go2protoexample/proto" 11 | "go2protoexample/util" 12 | ) 13 | 14 | const ( 15 | address = "localhost:50051" 16 | ) 17 | 18 | func main() { 19 | conn, err := grpc.Dial(address, grpc.WithInsecure()) 20 | if err != nil { 21 | log.Fatal("did not connect ", err) 22 | } 23 | defer conn.Close() 24 | 25 | c := proto.NewDemoClient(conn) 26 | demoResponse, err := c.CreateDemo(context.Background(), util.CreateParameter()) 27 | 28 | if err != nil { 29 | log.Fatal("could not greet ", err) 30 | } 31 | j, _ := json.Marshal(demoResponse) 32 | log.Printf("demoResponse: %v", string(j)) 33 | } 34 | -------------------------------------------------------------------------------- /example/service/service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // server.go 4 | 5 | import ( 6 | "encoding/json" 7 | "log" 8 | "net" 9 | 10 | "golang.org/x/net/context" 11 | "google.golang.org/grpc" 12 | 13 | "go2protoexample/proto" 14 | "go2protoexample/util" 15 | ) 16 | 17 | const ( 18 | port = ":50051" 19 | ) 20 | 21 | type DemoService struct{} 22 | 23 | func (s *DemoService) CreateDemo(ctx context.Context, demoRequest *proto.DemoRequest) (*proto.DemoResponse, error) { 24 | j, _ := json.Marshal(demoRequest) 25 | log.Println(string(j)) 26 | return util.CreateResult(demoRequest), nil 27 | } 28 | 29 | func main() { 30 | lis, err := net.Listen("tcp", port) 31 | if err != nil { 32 | log.Fatal("failed to listen: %v", err) 33 | } 34 | s := grpc.NewServer() 35 | proto.RegisterDemoServer(s, &DemoService{}) 36 | s.Serve(lis) 37 | } 38 | -------------------------------------------------------------------------------- /proto.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | MicroService 用于存储解析后的文件原信息 5 | */ 6 | type MicroService struct { 7 | PackageName string 8 | Imports []string 9 | ImportTime bool 10 | Service Service 11 | Messages []Message 12 | } 13 | 14 | /* 15 | Service 服务信息 16 | */ 17 | type Service struct { 18 | Name string // 服务名称 19 | PackageName string // 包名 20 | ServiceFunctions []ServiceFunction // 方法列表 21 | } 22 | 23 | /* 24 | ServiceFunction 接口定义的方法 25 | */ 26 | type ServiceFunction struct { 27 | Name string 28 | ParamType string 29 | ResultType string 30 | Comment string 31 | Stream bool 32 | PingPong bool 33 | } 34 | 35 | /* 36 | Message 接口定义的结构体 37 | */ 38 | type Message struct { 39 | Name string 40 | MessageFields []MessageField 41 | } 42 | 43 | /* 44 | MessageField 结构体字段属性 45 | */ 46 | type MessageField struct { 47 | Index int 48 | FieldName string 49 | FieldType string 50 | Comment string 51 | } 52 | -------------------------------------------------------------------------------- /template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var protoTemplate = `// Code generated by go2proto. DO NOT EDIT. 4 | syntax = "proto3"; 5 | 6 | {{- range $v := .Imports}} 7 | import "{{$v}}"; 8 | {{- end -}} 9 | {{- if .ImportTime}} 10 | import "google/protobuf/timestamp.proto"; 11 | {{end}} 12 | package {{.PackageName}}; 13 | {{ if .Service.Name}} 14 | service {{.Service.Name}} { 15 | {{- range .Service.ServiceFunctions}} 16 | {{- if .PingPong}} 17 | rpc {{.Name}} (stream {{.ParamType}}) returns (stream {{.ResultType}}) {} {{.Comment}} 18 | {{- else if .Stream}} 19 | rpc {{.Name}} ({{.ParamType}}) returns (stream {{.ResultType}}) {} {{.Comment}} 20 | {{- else}} 21 | rpc {{.Name}} ({{.ParamType}}) returns ({{.ResultType}}) {} {{.Comment}} 22 | {{- end}} 23 | {{- end}} 24 | } 25 | {{end -}} 26 | {{range .Messages}} 27 | message {{.Name}} { 28 | {{- range .MessageFields}} 29 | {{.FieldType}} {{.FieldName}} = {{.Index}}; {{.Comment}} 30 | {{- end}} 31 | } 32 | {{end -}} 33 | ` 34 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # go2proto 2 | 3 | **不用了解Protobuf语法也能轻松使用golang开发GRPC服务** 4 | 5 | go2proto 可以很轻松的根据Golang定义的接口生成proto文件,很大程度简化GRPC服务的开发工作。当公司要使用GRPC开发项目的时候就不用再感叹`学不动了` 6 | 7 | ## show code 8 | 9 | - 创建一个user.go, 写入如下内容 10 | 11 | ```go 12 | package server 13 | 14 | type User interface { 15 | Createuser(request Request) Response 16 | } 17 | 18 | type Request struct { 19 | Name string 20 | } 21 | type Response struct { 22 | Result string 23 | } 24 | ``` 25 | 26 | - 生成proto文件 27 | 28 | 在user.go 同目录下执行 ` go2proto -f user.go` 就会自动在当前目录的proto文件夹生成user.proto 文件 29 | 30 | ```protobuf 31 | // Code generated by go2proto. DO NOT EDIT. 32 | syntax = "proto3"; 33 | package proto; 34 | 35 | service User { 36 | rpc Createuser (Request) returns (Response) {} 37 | } 38 | 39 | message Request { 40 | string Name = 1; 41 | } 42 | 43 | message Response { 44 | string Result = 1; 45 | } 46 | ``` 47 | 48 | 是不是很简单呢,可以完全不用了解Protobuf语法,只要用Go定义接口就可以 49 | 50 | ## 安装 51 | 52 | ```shell 53 | go get -u github.com/akkagao/go2proto 54 | ``` 55 | 56 | ## 使用 57 | 58 | 安装完执行 go2proto 如果能输出一下内容则说明安装成功 59 | 60 | ```shell 61 | ➜ go2proto git:(master) ✗ go2proto 62 | go2proto version: go2proto/1.0.0 63 | Usage: go2proto [-f] [-t] 64 | 65 | Options: 66 | -f string 67 | source file path 68 | -t string 69 | proto file target path (default "proto") 70 | ``` 71 | 72 | -f 参数用于指定 go接口文件 73 | 74 | -t 参数用于指定生成的proto文件存储的目录 75 | 76 | ## 注意事项 77 | 78 | 由于这里定义服务的go文件只是用于生成proto文件,建议不要在代码中引用这里定义的struct。 79 | 80 | 切记由于proto中的字段顺序都是有编号的,所以不要轻易删除字段或修改字段顺序。尤其是项目发布后。 81 | 82 | 重要的事情说三遍: 83 | 84 | **不要删除字段,不要修改顺序** 85 | 86 | **不要删除字段,不要修改顺序** 87 | 88 | **不要删除字段,不要修改顺序** 89 | 90 | ## 实现方法 91 | 92 | 使用Go提供的源码解析工具把go文件解析成ast语法树,然后分析ast语法树内容。通过模板生成proto文件。 93 | 94 | 代码很简单关键代码不到300行,有兴趣可以花几分钟时间看一下。 95 | 96 | ## 参考资料: 97 | 98 | https://www.jianshu.com/p/937d649039ec 99 | 100 | https://segmentfault.com/a/1190000020386857 101 | 102 | 感谢以上两篇博客的作者 -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 4 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 5 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 6 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 7 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 8 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 9 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 10 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 11 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 12 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 13 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= 14 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 15 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 16 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 17 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 18 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 19 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 20 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 21 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 22 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 23 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 24 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 25 | google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk= 26 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 27 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 28 | -------------------------------------------------------------------------------- /example/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 4 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 5 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 6 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 7 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 8 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 9 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 10 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 11 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 12 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 13 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 14 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 15 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 16 | golang.org/x/net v0.0.0-20190918130420-a8b05e9114ab h1:h5tBRKZ1aY/bo6GNqe/4zWC8GkaLOFQ5wPKIOQ0i2sA= 17 | golang.org/x/net v0.0.0-20190918130420-a8b05e9114ab/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 18 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 19 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 20 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 21 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 22 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 23 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 24 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 25 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 26 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 27 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 28 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 29 | google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk= 30 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 31 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 32 | -------------------------------------------------------------------------------- /example/util/parameterUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "math/rand" 5 | "strings" 6 | "time" 7 | 8 | "github.com/google/uuid" 9 | 10 | "go2proto/example/proto" 11 | ) 12 | 13 | func CreateParameter() *proto.DemoRequest { 14 | rand.Seed(time.Now().UnixNano()) 15 | demoRequest := &proto.DemoRequest{ 16 | CommonRequest: &proto.CommonRequest{ 17 | Token: "", 18 | Device: &proto.Device{ 19 | Channel: "999999999", 20 | }, 21 | }, 22 | Float64Type: rand.Float64(), 23 | Float32Type: rand.Float32(), 24 | Int32Type: rand.Int31(), 25 | Int64Type: rand.Int63(), 26 | Uint32Type: rand.Uint32(), 27 | Uint64Type: rand.Uint64(), 28 | BoolType: false, 29 | StringType: time.Now().Format("2006/01/02 15:04:05"), 30 | Float64ArrayType: []float64{rand.Float64()}, 31 | Float32ArrayType: []float32{rand.Float32()}, 32 | Int32ArrayType: []int32{rand.Int31()}, 33 | Int64ArrayType: []int64{rand.Int63()}, 34 | Uint32ArrayType: []uint32{rand.Uint32()}, 35 | Uint64ArrayType: []uint64{rand.Uint64()}, 36 | BoolArrayType: []bool{true, false}, 37 | StringArrayType: []string{"1", "2"}, 38 | BytesType: []byte("hello"), 39 | StructType: createStructType(), 40 | StructTypes: []*proto.StructType{ 41 | createStructType(), 42 | createStructType(), 43 | }, 44 | } 45 | return demoRequest 46 | } 47 | 48 | func CreateResult(demoRequest *proto.DemoRequest) *proto.DemoResponse { 49 | rand.Seed(time.Now().UnixNano()) 50 | demoResponse := &proto.DemoResponse{ 51 | CommonResponse: &proto.CommonResponse{ 52 | RequestId: getReqId(), 53 | Code: "200", 54 | Message: "success", 55 | }, 56 | Float64Type: demoRequest.Float64Type, 57 | Float32Type: demoRequest.Float32Type, 58 | Int32Type: demoRequest.Int32Type, 59 | Int64Type: demoRequest.Int64Type, 60 | Uint32Type: demoRequest.Uint32Type, 61 | Uint64Type: demoRequest.Uint64Type, 62 | BoolType: demoRequest.BoolType, 63 | StringType: demoRequest.StringType, 64 | Float64ArrayType: demoRequest.Float64ArrayType, 65 | Float32ArrayType: demoRequest.Float32ArrayType, 66 | Int32ArrayType: demoRequest.Int32ArrayType, 67 | Int64ArrayType: demoRequest.Int64ArrayType, 68 | Uint32ArrayType: demoRequest.Uint32ArrayType, 69 | Uint64ArrayType: demoRequest.Uint64ArrayType, 70 | BoolArrayType: demoRequest.BoolArrayType, 71 | StringArrayType: demoRequest.StringArrayType, 72 | BytesType: demoRequest.BytesType, 73 | StructType: demoRequest.StructType, 74 | StructTypes: demoRequest.StructTypes, 75 | } 76 | return demoResponse 77 | } 78 | 79 | func getReqId() string { 80 | uuidTmp, err := uuid.NewRandom() 81 | if err != nil { 82 | return "" 83 | } 84 | return strings.Replace(uuidTmp.String(), "-", "", -1) 85 | 86 | } 87 | 88 | func createStructType() *proto.StructType { 89 | structType := &proto.StructType{ 90 | Float64Type: rand.Float64(), 91 | Float32Type: rand.Float32(), 92 | Int32Type: rand.Int31(), 93 | Int64Type: rand.Int63(), 94 | Uint32Type: rand.Uint32(), 95 | Uint64Type: rand.Uint64(), 96 | BoolType: false, 97 | StringType: time.Now().Format("2006/01/02 15:04:05"), 98 | Float64ArrayType: []float64{rand.Float64()}, 99 | Float32ArrayType: []float32{rand.Float32()}, 100 | Int32ArrayType: []int32{rand.Int31()}, 101 | Int64ArrayType: []int64{rand.Int63()}, 102 | Uint32ArrayType: []uint32{rand.Uint32()}, 103 | Uint64ArrayType: []uint64{rand.Uint64()}, 104 | BoolArrayType: []bool{true, false}, 105 | StringArrayType: []string{"1", "2"}, 106 | BytesType: []byte("hello"), 107 | } 108 | return structType 109 | } 110 | -------------------------------------------------------------------------------- /example/server/demo.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "go2protoexample/server/common" 5 | ) 6 | 7 | /* 8 | UserDemo 用户服务 9 | */ 10 | type Demo interface { 11 | CreateDemo(demoRequest DemoRequest) DemoResponse // 创建DEMO 12 | // DemoStream_stream(userRequest DemoRequestStream) DemoResponseStream // stream 调用 13 | // DemoPingPang_pingpang(userRequest DemoRequestPingPang) DemoResponsePingPang // pingpang 调用 14 | } 15 | 16 | type DemoRequest struct { 17 | CommonRequest common.CommonRequest // 请求公共参数 18 | Float64Type float64 // float64 参数测试 19 | Float32Type float32 // float32 参数测试 20 | Int32Type int32 // int32 参数测试 21 | Int64Type int64 // int64 参数测试 22 | Uint32Type uint32 // uint32 参数测试 23 | Uint64Type uint64 // uint64 参数测试 24 | BoolType bool // bool 参数测试 25 | StringType string // string 参数测试 26 | Float64ArrayType []float64 // []float64 参数测试 27 | Float32ArrayType []float32 // []float32 参数测试 28 | Int32ArrayType []int32 // []int32 参数测试 29 | Int64ArrayType []int64 // []int64 参数测试 30 | Uint32ArrayType []uint32 // []uint32 参数测试 31 | Uint64ArrayType []uint64 // []uint64 参数测试 32 | BoolArrayType []bool // []bool 参数测试 33 | StringArrayType []string // []string 参数测试 34 | BytesType []byte // []byte 类型测试 35 | StructType StructType // 结构体类型的测试 36 | StructTypes []StructType // 结构体数组类型测试 37 | } 38 | 39 | type DemoRequestPingPang struct { 40 | CommonRequest common.CommonRequest // 请求公共参数 41 | Float64Type float64 // float64 参数测试 42 | Float32Type float32 // float32 参数测试 43 | Int32Type int32 // int32 参数测试 44 | Int64Type int64 // int64 参数测试 45 | Uint32Type uint32 // uint32 参数测试 46 | Uint64Type uint64 // uint64 参数测试 47 | BoolType bool // bool 参数测试 48 | StringType string // string 参数测试 49 | Float64ArrayType []float64 // []float64 参数测试 50 | Float32ArrayType []float32 // []float32 参数测试 51 | Int32ArrayType []int32 // []int32 参数测试 52 | Int64ArrayType []int64 // []int64 参数测试 53 | Uint32ArrayType []uint32 // []uint32 参数测试 54 | Uint64ArrayType []uint64 // []uint64 参数测试 55 | BoolArrayType []bool // []bool 参数测试 56 | StringArrayType []string // []string 参数测试 57 | BytesType []byte // []byte 类型测试 58 | StructType StructType // 结构体类型的测试 59 | StructTypes []StructType // 结构体数组类型测试 60 | } 61 | 62 | type DemoRequestStream struct { 63 | CommonRequest common.CommonRequest // 请求公共参数 64 | Float64Type float64 // float64 参数测试 65 | Float32Type float32 // float32 参数测试 66 | Int32Type int32 // int32 参数测试 67 | Int64Type int64 // int64 参数测试 68 | Uint32Type uint32 // uint32 参数测试 69 | Uint64Type uint64 // uint64 参数测试 70 | BoolType bool // bool 参数测试 71 | StringType string // string 参数测试 72 | Float64ArrayType []float64 // []float64 参数测试 73 | Float32ArrayType []float32 // []float32 参数测试 74 | Int32ArrayType []int32 // []int32 参数测试 75 | Int64ArrayType []int64 // []int64 参数测试 76 | Uint32ArrayType []uint32 // []uint32 参数测试 77 | Uint64ArrayType []uint64 // []uint64 参数测试 78 | BoolArrayType []bool // []bool 参数测试 79 | StringArrayType []string // []string 参数测试 80 | BytesType []byte // []byte 类型测试 81 | StructType StructType // 结构体类型的测试 82 | StructTypes []StructType // 结构体数组类型测试 83 | } 84 | 85 | type StructType struct { 86 | Float64Type float64 // float64 参数测试 87 | Float32Type float32 // float32 参数测试 88 | Int32Type int32 // int32 参数测试 89 | Int64Type int64 // int64 参数测试 90 | Uint32Type uint32 // uint32 参数测试 91 | Uint64Type uint64 // uint64 参数测试 92 | BoolType bool // bool 参数测试 93 | StringType string // string 参数测试 94 | Float64ArrayType []float64 // []float64 参数测试 95 | Float32ArrayType []float32 // []float32 参数测试 96 | Int32ArrayType []int32 // []int32 参数测试 97 | Int64ArrayType []int64 // []int64 参数测试 98 | Uint32ArrayType []uint32 // []uint32 参数测试 99 | Uint64ArrayType []uint64 // []uint64 参数测试 100 | BoolArrayType []bool // []bool 参数测试 101 | StringArrayType []string // []string 参数测试 102 | BytesType []byte // []byte 类型测试 103 | } 104 | 105 | type DemoResponse struct { 106 | CommonResponse common.CommonResponse // 返回公共参数 107 | Float64Type float64 // float64 参数测试 108 | Float32Type float32 // float32 参数测试 109 | Int32Type int32 // int32 参数测试 110 | Int64Type int64 // int64 参数测试 111 | Uint32Type uint32 // uint32 参数测试 112 | Uint64Type uint64 // uint64 参数测试 113 | BoolType bool // bool 参数测试 114 | StringType string // string 参数测试 115 | Float64ArrayType []float64 // []float64 参数测试 116 | Float32ArrayType []float32 // []float32 参数测试 117 | Int32ArrayType []int32 // []int32 参数测试 118 | Int64ArrayType []int64 // []int64 参数测试 119 | Uint32ArrayType []uint32 // []uint32 参数测试 120 | Uint64ArrayType []uint64 // []uint64 参数测试 121 | BoolArrayType []bool // []bool 参数测试 122 | StringArrayType []string // []string 参数测试 123 | BytesType []byte // []byte 类型测试 124 | StructType StructType // 结构体类型的测试 125 | StructTypes []StructType // 结构体数组类型测试 126 | } 127 | 128 | type DemoResponseStream struct { 129 | CommonResponse common.CommonResponse // 返回公共参数 130 | Float64Type float64 // float64 参数测试 131 | Float32Type float32 // float32 参数测试 132 | Int32Type int32 // int32 参数测试 133 | Int64Type int64 // int64 参数测试 134 | Uint32Type uint32 // uint32 参数测试 135 | Uint64Type uint64 // uint64 参数测试 136 | BoolType bool // bool 参数测试 137 | StringType string // string 参数测试 138 | Float64ArrayType []float64 // []float64 参数测试 139 | Float32ArrayType []float32 // []float32 参数测试 140 | Int32ArrayType []int32 // []int32 参数测试 141 | Int64ArrayType []int64 // []int64 参数测试 142 | Uint32ArrayType []uint32 // []uint32 参数测试 143 | Uint64ArrayType []uint64 // []uint64 参数测试 144 | BoolArrayType []bool // []bool 参数测试 145 | StringArrayType []string // []string 参数测试 146 | BytesType []byte // []byte 类型测试 147 | StructType StructType // 结构体类型的测试 148 | StructTypes []StructType // 结构体数组类型测试 149 | } 150 | 151 | type DemoResponsePingPang struct { 152 | CommonResponse common.CommonResponse // 返回公共参数 153 | Float64Type float64 // float64 参数测试 154 | Float32Type float32 // float32 参数测试 155 | Int32Type int32 // int32 参数测试 156 | Int64Type int64 // int64 参数测试 157 | Uint32Type uint32 // uint32 参数测试 158 | Uint64Type uint64 // uint64 参数测试 159 | BoolType bool // bool 参数测试 160 | StringType string // string 参数测试 161 | Float64ArrayType []float64 // []float64 参数测试 162 | Float32ArrayType []float32 // []float32 参数测试 163 | Int32ArrayType []int32 // []int32 参数测试 164 | Int64ArrayType []int64 // []int64 参数测试 165 | Uint32ArrayType []uint32 // []uint32 参数测试 166 | Uint64ArrayType []uint64 // []uint64 参数测试 167 | BoolArrayType []bool // []bool 参数测试 168 | StringArrayType []string // []string 参数测试 169 | BytesType []byte // []byte 类型测试 170 | StructType StructType // 结构体类型的测试 171 | StructTypes []StructType // 结构体数组类型测试 172 | } 173 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "log" 10 | "os" 11 | "path" 12 | "strings" 13 | "text/template" 14 | "unicode" 15 | ) 16 | 17 | const streamFlag = "_stream" 18 | const pingpang = "_pingpang" 19 | 20 | var filePath string 21 | 22 | // var dir string 23 | var target string 24 | 25 | func init() { 26 | flag.StringVar(&filePath, "f", "", "source file path") 27 | // flag.StringVar(&dir, "d", "", "source file dir path") 28 | flag.StringVar(&target, "t", "proto", "proto file target path") 29 | flag.Usage = usage 30 | } 31 | 32 | func main() { 33 | flag.Parse() 34 | if filePath == "" { 35 | flag.Usage() 36 | return 37 | } 38 | start(filePath) 39 | } 40 | 41 | func start(interfacePath string) { 42 | // Create the AST by parsing src. 43 | fset := token.NewFileSet() // positions are relative to fset 44 | f, err := parser.ParseFile(fset, interfacePath, nil, parser.ParseComments) 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | microService := MicroService{PackageName: target} 50 | imports := []string{} 51 | messages := []Message{} 52 | ast.Inspect(f, func(node ast.Node) bool { 53 | if node == nil { 54 | return true 55 | } 56 | if importSpec, ok := node.(*ast.ImportSpec); ok { 57 | if importSpec.Path.Value == "\"time\"" { 58 | microService.ImportTime = true 59 | } else { 60 | importpath := importSpec.Path.Value 61 | importpath = fmt.Sprintf("%v%v.proto", target, importpath[strings.LastIndex(importpath, "/"):len(importpath)-1]) 62 | imports = append(imports, importpath) 63 | } 64 | } 65 | 66 | if typeSpecNode, ok := node.(*ast.TypeSpec); ok { 67 | // 处理接口 68 | if interfaceNode, f := typeSpecNode.Type.(*ast.InterfaceType); f { 69 | fmt.Println("接口名称:", typeSpecNode.Name.Name) 70 | serviceFunctions := interfaceParser(interfaceNode) 71 | service := Service{} 72 | service.Name = typeSpecNode.Name.Name 73 | service.PackageName = genPackageName(typeSpecNode.Name.Name) 74 | service.ServiceFunctions = serviceFunctions 75 | microService.Service = service 76 | // spew.Dump(serviceFunctions) 77 | } 78 | // 处理结构体 79 | if structNode, f := typeSpecNode.Type.(*ast.StructType); f { 80 | structName := typeSpecNode.Name.Name 81 | log.Println("struct名称:", structName) 82 | messageFields := structParser(structName, structNode) 83 | message := Message{} 84 | message.Name = structName 85 | message.MessageFields = messageFields 86 | messages = append(messages, message) 87 | // spew.Dump(messageFields) 88 | } 89 | 90 | } 91 | return true 92 | }) 93 | microService.Messages = messages 94 | microService.Imports = imports 95 | 96 | targetFileName := strings.Replace(path.Base(interfacePath), ".go", ".proto", -1) 97 | saveToFile(microService, fmt.Sprintf("%v/%v", target, targetFileName)) 98 | } 99 | 100 | func genPackageName(s string) string { 101 | if unicode.IsUpper([]rune(s)[0]) { 102 | return strings.ToLower(string(s[0])) + string(s[1:]) 103 | } 104 | return s 105 | } 106 | 107 | /* 108 | proto 文件模板 109 | */ 110 | func saveToFile(microService MicroService, protoPath string) { 111 | t, err := template.New("protoTemplate").Parse(protoTemplate) 112 | if err != nil { 113 | log.Panic(err) 114 | } 115 | os.MkdirAll(path.Dir(protoPath), 0755) 116 | os.Remove(protoPath) 117 | f, err := os.OpenFile(protoPath, os.O_CREATE|os.O_RDWR, 0755) 118 | if err != nil { 119 | log.Panic(err) 120 | } 121 | 122 | err = t.Execute(f, microService) 123 | if err != nil { 124 | log.Panic(err) 125 | } 126 | } 127 | 128 | /* 129 | 解析结构体 130 | */ 131 | func structParser(structName string, structNode *ast.StructType) []MessageField { 132 | messageFields := []MessageField{} 133 | for i, field := range structNode.Fields.List { 134 | messageField := MessageField{} 135 | messageField.Index = i + 1 136 | // 如果是类似name,address string 这样的定义则报错 137 | if len(field.Names) != 1 { 138 | log.Fatalf("struct %v error,the field can't define like 'name,address string'", structName) 139 | } 140 | messageField.FieldName = field.Names[0].Name 141 | 142 | // 基本类型处理 143 | if fieldType, ok := field.Type.(*ast.Ident); ok { 144 | messageField.FieldType = getProtoType(fieldType.Name) 145 | } 146 | 147 | // map类型处理 148 | if fieldType, ok := field.Type.(*ast.MapType); ok { 149 | key, value := "", "" 150 | if keyType, ok := fieldType.Key.(*ast.Ident); ok { 151 | key = getProtoType(keyType.Name) 152 | } 153 | if valueType, ok := fieldType.Value.(*ast.Ident); ok { 154 | value = getProtoType(valueType.Name) 155 | } 156 | messageField.FieldType = fmt.Sprintf("map<%v,%v>", key, value) 157 | } 158 | 159 | // 处理引用类型 160 | if fieldType, ok := field.Type.(*ast.SelectorExpr); ok { 161 | if p, ok := fieldType.X.(*ast.Ident); ok && p.Name == "time" { 162 | messageField.FieldType = "google.protobuf.Timestamp" 163 | } else { 164 | messageField.FieldType = fieldType.Sel.Name 165 | } 166 | } 167 | // 处理参数是数组的情况 168 | if fieldType, ok := field.Type.(*ast.ArrayType); ok { 169 | if fieldTypeElt, ok := fieldType.Elt.(*ast.Ident); ok { 170 | // byte 数组特殊处理 转换成bytes 171 | if fieldTypeElt.Name == "byte" { 172 | messageField.FieldType = "bytes" 173 | } else { 174 | messageField.FieldType = "repeated " + getProtoType(fieldTypeElt.Name) 175 | } 176 | } 177 | } 178 | // 获取注释 179 | if field.Comment != nil { 180 | messageField.Comment = field.Comment.List[0].Text 181 | } 182 | messageFields = append(messageFields, messageField) 183 | } 184 | return messageFields 185 | } 186 | 187 | /* 188 | 解析接口代码 189 | */ 190 | func interfaceParser(interfaceNode *ast.InterfaceType) []ServiceFunction { 191 | serviceFunctions := []ServiceFunction{} 192 | // 解析方法列表 193 | for _, function := range interfaceNode.Methods.List { 194 | 195 | serviceFunction := ServiceFunction{} 196 | if function.Comment != nil { 197 | serviceFunction.Comment = function.Comment.List[0].Text 198 | } 199 | // 获取方法名称 200 | if len(function.Names) != 1 { 201 | log.Fatal("parser function error") 202 | } 203 | 204 | functionName := function.Names[0].Name 205 | if strings.HasSuffix(functionName, streamFlag) { 206 | serviceFunction.Name = strings.Replace(functionName, streamFlag, "", -1) 207 | serviceFunction.Stream = true 208 | } else if strings.HasSuffix(functionName, pingpang) { 209 | serviceFunction.Name = strings.Replace(functionName, pingpang, "", -1) 210 | serviceFunction.PingPong = true 211 | } else { 212 | serviceFunction.Name = functionName 213 | } 214 | 215 | // 解析方法 216 | if funcBody, ok := function.Type.(*ast.FuncType); ok { 217 | // 解析参数列表 218 | for i, param := range funcBody.Params.List { 219 | // 获取参数名称 220 | for _, paramName := range param.Names { 221 | log.Printf("function:%v index:%v paramName:%v ", functionName, i+1, paramName.Name) 222 | } 223 | // 获取参数类型 224 | if paramType, ok := param.Type.(*ast.Ident); ok { 225 | serviceFunction.ParamType = paramType.Name 226 | log.Printf("function:%v index:%v paramType:%v ", functionName, i+1, paramType.Name) 227 | } 228 | } 229 | // 解析返回值 230 | for i, result := range funcBody.Results.List { 231 | // 获取参数名称 232 | for _, resultName := range result.Names { 233 | log.Printf("function:%v index:%v resultName:%v ", functionName, i+1, resultName.Name) 234 | } 235 | // 获取参数类型 236 | if resultType, ok := result.Type.(*ast.Ident); ok { 237 | serviceFunction.ResultType = resultType.Name 238 | log.Printf("function:%v index:%v resultType:%v ", functionName, i+1, resultType.Name) 239 | } 240 | } 241 | } 242 | serviceFunctions = append(serviceFunctions, serviceFunction) 243 | } 244 | return serviceFunctions 245 | } 246 | 247 | /* 248 | go 类型转换成proto类型 249 | */ 250 | func getProtoType(fieldType string) string { 251 | switch fieldType { 252 | case "float64": 253 | return "double"; 254 | case "float32": 255 | return "float" 256 | case "int32", "int": 257 | return "int32" 258 | case "int64": 259 | return "int64" 260 | case "uint32": 261 | return "uint32" 262 | case "uint64": 263 | return "uint64" 264 | case "bool": 265 | return "bool" 266 | case "string": 267 | return "string" 268 | case "int8", "int16": 269 | log.Fatal("int8, int16 is nonsupport type") 270 | return fieldType 271 | default: 272 | // 如果类型是引用其他结构体,则直接返回名称 273 | return fieldType 274 | } 275 | } 276 | 277 | func usage() { 278 | fmt.Fprintf(os.Stderr, `go2proto version: go2proto/1.0.0 279 | Usage: go2proto [-f] [-t] 280 | 281 | Options: 282 | `) 283 | flag.PrintDefaults() 284 | } 285 | --------------------------------------------------------------------------------