Skip to content

Commit 522e5d4

Browse files
authored
Generate documentation from proto comments (#743)
Motivation: We have access to the underlying comments associated with RPCs in the protobuf definition. These often explain what each RPC does and can have great value to the user. Modifications: - Generate documentation for client stubs and server provider protocol based on the source comments in the proto file. - Re-generate code. Result: - The generate code has more helpful documentation
1 parent c4dc6a3 commit 522e5d4

File tree

7 files changed

+135
-29
lines changed

7 files changed

+135
-29
lines changed

Sources/Examples/Echo/Model/echo.grpc.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public final class Echo_EchoClient: GRPCClient, Echo_EchoClientProtocol {
4949
self.defaultCallOptions = defaultCallOptions
5050
}
5151

52-
/// Asynchronous unary call to Get.
52+
/// Immediately returns an echo of a request.
5353
///
5454
/// - Parameters:
5555
/// - request: Request to send to Get.
@@ -61,7 +61,7 @@ public final class Echo_EchoClient: GRPCClient, Echo_EchoClientProtocol {
6161
callOptions: callOptions ?? self.defaultCallOptions)
6262
}
6363

64-
/// Asynchronous server-streaming call to Expand.
64+
/// Splits a request into words and returns each word in a stream of messages.
6565
///
6666
/// - Parameters:
6767
/// - request: Request to send to Expand.
@@ -75,7 +75,7 @@ public final class Echo_EchoClient: GRPCClient, Echo_EchoClientProtocol {
7575
handler: handler)
7676
}
7777

78-
/// Asynchronous client-streaming call to Collect.
78+
/// Collects a stream of messages and returns them concatenated when the caller closes.
7979
///
8080
/// Callers should use the `send` method on the returned object to send messages
8181
/// to the server. The caller should send an `.end` after the final message has been sent.
@@ -88,7 +88,7 @@ public final class Echo_EchoClient: GRPCClient, Echo_EchoClientProtocol {
8888
callOptions: callOptions ?? self.defaultCallOptions)
8989
}
9090

91-
/// Asynchronous bidirectional-streaming call to Update.
91+
/// Streams back messages as they are received in an input stream.
9292
///
9393
/// Callers should use the `send` method on the returned object to send messages
9494
/// to the server. The caller should send an `.end` after the final message has been sent.
@@ -107,9 +107,13 @@ public final class Echo_EchoClient: GRPCClient, Echo_EchoClientProtocol {
107107

108108
/// To build a server, implement a class that conforms to this protocol.
109109
public protocol Echo_EchoProvider: CallHandlerProvider {
110+
/// Immediately returns an echo of a request.
110111
func get(request: Echo_EchoRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Echo_EchoResponse>
112+
/// Splits a request into words and returns each word in a stream of messages.
111113
func expand(request: Echo_EchoRequest, context: StreamingResponseCallContext<Echo_EchoResponse>) -> EventLoopFuture<GRPCStatus>
114+
/// Collects a stream of messages and returns them concatenated when the caller closes.
112115
func collect(context: UnaryResponseCallContext<Echo_EchoResponse>) -> EventLoopFuture<(StreamEvent<Echo_EchoRequest>) -> Void>
116+
/// Streams back messages as they are received in an input stream.
113117
func update(context: StreamingResponseCallContext<Echo_EchoResponse>) -> EventLoopFuture<(StreamEvent<Echo_EchoRequest>) -> Void>
114118
}
115119

Sources/Examples/HelloWorld/Model/helloworld.grpc.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public final class Helloworld_GreeterClient: GRPCClient, Helloworld_GreeterClien
4646
self.defaultCallOptions = defaultCallOptions
4747
}
4848

49-
/// Asynchronous unary call to SayHello.
49+
/// Sends a greeting.
5050
///
5151
/// - Parameters:
5252
/// - request: Request to send to SayHello.
@@ -62,6 +62,7 @@ public final class Helloworld_GreeterClient: GRPCClient, Helloworld_GreeterClien
6262

6363
/// To build a server, implement a class that conforms to this protocol.
6464
public protocol Helloworld_GreeterProvider: CallHandlerProvider {
65+
/// Sends a greeting.
6566
func sayHello(request: Helloworld_HelloRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Helloworld_HelloReply>
6667
}
6768

Sources/Examples/RouteGuide/Model/route_guide.grpc.swift

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ public final class Routeguide_RouteGuideClient: GRPCClient, Routeguide_RouteGuid
4949
self.defaultCallOptions = defaultCallOptions
5050
}
5151

52-
/// Asynchronous unary call to GetFeature.
52+
/// A simple RPC.
53+
///
54+
/// Obtains the feature at a given position.
55+
///
56+
/// A feature with an empty name is returned if there's no feature at the given
57+
/// position.
5358
///
5459
/// - Parameters:
5560
/// - request: Request to send to GetFeature.
@@ -61,7 +66,12 @@ public final class Routeguide_RouteGuideClient: GRPCClient, Routeguide_RouteGuid
6166
callOptions: callOptions ?? self.defaultCallOptions)
6267
}
6368

