commit 696184f41ad91dc8998e16233563438db043c497 Author: Young Xu Date: Tue Mar 21 01:08:22 2023 +0800 feat: api diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e8e8267 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +go.sum diff --git a/example.proto b/example.proto new file mode 100644 index 0000000..84fb59b --- /dev/null +++ b/example.proto @@ -0,0 +1,70 @@ +syntax = "proto3"; + +package controller; + +option go_package = "./;core"; + + +// @route_group: true +// @route_api: /api/file +// @gen_to: ./core/file_controller.go +service File { + // @desc: 列表 + // @author: Young Xu + // @method: GET + // @api: /list + rpc List (ListReq) returns (ListResp); + // @desc: 上传 + // @author: Young Xu + // @method: POST + // @api: /upload + rpc Upload (UploadReq) returns (UploadResp); + // @desc: 删除 + // @author: Young Xu + // @method: POST + // @api: /delete + rpc Delete (DeleteReq) returns (DeleteResp); + // @desc: 下载 + // @author: Young Xu + // @method: GET + // @api: /download + rpc Download (DownloadReq) returns (DownloadResp); + // @desc: + // @author: + // @method: + // @api: /update_user_info + rpc UpdateUserInfo (UpdateUserInfoReq) returns (UpdateUserInfoResp); +} + + +message ListReq {} + +message ListResp { + message Item { + string filename = 1; // 文件名 + string file_size = 2; // 文件大小 + string created_at = 3; // 上传时间 + } + repeated Item items = 1; // 列表 +} + +message UploadReq {} + +message UploadResp {} + +message DeleteReq { + string filename = 1; // 文件名 +} + +message DeleteResp {} + +message DownloadReq { + // @v: required + string f = 1; // 文件地址 +} + +message DownloadResp {} + +message UpdateUserInfoReq {} + +message UpdateUserInfoResp {} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e873b7f --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module gitter.top/coco/gobuf + +go 1.18 + +require ( + github.com/emicklei/proto v1.11.1 + github.com/stretchr/testify v1.8.2 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gitter.top/sync/proto-contrib v0.15.0 + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/gobuf.go b/gobuf.go new file mode 100644 index 0000000..3a22b90 --- /dev/null +++ b/gobuf.go @@ -0,0 +1,118 @@ +package gobuf + +import ( + "bytes" + "github.com/emicklei/proto" + "gitter.top/sync/proto-contrib/pkg/protofmt" + "io" + "os" +) + +type Parser struct { + filename string + rawData io.Reader + proto *proto.Proto +} + +func NewParser(file string) (*Parser, error) { + reader, err := os.Open(file) + if err != nil { + return nil, err + } + defer reader.Close() + + parser := proto.NewParser(reader) + definition, err := parser.Parse() + if err != nil { + return nil, err + } + return &Parser{ + filename: file, + rawData: reader, + proto: definition, + }, nil +} + +func (parser *Parser) ExistService(serviceName string) bool { + var result bool + proto.Walk(parser.proto, proto.WithService(func(service *proto.Service) { + if service.Name == serviceName { + result = true + } + })) + return result +} + +func (parser *Parser) ExistMessage(messageName string) bool { + var result bool + proto.Walk(parser.proto, proto.WithMessage(func(message *proto.Message) { + if message.Name == messageName { + result = true + } + })) + return result +} + +func (parser *Parser) ExistRPC(serviceName, rpcName string) bool { + var result bool + proto.Walk(parser.proto, proto.WithService(func(service *proto.Service) { + if service.Name != serviceName { + return + } + for _, element := range service.Elements { + if rpc, ok := element.(*proto.RPC); ok { + if rpc.Name == rpcName { + result = true + return + } + } + } + })) + return result +} + +func (parser *Parser) AddRPC(serviceName, rpcName string) error { + proto.Walk(parser.proto, proto.WithService(func(service *proto.Service) { + if service.Name != serviceName { + return + } + service.Elements = append(service.Elements, &proto.RPC{ + Comment: &proto.Comment{ + Lines: []string{ + " @desc: ", + " @author: ", + " @method: ", + " @api: /" + calm2Case(rpcName), + }, + }, + Name: rpcName, + RequestType: rpcName + "Req", + ReturnsType: rpcName + "Resp", + Parent: service, + }) + })) + + parser.proto.Elements = append(parser.proto.Elements, &proto.Message{ + Name: rpcName + "Req", + Parent: parser.proto, + }) + parser.proto.Elements = append(parser.proto.Elements, &proto.Message{ + Name: rpcName + "Resp", + Parent: parser.proto, + }) + + return parser.writeSync() +} + +func (parser *Parser) writeSync() error { + var buf = new(bytes.Buffer) + + protofmt.NewFormatter(buf, " ").Format(parser.proto) // 1 tab + + // write back to input + if err := os.WriteFile(parser.filename, buf.Bytes(), os.ModePerm); err != nil { + return err + } + + return nil +} diff --git a/gobuf_test.go b/gobuf_test.go new file mode 100644 index 0000000..739dee0 --- /dev/null +++ b/gobuf_test.go @@ -0,0 +1,22 @@ +package gobuf + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParser_AddRPC(t *testing.T) { + parser, err := NewParser("example.proto") + assert.Nil(t, err) + existService := parser.ExistService("File") + assert.EqualValues(t, true, existService) + existRPC := parser.ExistRPC("File", "List") + assert.EqualValues(t, true, existRPC) + existRPC = parser.ExistRPC("File", "List1") + assert.EqualValues(t, false, existRPC) + existMsg := parser.ExistMessage("DeleteReq") + assert.EqualValues(t, true, existMsg) + err = parser.AddRPC("File", "UpdateUserInfo") + assert.Nil(t, err) +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..d9fc304 --- /dev/null +++ b/utils.go @@ -0,0 +1,22 @@ +package gobuf + +import ( + "bytes" + "unicode" +) + +// calm2Case 驼峰转下划线 +func calm2Case(src string) string { + buffer := new(bytes.Buffer) + for i, r := range src { + if unicode.IsUpper(r) { + if i != 0 { + buffer.WriteRune('_') + } + buffer.WriteRune(unicode.ToLower(r)) + } else { + buffer.WriteRune(r) + } + } + return buffer.String() +}