23
23
//
24
24
// With [buf], your buf.gen.yaml will look like this:
25
25
//
26
- // version: v1
26
+ // version: v2
27
27
// plugins:
28
- // - name: go
28
+ // - local: protoc-gen- go
29
29
// out: gen
30
- // - name: connect-go
30
+ // - local: protoc-gen- connect-go
31
31
// out: gen
32
32
//
33
33
// This generates service definitions for the Protobuf types and services
34
34
// defined by file.proto. If file.proto defines the foov1 Protobuf package, the
35
35
// invocations above will write output to:
36
36
//
37
37
// gen/path/to/file.pb.go
38
- // gen/path/to/connectfoov1/file.connect.go
38
+ // gen/path/to/foov1connect/file.connect.go
39
+ //
40
+ // The generated code is configurable with the same parameters as the protoc-gen-go
41
+ // plugin, with the following additional parameters:
42
+ //
43
+ // - package_suffix: To generate into a sub-package of the package containing the
44
+ // base .pb.go files using the given suffix. An empty suffix denotes to
45
+ // generate into the same package as the base pb.go files. Default is "connect".
46
+ //
47
+ // For example, to generate into the same package as the base .pb.go files:
48
+ //
49
+ // version: v2
50
+ // plugins:
51
+ // - local: protoc-gen-go
52
+ // out: gen
53
+ // - local: protoc-gen-connect-go
54
+ // out: gen
55
+ // opts: package_suffix
56
+ //
57
+ // This will generate output to:
58
+ //
59
+ // gen/path/to/file.pb.go
60
+ // gen/path/to/file.connect.go
39
61
//
40
62
// [buf]: https://buf.build
41
63
package main
42
64
43
65
import (
44
66
"bytes"
67
+ "flag"
45
68
"fmt"
69
+ "go/token"
46
70
"os"
47
71
"path"
48
72
"path/filepath"
@@ -64,7 +88,8 @@ const (
64
88
connectPackage = protogen .GoImportPath ("connectrpc.com/connect" )
65
89
66
90
generatedFilenameExtension = ".connect.go"
67
- generatedPackageSuffix = "connect"
91
+ defaultPackageSuffix = "connect"
92
+ packageSuffixFlagName = "package_suffix"
68
93
69
94
usage = "See https://connectrpc.com/docs/go/getting-started to learn how to use this plugin.\n \n Flags:\n -h, --help\t Print this help and exit.\n --version\t Print the version and exit."
70
95
@@ -89,46 +114,63 @@ func main() {
89
114
fmt .Fprintln (os .Stderr , usage )
90
115
os .Exit (1 )
91
116
}
92
- protogen.Options {}.Run (
117
+ var flagSet flag.FlagSet
118
+ packageSuffix := flagSet .String (
119
+ packageSuffixFlagName ,
120
+ defaultPackageSuffix ,
121
+ "Generate files into a sub-package of the package containing the base .pb.go files using the given suffix. An empty suffix denotes to generate into the same package as the base pb.go files." ,
122
+ )
123
+ protogen.Options {
124
+ ParamFunc : flagSet .Set ,
125
+ }.Run (
93
126
func (plugin * protogen.Plugin ) error {
94
127
plugin .SupportedFeatures = uint64 (pluginpb .CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL ) | uint64 (pluginpb .CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS )
95
128
plugin .SupportedEditionsMinimum = descriptorpb .Edition_EDITION_PROTO2
96
129
plugin .SupportedEditionsMaximum = descriptorpb .Edition_EDITION_2023
97
130
for _ , file := range plugin .Files {
98
131
if file .Generate {
99
- generate (plugin , file )
132
+ generate (plugin , file , * packageSuffix )
100
133
}
101
134
}
102
135
return nil
103
136
},
104
137
)
105
138
}
106
139
107
- func generate (plugin * protogen.Plugin , file * protogen.File ) {
140
+ func generate (plugin * protogen.Plugin , file * protogen.File , packageSuffix string ) {
108
141
if len (file .Services ) == 0 {
109
142
return
110
143
}
111
- file .GoPackageName += generatedPackageSuffix
112
144
113
- generatedFilenamePrefixToSlash := filepath .ToSlash (file .GeneratedFilenamePrefix )
114
- file .GeneratedFilenamePrefix = path .Join (
115
- path .Dir (generatedFilenamePrefixToSlash ),
116
- string (file .GoPackageName ),
117
- path .Base (generatedFilenamePrefixToSlash ),
118
- )
119
- generatedFile := plugin .NewGeneratedFile (
120
- file .GeneratedFilenamePrefix + generatedFilenameExtension ,
121
- protogen .GoImportPath (path .Join (
145
+ goImportPath := file .GoImportPath
146
+ if packageSuffix != "" {
147
+ if ! token .IsIdentifier (packageSuffix ) {
148
+ plugin .Error (fmt .Errorf ("package_suffix %q is not a valid Go identifier" , packageSuffix ))
149
+ return
150
+ }
151
+ file .GoPackageName += protogen .GoPackageName (packageSuffix )
152
+ generatedFilenamePrefixToSlash := filepath .ToSlash (file .GeneratedFilenamePrefix )
153
+ file .GeneratedFilenamePrefix = path .Join (
154
+ path .Dir (generatedFilenamePrefixToSlash ),
155
+ string (file .GoPackageName ),
156
+ path .Base (generatedFilenamePrefixToSlash ),
157
+ )
158
+ goImportPath = protogen .GoImportPath (path .Join (
122
159
string (file .GoImportPath ),
123
160
string (file .GoPackageName ),
124
- )),
161
+ ))
162
+ }
163
+ generatedFile := plugin .NewGeneratedFile (
164
+ file .GeneratedFilenamePrefix + generatedFilenameExtension ,
165
+ goImportPath ,
125
166
)
126
- generatedFile .Import (file .GoImportPath )
167
+ if packageSuffix != "" {
168
+ generatedFile .Import (file .GoImportPath )
169
+ }
127
170
generatePreamble (generatedFile , file )
128
171
generateServiceNameConstants (generatedFile , file .Services )
129
- generateServiceNameVariables (generatedFile , file )
130
172
for _ , service := range file .Services {
131
- generateService (generatedFile , service )
173
+ generateService (generatedFile , file , service )
132
174
}
133
175
}
134
176
@@ -213,29 +255,22 @@ func generateServiceNameConstants(g *protogen.GeneratedFile, services []*protoge
213
255
g .P ()
214
256
}
215
257
216
- func generateServiceNameVariables (g * protogen.GeneratedFile , file * protogen.File ) {
217
- wrapComments (g , "These variables are the protoreflect.Descriptor objects for the RPCs defined in this package." )
218
- g .P ("var (" )
219
- for _ , service := range file .Services {
220
- serviceDescName := unexport (fmt .Sprintf ("%sServiceDescriptor" , service .Desc .Name ()))
221
- g .P (serviceDescName , ` = ` ,
222
- g .QualifiedGoIdent (file .GoDescriptorIdent ),
223
- `.Services().ByName("` , service .Desc .Name (), `")` )
224
- for _ , method := range service .Methods {
225
- g .P (procedureVarMethodDescriptor (method ), ` = ` ,
226
- serviceDescName ,
227
- `.Methods().ByName("` , method .Desc .Name (), `")` )
228
- }
258
+ func generateServiceMethodsVar (g * protogen.GeneratedFile , file * protogen.File , service * protogen.Service ) {
259
+ if len (service .Methods ) == 0 {
260
+ return
229
261
}
230
- g .P (")" )
262
+ serviceMethodsName := unexport (fmt .Sprintf ("%sMethods" , service .Desc .Name ()))
263
+ g .P (serviceMethodsName , ` := ` ,
264
+ g .QualifiedGoIdent (file .GoDescriptorIdent ),
265
+ `.Services().ByName("` , service .Desc .Name (), `").Methods()` )
231
266
}
232
267
233
- func generateService (g * protogen.GeneratedFile , service * protogen.Service ) {
268
+ func generateService (g * protogen.GeneratedFile , file * protogen. File , service * protogen.Service ) {
234
269
names := newNames (service )
235
270
generateClientInterface (g , service , names )
236
- generateClientImplementation (g , service , names )
271
+ generateClientImplementation (g , file , service , names )
237
272
generateServerInterface (g , service , names )
238
- generateServerConstructor (g , service , names )
273
+ generateServerConstructor (g , file , service , names )
239
274
generateUnimplementedServerImplementation (g , service , names )
240
275
}
241
276
@@ -260,7 +295,7 @@ func generateClientInterface(g *protogen.GeneratedFile, service *protogen.Servic
260
295
g .P ()
261
296
}
262
297
263
- func generateClientImplementation (g * protogen.GeneratedFile , service * protogen.Service , names names ) {
298
+ func generateClientImplementation (g * protogen.GeneratedFile , file * protogen. File , service * protogen.Service , names names ) {
264
299
clientOption := connectPackage .Ident ("ClientOption" )
265
300
266
301
// Client constructor.
@@ -281,6 +316,7 @@ func generateClientImplementation(g *protogen.GeneratedFile, service *protogen.S
281
316
if len (service .Methods ) > 0 {
282
317
g .P ("baseURL = " , stringsPackage .Ident ("TrimRight" ), `(baseURL, "/")` )
283
318
}
319
+ generateServiceMethodsVar (g , file , service )
284
320
g .P ("return &" , names .ClientImpl , "{" )
285
321
for _ , method := range service .Methods {
286
322
g .P (unexport (method .GoName ), ": " ,
@@ -396,7 +432,7 @@ func generateServerInterface(g *protogen.GeneratedFile, service *protogen.Servic
396
432
g .P ()
397
433
}
398
434
399
- func generateServerConstructor (g * protogen.GeneratedFile , service * protogen.Service , names names ) {
435
+ func generateServerConstructor (g * protogen.GeneratedFile , file * protogen. File , service * protogen.Service , names names ) {
400
436
wrapComments (g , names .ServerConstructor , " builds an HTTP handler from the service implementation." ,
401
437
" It returns the path on which to mount the handler and the handler itself." )
402
438
g .P ("//" )
@@ -409,6 +445,7 @@ func generateServerConstructor(g *protogen.GeneratedFile, service *protogen.Serv
409
445
handlerOption := connectPackage .Ident ("HandlerOption" )
410
446
g .P ("func " , names .ServerConstructor , "(svc " , names .Server , ", opts ..." , handlerOption ,
411
447
") (string, " , httpPackage .Ident ("Handler" ), ") {" )
448
+ generateServiceMethodsVar (g , file , service )
412
449
for _ , method := range service .Methods {
413
450
isStreamingServer := method .Desc .IsStreamingServer ()
414
451
isStreamingClient := method .Desc .IsStreamingClient ()
@@ -522,7 +559,8 @@ func procedureHandlerName(m *protogen.Method) string {
522
559
}
523
560
524
561
func procedureVarMethodDescriptor (m * protogen.Method ) string {
525
- return unexport (fmt .Sprintf ("%s%sMethodDescriptor" , m .Parent .GoName , m .GoName ))
562
+ serviceMethodsName := unexport (fmt .Sprintf ("%sMethods" , m .Parent .GoName ))
563
+ return serviceMethodsName + `.ByName("` + string (m .Desc .Name ()) + `")`
526
564
}
527
565
528
566
func isDeprecatedService (service * protogen.Service ) bool {
0 commit comments