64-
/// Asynchronous server-streaming call to ListFeatures.
69+
/// A server-to-client streaming RPC.
70+
///
71+
/// Obtains the Features available within the given Rectangle. Results are
72+
/// streamed rather than returned at once (e.g. in a response message with a
73+
/// repeated field), as the rectangle may cover a large area and contain a
74+
/// huge number of features.
6575
///
6676
/// - Parameters:
6777
/// - request: Request to send to ListFeatures.
@@ -75,7 +85,10 @@ public final class Routeguide_RouteGuideClient: GRPCClient, Routeguide_RouteGuid
7585
handler: handler)
7686
}
7787

78-
/// Asynchronous client-streaming call to RecordRoute.
88+
/// A client-to-server streaming RPC.
89+
///
90+
/// Accepts a stream of Points on a route being traversed, returning a
91+
/// RouteSummary when traversal is completed.
7992
///
8093
/// Callers should use the `send` method on the returned object to send messages
8194
/// to the server. The caller should send an `.end` after the final message has been sent.
@@ -88,7 +101,10 @@ public final class Routeguide_RouteGuideClient: GRPCClient, Routeguide_RouteGuid
88101
callOptions: callOptions ?? self.defaultCallOptions)
89102
}
90103

91-
/// Asynchronous bidirectional-streaming call to RouteChat.
104+
/// A Bidirectional streaming RPC.
105+
///
106+
/// Accepts a stream of RouteNotes sent while a route is being traversed,
107+
/// while receiving other RouteNotes (e.g. from other users).
92108
///
93109
/// Callers should use the `send` method on the returned object to send messages
94110
/// to the server. The caller should send an `.end` after the final message has been sent.
@@ -107,9 +123,29 @@ public final class Routeguide_RouteGuideClient: GRPCClient, Routeguide_RouteGuid
107123

108124
/// To build a server, implement a class that conforms to this protocol.
109125
public protocol Routeguide_RouteGuideProvider: CallHandlerProvider {
126+
/// A simple RPC.
127+
///
128+
/// Obtains the feature at a given position.
129+
///
130+
/// A feature with an empty name is returned if there's no feature at the given
131+
/// position.
110132
func getFeature(request: Routeguide_Point, context: StatusOnlyCallContext) -> EventLoopFuture<Routeguide_Feature>
133+
/// A server-to-client streaming RPC.
134+
///
135+
/// Obtains the Features available within the given Rectangle. Results are
136+
/// streamed rather than returned at once (e.g. in a response message with a
137+
/// repeated field), as the rectangle may cover a large area and contain a
138+
/// huge number of features.
111139
func listFeatures(request: Routeguide_Rectangle, context: StreamingResponseCallContext<Routeguide_Feature>) -> EventLoopFuture<GRPCStatus>
140+
/// A client-to-server streaming RPC.
141+
///
142+
/// Accepts a stream of Points on a route being traversed, returning a
143+
/// RouteSummary when traversal is completed.
112144
func recordRoute(context: UnaryResponseCallContext<Routeguide_RouteSummary>) -> EventLoopFuture<(StreamEvent<Routeguide_Point>) -> Void>
145+
/// A Bidirectional streaming RPC.
146+
///
147+
/// Accepts a stream of RouteNotes sent while a route is being traversed,
148+
/// while receiving other RouteNotes (e.g. from other users).
113149
func routeChat(context: StreamingResponseCallContext<Routeguide_RouteNote>) -> EventLoopFuture<(StreamEvent<Routeguide_RouteNote>) -> Void>
114150
}
115151

Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
5353
self.defaultCallOptions = defaultCallOptions
5454
}
5555

