summaryrefslogtreecommitdiff
path: root/plugins/grpc/parser
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/grpc/parser')
-rw-r--r--plugins/grpc/parser/message.proto7
-rw-r--r--plugins/grpc/parser/parse.go114
-rw-r--r--plugins/grpc/parser/parse_test.go71
-rw-r--r--plugins/grpc/parser/pong.proto10
-rw-r--r--plugins/grpc/parser/test.proto20
-rw-r--r--plugins/grpc/parser/test_import.proto12
-rw-r--r--plugins/grpc/parser/test_nested/message.proto7
-rw-r--r--plugins/grpc/parser/test_nested/pong.proto10
-rw-r--r--plugins/grpc/parser/test_nested/test_import.proto12
9 files changed, 263 insertions, 0 deletions
diff --git a/plugins/grpc/parser/message.proto b/plugins/grpc/parser/message.proto
new file mode 100644
index 00000000..a4012010
--- /dev/null
+++ b/plugins/grpc/parser/message.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+package app.namespace;
+
+message Message {
+ string msg = 1;
+ int64 value = 2;
+} \ No newline at end of file
diff --git a/plugins/grpc/parser/parse.go b/plugins/grpc/parser/parse.go
new file mode 100644
index 00000000..d59b0927
--- /dev/null
+++ b/plugins/grpc/parser/parse.go
@@ -0,0 +1,114 @@
+package parser
+
+import (
+ "bytes"
+ "io"
+ "os"
+
+ pp "github.com/emicklei/proto"
+)
+
+// Service contains information about singular GRPC service.
+type Service struct {
+ // Package defines service namespace.
+ Package string
+
+ // Name defines service name.
+ Name string
+
+ // Methods list.
+ Methods []Method
+}
+
+// Method describes singular RPC method.
+type Method struct {
+ // Name is method name.
+ Name string
+
+ // StreamsRequest defines if method accept stream input.
+ StreamsRequest bool
+
+ // RequestType defines message name (from the same package) of method input.
+ RequestType string
+
+ // StreamsReturns defines if method streams result.
+ StreamsReturns bool
+
+ // ReturnsType defines message name (from the same package) of method return value.
+ ReturnsType string
+}
+
+// File parses given proto file or returns error.
+func File(file string, importPath string) ([]Service, error) {
+ reader, _ := os.Open(file)
+ defer reader.Close()
+
+ return parse(reader, importPath)
+}
+
+// Bytes parses string into proto definition.
+func Bytes(data []byte) ([]Service, error) {
+ return parse(bytes.NewBuffer(data), "")
+}
+
+func parse(reader io.Reader, importPath string) ([]Service, error) {
+ proto, err := pp.NewParser(reader).Parse()
+ if err != nil {
+ return nil, err
+ }
+
+ return parseServices(
+ proto,
+ parsePackage(proto),
+ importPath,
+ )
+}
+
+func parsePackage(proto *pp.Proto) string {
+ for _, e := range proto.Elements {
+ if p, ok := e.(*pp.Package); ok {
+ return p.Name
+ }
+ }
+
+ return ""
+}
+
+func parseServices(proto *pp.Proto, pkg string, importPath string) ([]Service, error) {
+ services := make([]Service, 0)
+
+ pp.Walk(proto, pp.WithService(func(service *pp.Service) {
+ services = append(services, Service{
+ Package: pkg,
+ Name: service.Name,
+ Methods: parseMethods(service),
+ })
+ }))
+
+ pp.Walk(proto, func(v pp.Visitee) {
+ if i, ok := v.(*pp.Import); ok {
+ if im, err := File(importPath+"/"+i.Filename, importPath); err == nil {
+ services = append(services, im...)
+ }
+ }
+ })
+
+ return services, nil
+}
+
+func parseMethods(s *pp.Service) []Method {
+ methods := make([]Method, 0)
+ for _, e := range s.Elements {
+ if m, ok := e.(*pp.RPC); ok {
+ methods = append(methods, Method{
+ Name: m.Name,
+ StreamsRequest: m.StreamsRequest,
+ RequestType: m.RequestType,
+ StreamsReturns: m.StreamsReturns,
+ ReturnsType: m.ReturnsType,
+ })
+ }
+ }
+
+ return methods
+}
diff --git a/plugins/grpc/parser/parse_test.go b/plugins/grpc/parser/parse_test.go
new file mode 100644
index 00000000..b71c133d
--- /dev/null
+++ b/plugins/grpc/parser/parse_test.go
@@ -0,0 +1,71 @@
+package parser
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParseFile(t *testing.T) {
+ services, err := File("test.proto", "")
+ assert.NoError(t, err)
+ assert.Len(t, services, 2)
+
+ assert.Equal(t, "app.namespace", services[0].Package)
+}
+
+func TestParseFileWithImportsNestedFolder(t *testing.T) {
+ services, err := File("./test_nested/test_import.proto", "./test_nested")
+ assert.NoError(t, err)
+ assert.Len(t, services, 2)
+
+ assert.Equal(t, "app.namespace", services[0].Package)
+}
+
+func TestParseFileWithImports(t *testing.T) {
+ services, err := File("test_import.proto", ".")
+ assert.NoError(t, err)
+ assert.Len(t, services, 2)
+
+ assert.Equal(t, "app.namespace", services[0].Package)
+}
+
+func TestParseNotFound(t *testing.T) {
+ _, err := File("test2.proto", "")
+ assert.Error(t, err)
+}
+
+func TestParseBytes(t *testing.T) {
+ services, err := Bytes([]byte{})
+ assert.NoError(t, err)
+ assert.Len(t, services, 0)
+}
+
+func TestParseString(t *testing.T) {
+ services, err := Bytes([]byte(`
+syntax = "proto3";
+package app.namespace;
+
+// Ping Service.
+service PingService {
+ // Ping Method.
+ rpc Ping (Message) returns (Message) {
+ }
+}
+
+// Pong service.
+service PongService {
+ rpc Pong (stream Message) returns (stream Message) {
+ }
+}
+
+message Message {
+ string msg = 1;
+ int64 value = 2;
+}
+`))
+ assert.NoError(t, err)
+ assert.Len(t, services, 2)
+
+ assert.Equal(t, "app.namespace", services[0].Package)
+}
diff --git a/plugins/grpc/parser/pong.proto b/plugins/grpc/parser/pong.proto
new file mode 100644
index 00000000..9756fabe
--- /dev/null
+++ b/plugins/grpc/parser/pong.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+package app.namespace;
+
+import "message.proto";
+
+// Pong service.
+service PongService {
+ rpc Pong (stream Message) returns (stream Message) {
+ }
+} \ No newline at end of file
diff --git a/plugins/grpc/parser/test.proto b/plugins/grpc/parser/test.proto
new file mode 100644
index 00000000..e2230954
--- /dev/null
+++ b/plugins/grpc/parser/test.proto
@@ -0,0 +1,20 @@
+syntax = "proto3";
+package app.namespace;
+
+// Ping Service.
+service PingService {
+ // Ping Method.
+ rpc Ping (Message) returns (Message) {
+ }
+}
+
+// Pong service.
+service PongService {
+ rpc Pong (stream Message) returns (stream Message) {
+ }
+}
+
+message Message {
+ string msg = 1;
+ int64 value = 2;
+} \ No newline at end of file
diff --git a/plugins/grpc/parser/test_import.proto b/plugins/grpc/parser/test_import.proto
new file mode 100644
index 00000000..1b954fc1
--- /dev/null
+++ b/plugins/grpc/parser/test_import.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+package app.namespace;
+
+import "message.proto";
+import "pong.proto";
+
+// Ping Service.
+service PingService {
+ // Ping Method.
+ rpc Ping (Message) returns (Message) {
+ }
+} \ No newline at end of file
diff --git a/plugins/grpc/parser/test_nested/message.proto b/plugins/grpc/parser/test_nested/message.proto
new file mode 100644
index 00000000..a4012010
--- /dev/null
+++ b/plugins/grpc/parser/test_nested/message.proto
@@ -0,0 +1,7 @@
+syntax = "proto3";
+package app.namespace;
+
+message Message {
+ string msg = 1;
+ int64 value = 2;
+} \ No newline at end of file
diff --git a/plugins/grpc/parser/test_nested/pong.proto b/plugins/grpc/parser/test_nested/pong.proto
new file mode 100644
index 00000000..9756fabe
--- /dev/null
+++ b/plugins/grpc/parser/test_nested/pong.proto
@@ -0,0 +1,10 @@
+syntax = "proto3";
+package app.namespace;
+
+import "message.proto";
+
+// Pong service.
+service PongService {
+ rpc Pong (stream Message) returns (stream Message) {
+ }
+} \ No newline at end of file
diff --git a/plugins/grpc/parser/test_nested/test_import.proto b/plugins/grpc/parser/test_nested/test_import.proto
new file mode 100644
index 00000000..a3a476ba
--- /dev/null
+++ b/plugins/grpc/parser/test_nested/test_import.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+package app.namespace;
+
+import "message.proto";
+import "pong.proto";
+
+// Ping Service.
+service PingService {
+ // Ping Method.
+ rpc Ping (Message) returns (Message) {
+ }
+}