forked from goadesign/goa
/
proto.go
119 lines (101 loc) · 2.97 KB
/
proto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package codegen
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"goa.design/goa/codegen"
"goa.design/goa/expr"
"goa.design/goa/pkg"
)
const (
// ProtoVersion is the protocol buffer version used to generate .proto files
ProtoVersion = "proto3"
)
// ProtoFiles returns a *.proto file for each gRPC service.
func ProtoFiles(genpkg string, root *expr.RootExpr) []*codegen.File {
fw := make([]*codegen.File, len(root.API.GRPC.Services))
for i, svc := range root.API.GRPC.Services {
fw[i] = protoFile(genpkg, svc)
}
return fw
}
func protoFile(genpkg string, svc *expr.GRPCServiceExpr) *codegen.File {
data := GRPCServices.Get(svc.Name())
svcName := codegen.SnakeCase(data.Service.VarName)
path := filepath.Join(codegen.Gendir, "grpc", svcName, pbPkgName, svcName+".proto")
sections := []*codegen.SectionTemplate{
// header comments
&codegen.SectionTemplate{
Name: "proto-header",
Source: protoHeaderT,
Data: map[string]interface{}{
"Title": fmt.Sprintf("%s protocol buffer definition", svc.Name()),
"ToolVersion": pkg.Version(),
},
},
// proto syntax and package
&codegen.SectionTemplate{
Name: "proto-start",
Source: protoStartT,
Data: map[string]interface{}{
"ProtoVersion": ProtoVersion,
"Pkg": codegen.SnakeCase(codegen.Goify(svcName, false)),
},
},
// service definition
&codegen.SectionTemplate{Name: "grpc-service", Source: serviceT, Data: data},
}
// message definition
for _, m := range data.Messages {
sections = append(sections, &codegen.SectionTemplate{Name: "grpc-message", Source: messageT, Data: m})
}
return &codegen.File{
Path: path,
SectionTemplates: sections,
FinalizeFunc: protoc,
}
}
func protoc(path string) error {
dir := filepath.Dir(path)
os.MkdirAll(dir, 0777)
args := []string{"--go_out=plugins=grpc:.", path, "--proto_path", dir}
cmd := exec.Command("protoc", args...)
cmd.Dir = filepath.Dir(path)
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to run protoc: %s: %s", err, output)
}
return nil
}
const (
protoHeaderT = `{{ if .Title -}}
// Code generated with goa {{ .ToolVersion }}, DO NOT EDIT.
//
// {{ .Title }}
//
// Command:
{{ comment commandLine }}
{{- end }}
`
protoStartT = `
syntax = {{ printf "%q" .ProtoVersion }};
package {{ .Pkg }};
option go_package = "{{ .Pkg }}pb";
`
// input: ServiceData
serviceT = `
{{ .Description | comment }}
service {{ .Name }} {
{{- range .Endpoints }}
{{ if .Method.Description }}{{ .Method.Description | comment }}{{ end }}
{{- $serverStream := or (eq .Method.StreamKind 3) (eq .Method.StreamKind 4) }}
{{- $clientStream := or (eq .Method.StreamKind 2) (eq .Method.StreamKind 4) }}
rpc {{ .Method.VarName }} ({{ if $clientStream }}stream {{ end }}{{ .Request.Message.VarName }}) returns ({{ if $serverStream }}stream {{ end }}{{ .Response.Message.VarName }});
{{- end }}
}
`
// input: service.UserTypeData
messageT = `{{ comment .Description }}
message {{ .VarName }}{{ .Def }}
`
)