56-
/// Asynchronous unary call to EmptyCall.
56+
/// One empty request followed by one empty response.
5757
///
5858
/// - Parameters:
5959
/// - request: Request to send to EmptyCall.
@@ -65,7 +65,7 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
6565
callOptions: callOptions ?? self.defaultCallOptions)
6666
}
6767

68-
/// Asynchronous unary call to UnaryCall.
68+
/// One request followed by one response.
6969
///
7070
/// - Parameters:
7171
/// - request: Request to send to UnaryCall.
@@ -77,7 +77,9 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
7777
callOptions: callOptions ?? self.defaultCallOptions)
7878
}
7979

80-
/// Asynchronous unary call to CacheableUnaryCall.
80+
/// One request followed by one response. Response has cache control
81+
/// headers set such that a caching HTTP proxy (such as GFE) can
82+
/// satisfy subsequent requests.
8183
///
8284
/// - Parameters:
8385
/// - request: Request to send to CacheableUnaryCall.
@@ -89,7 +91,8 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
8991
callOptions: callOptions ?? self.defaultCallOptions)
9092
}
9193

92-
/// Asynchronous server-streaming call to StreamingOutputCall.
94+
/// One request followed by a sequence of responses (streamed download).
95+
/// The server returns the payload with client desired type and sizes.
9396
///
9497
/// - Parameters:
9598
/// - request: Request to send to StreamingOutputCall.
@@ -103,7 +106,8 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
103106
handler: handler)
104107
}
105108

106-
/// Asynchronous client-streaming call to StreamingInputCall.
109+
/// A sequence of requests followed by one response (streamed upload).
110+
/// The server returns the aggregated size of client payload as the result.
107111
///
108112
/// Callers should use the `send` method on the returned object to send messages
109113
/// to the server. The caller should send an `.end` after the final message has been sent.
@@ -116,7 +120,9 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
116120
callOptions: callOptions ?? self.defaultCallOptions)
117121
}
118122

119-
/// Asynchronous bidirectional-streaming call to FullDuplexCall.
123+
/// A sequence of requests with each request served by the server immediately.
124+
/// As one request could lead to multiple responses, this interface
125+
/// demonstrates the idea of full duplexing.
120126
///
121127
/// Callers should use the `send` method on the returned object to send messages
122128
/// to the server. The caller should send an `.end` after the final message has been sent.
@@ -131,7 +137,10 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
131137
handler: handler)
132138
}
133139

134-
/// Asynchronous bidirectional-streaming call to HalfDuplexCall.
140+
/// A sequence of requests followed by a sequence of responses.
141+
/// The server buffers all the client requests and then serves them in order. A
142+
/// stream of responses are returned to the client when the server starts with
143+
/// first request.
135144
///
136145
/// Callers should use the `send` method on the returned object to send messages
137146
/// to the server. The caller should send an `.end` after the final message has been sent.
@@ -146,7 +155,8 @@ public final class Grpc_Testing_TestServiceClient: GRPCClient, Grpc_Testing_Test
146155
handler: handler)
147156
}
148157

149-
/// Asynchronous unary call to UnimplementedCall.
158+
/// The test server will not implement this method. It will be used
159+
/// to test the behavior when clients call unimplemented methods.
150160
///
151161
/// - Parameters:
152162
/// - request: Request to send to UnimplementedCall.
@@ -179,7 +189,7 @@ public final class Grpc_Testing_UnimplementedServiceClient: GRPCClient, Grpc_Tes
179189
self.defaultCallOptions = defaultCallOptions
180190
}
181191

182-
/// Asynchronous unary call to UnimplementedCall.
192+
/// A call that no server should implement
183193
///
184194
/// - Parameters:
185195
/// - request: Request to send to UnimplementedCall.
@@ -213,7 +223,7 @@ public final class Grpc_Testing_ReconnectServiceClient: GRPCClient, Grpc_Testing
213223
self.defaultCallOptions = defaultCallOptions
214224
}
215225

216-
/// Asynchronous unary call to Start.
226+
/// Unary call to Start
217227
///
218228
/// - Parameters:
219229
/// - request: Request to send to Start.
@@ -225,7 +235,7 @@ public final class Grpc_Testing_ReconnectServiceClient: GRPCClient, Grpc_Testing
225235
callOptions: callOptions ?? self.defaultCallOptions)
226236
}
227237

228-
/// Asynchronous unary call to Stop.
238+
/// Unary call to Stop
229239
///
230240
/// - Parameters:
231241
/// - request: Request to send to Stop.
@@ -241,12 +251,28 @@ public final class Grpc_Testing_ReconnectServiceClient: GRPCClient, Grpc_Testing
241251

242252
/// To build a server, implement a class that conforms to this protocol.
243253
public protocol Grpc_Testing_TestServiceProvider: CallHandlerProvider {
254+
/// One empty request followed by one empty response.
244255
func emptyCall(request: Grpc_Testing_Empty, context: StatusOnlyCallContext) -> EventLoopFuture<Grpc_Testing_Empty>
256+
/// One request followed by one response.
245257
func unaryCall(request: Grpc_Testing_SimpleRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Grpc_Testing_SimpleResponse>
258+
/// One request followed by one response. Response has cache control
259+
/// headers set such that a caching HTTP proxy (such as GFE) can
260+
/// satisfy subsequent requests.
246261
func cacheableUnaryCall(request: Grpc_Testing_SimpleRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Grpc_Testing_SimpleResponse>
262+
/// One request followed by a sequence of responses (streamed download).
263+
/// The server returns the payload with client desired type and sizes.
247264
func streamingOutputCall(request: Grpc_Testing_StreamingOutputCallRequest, context: StreamingResponseCallContext<Grpc_Testing_StreamingOutputCallResponse>) -> EventLoopFuture<GRPCStatus>
265+
/// A sequence of requests followed by one response (streamed upload).
266+
/// The server returns the aggregated size of client payload as the result.
248267
func streamingInputCall(context: UnaryResponseCallContext<Grpc_Testing_StreamingInputCallResponse>) -> EventLoopFuture<(StreamEvent<Grpc_Testing_StreamingInputCallRequest>) -> Void>
268+
/// A sequence of requests with each request served by the server immediately.
269+
/// As one request could lead to multiple responses, this interface
270+
/// demonstrates the idea of full duplexing.
249271
func fullDuplexCall(context: StreamingResponseCallContext<Grpc_Testing_StreamingOutputCallResponse>) -> EventLoopFuture<(StreamEvent<Grpc_Testing_StreamingOutputCallRequest>) -> Void>
272+
/// A sequence of requests followed by a sequence of responses.
273+
/// The server buffers all the client requests and then serves them in order. A
274+
/// stream of responses are returned to the client when the server starts with
275+
/// first request.
250276
func halfDuplexCall(context: StreamingResponseCallContext<Grpc_Testing_StreamingOutputCallResponse>) -> EventLoopFuture<(StreamEvent<Grpc_Testing_StreamingOutputCallRequest>) -> Void>
251277
}
252278

@@ -307,6 +333,7 @@ extension Grpc_Testing_TestServiceProvider {
307333

308334
/// To build a server, implement a class that conforms to this protocol.
309335
public protocol Grpc_Testing_UnimplementedServiceProvider: CallHandlerProvider {
336+
/// A call that no server should implement
310337
func unimplementedCall(request: Grpc_Testing_Empty, context: StatusOnlyCallContext) -> EventLoopFuture<Grpc_Testing_Empty>
311338
}
312339

@@ -362,7 +389,7 @@ extension Grpc_Testing_ReconnectServiceProvider {
362389
}
363390

364391

365-
/// Provides conformance to `GRPCPayload` for the request and response messages
392+
// Provides conformance to `GRPCPayload` for request and response messages
366393
extension Grpc_Testing_Empty: GRPCProtobufPayload {}
367394
extension Grpc_Testing_SimpleRequest: GRPCProtobufPayload {}
368395
extension Grpc_Testing_SimpleResponse: GRPCProtobufPayload {}

Sources/protoc-gen-grpc-swift/Generator-Client.swift

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ extension Generator {
7070

7171
for method in service.methods {
7272
self.method = method
73-
switch streamingType(method) {
73+
let streamType = streamingType(self.method)
74+
switch streamType {
7475
case .unary:
75-
println("/// Asynchronous unary call to \(method.name).")
76+
println(self.method.documentation(streamingType: streamType), newline: false)
7677
println("///")
7778
printParameters()
7879
printRequestParameter()
@@ -87,7 +88,7 @@ extension Generator {
8788
println("}")
8889

8990
case .serverStreaming:
90-
println("/// Asynchronous server-streaming call to \(method.name).")
91+
println(self.method.documentation(streamingType: streamType), newline: false)
9192
println("///")
9293
printParameters()
9394
printRequestParameter()
@@ -104,7 +105,7 @@ extension Generator {
104105
println("}")
105106

106107
case .clientStreaming:
107-
println("/// Asynchronous client-streaming call to \(method.name).")
108+
println(self.method.documentation(streamingType: streamType), newline: false)
108109
println("///")
109110
printClientStreamingDetails()
110111
println("///")
@@ -119,7 +120,7 @@ extension Generator {
119120
println("}")
120121

121122
case .bidirectionalStreaming:
122-
println("/// Asynchronous bidirectional-streaming call to \(method.name).")
123+
println(self.method.documentation(streamingType: streamType), newline: false)
123124
println("///")
124125
printClientStreamingDetails()
125126
println("///")
@@ -162,3 +163,35 @@ extension Generator {
162163
println("/// - handler: A closure called when each response is received from the server.")
163164
}
164165
}
166+
167+
fileprivate extension StreamingType {
168+
var name: String {
169+
switch self {
170+
case .unary:
171+
return "Unary"
172+
case .clientStreaming:
173+
return "Client streaming"
174+
case .serverStreaming:
175+
return "Server streaming"
176+
case .bidirectionalStreaming:
177+
return "Bidirectional streaming"
178+
}
179+
}
180+
}
181+
182+
extension MethodDescriptor {
183+
var documentation: String? {
184+
let comments = self.protoSourceComments(commentPrefix: "")
185+
return comments.isEmpty ? nil : comments
186+
}
187+
188+
fileprivate func documentation(streamingType: StreamingType) -> String {
189+
let sourceComments = self.protoSourceComments()
190+
191+
if sourceComments.isEmpty {
192+
return "/// \(streamingType.name) call to \(self.name)\n" // comments end with "\n" already.
193+
} else {
194+
return sourceComments // already prefixed with "///"
195+
}
196+
}
197+
}

Sources/protoc-gen-grpc-swift/Generator-Server.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,16 @@ extension Generator {
3131

3232
switch streamingType(method) {
3333
case .unary:
34+
println(self.method.protoSourceComments(), newline: false)
3435
println("func \(methodFunctionName)(request: \(methodInputName), context: StatusOnlyCallContext) -> EventLoopFuture<\(methodOutputName)>")
3536
case .serverStreaming:
37+
println(self.method.protoSourceComments(), newline: false)
3638
println("func \(methodFunctionName)(request: \(methodInputName), context: StreamingResponseCallContext<\(methodOutputName)>) -> EventLoopFuture<GRPCStatus>")
3739
case .clientStreaming:
40+
println(self.method.protoSourceComments(), newline: false)
3841
println("func \(methodFunctionName)(context: UnaryResponseCallContext<\(methodOutputName)>) -> EventLoopFuture<(StreamEvent<\(methodInputName)>) -> Void>")
3942
case .bidirectionalStreaming:
43+
println(self.method.protoSourceComments(), newline: false)
4044
println("func \(methodFunctionName)(context: StreamingResponseCallContext<\(methodOutputName)>) -> EventLoopFuture<(StreamEvent<\(methodInputName)>) -> Void>")
4145
}
4246
}

0 commit comments

Comments
 (0)