StructuredSwift+Client.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. /*
  2. * Copyright 2024, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. extension ClosureInvocationDescription {
  17. /// ```
  18. /// { response in
  19. /// try response.message
  20. /// }
  21. /// ```
  22. static var defaultClientUnaryResponseHandler: Self {
  23. ClosureInvocationDescription(
  24. argumentNames: ["response"],
  25. body: [.expression(.try(.identifierPattern("response").dot("message")))]
  26. )
  27. }
  28. }
  29. extension FunctionSignatureDescription {
  30. /// ```
  31. /// func <Name><Result>(
  32. /// request: GRPCCore.ClientRequest<Input>,
  33. /// serializer: some GRPCCore.MessageSerializer<Input>,
  34. /// deserializer: some GRPCCore.MessageDeserializer<Output>,
  35. /// options: GRPCCore.CallOptions,
  36. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Input>) async throws -> Result
  37. /// ) async throws -> Result where Result: Sendable
  38. /// ```
  39. static func clientMethod(
  40. accessLevel: AccessModifier? = nil,
  41. name: String,
  42. input: String,
  43. output: String,
  44. streamingInput: Bool,
  45. streamingOutput: Bool,
  46. includeDefaults: Bool,
  47. includeSerializers: Bool
  48. ) -> Self {
  49. var signature = FunctionSignatureDescription(
  50. accessModifier: accessLevel,
  51. kind: .function(name: name, isStatic: false),
  52. generics: [.member("Result")],
  53. parameters: [], // Populated below.
  54. keywords: [.async, .throws],
  55. returnType: .identifierPattern("Result"),
  56. whereClause: WhereClause(requirements: [.conformance("Result", "Sendable")])
  57. )
  58. signature.parameters.append(
  59. ParameterDescription(
  60. label: "request",
  61. type: .clientRequest(forType: input, streaming: streamingInput)
  62. )
  63. )
  64. if includeSerializers {
  65. signature.parameters.append(
  66. ParameterDescription(
  67. label: "serializer",
  68. // Type is optional, so be explicit about which 'some' to use
  69. type: ExistingTypeDescription.some(.serializer(forType: input))
  70. )
  71. )
  72. signature.parameters.append(
  73. ParameterDescription(
  74. label: "deserializer",
  75. // Type is optional, so be explicit about which 'some' to use
  76. type: ExistingTypeDescription.some(.deserializer(forType: output))
  77. )
  78. )
  79. }
  80. signature.parameters.append(
  81. ParameterDescription(
  82. label: "options",
  83. type: .callOptions,
  84. defaultValue: includeDefaults ? .memberAccess(.dot("defaults")) : nil
  85. )
  86. )
  87. signature.parameters.append(
  88. ParameterDescription(
  89. label: "onResponse",
  90. name: "handleResponse",
  91. type: .closure(
  92. ClosureSignatureDescription(
  93. parameters: [
  94. ParameterDescription(
  95. type: .clientResponse(forType: output, streaming: streamingOutput)
  96. )
  97. ],
  98. keywords: [.async, .throws],
  99. returnType: .identifierPattern("Result"),
  100. sendable: true,
  101. escaping: true
  102. )
  103. ),
  104. defaultValue: includeDefaults && !streamingOutput
  105. ? .closureInvocation(.defaultClientUnaryResponseHandler)
  106. : nil
  107. )
  108. )
  109. return signature
  110. }
  111. }
  112. extension FunctionDescription {
  113. /// ```
  114. /// func <Name><Result>(
  115. /// request: GRPCCore.ClientRequest<Input>,
  116. /// options: GRPCCore.CallOptions = .defaults,
  117. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Input>) async throws -> Result
  118. /// ) async throws -> Result where Result: Sendable {
  119. /// try await self.<Name>(
  120. /// request: request,
  121. /// serializer: <Serializer>,
  122. /// deserializer: <Deserializer>,
  123. /// options: options
  124. /// onResponse: handleResponse,
  125. /// )
  126. /// }
  127. /// ```
  128. static func clientMethodWithDefaults(
  129. accessLevel: AccessModifier? = nil,
  130. name: String,
  131. input: String,
  132. output: String,
  133. streamingInput: Bool,
  134. streamingOutput: Bool,
  135. serializer: Expression,
  136. deserializer: Expression
  137. ) -> Self {
  138. FunctionDescription(
  139. signature: .clientMethod(
  140. accessLevel: accessLevel,
  141. name: name,
  142. input: input,
  143. output: output,
  144. streamingInput: streamingInput,
  145. streamingOutput: streamingOutput,
  146. includeDefaults: true,
  147. includeSerializers: false
  148. ),
  149. body: [
  150. .expression(
  151. .try(
  152. .await(
  153. .functionCall(
  154. calledExpression: .identifierPattern("self").dot(name),
  155. arguments: [
  156. FunctionArgumentDescription(
  157. label: "request",
  158. expression: .identifierPattern("request")
  159. ),
  160. FunctionArgumentDescription(
  161. label: "serializer",
  162. expression: serializer
  163. ),
  164. FunctionArgumentDescription(
  165. label: "deserializer",
  166. expression: deserializer
  167. ),
  168. FunctionArgumentDescription(
  169. label: "options",
  170. expression: .identifierPattern("options")
  171. ),
  172. FunctionArgumentDescription(
  173. label: "onResponse",
  174. expression: .identifierPattern("handleResponse")
  175. ),
  176. ]
  177. )
  178. )
  179. )
  180. )
  181. ]
  182. )
  183. }
  184. }
  185. extension ProtocolDescription {
  186. /// ```
  187. /// protocol <Name>: Sendable {
  188. /// func foo<Result: Sendable>(
  189. /// ...
  190. /// ) async throws -> Result
  191. /// }
  192. /// ```
  193. static func clientProtocol(
  194. accessLevel: AccessModifier? = nil,
  195. name: String,
  196. methods: [MethodDescriptor]
  197. ) -> Self {
  198. ProtocolDescription(
  199. accessModifier: accessLevel,
  200. name: name,
  201. conformances: ["Sendable"],
  202. members: methods.map { method in
  203. .commentable(
  204. .preFormatted(docs(for: method)),
  205. .function(
  206. signature: .clientMethod(
  207. name: method.name.functionName,
  208. input: method.inputType,
  209. output: method.outputType,
  210. streamingInput: method.isInputStreaming,
  211. streamingOutput: method.isOutputStreaming,
  212. includeDefaults: false,
  213. includeSerializers: true
  214. )
  215. )
  216. )
  217. }
  218. )
  219. }
  220. }
  221. extension ExtensionDescription {
  222. /// ```
  223. /// extension <Name> {
  224. /// func foo<Result: Sendable>(
  225. /// request: GRPCCore.ClientRequest<Input>,
  226. /// options: GRPCCore.CallOptions = .defaults,
  227. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Input>) async throws -> Result
  228. /// ) async throws -> Result where Result: Sendable {
  229. /// // ...
  230. /// }
  231. /// // ...
  232. /// }
  233. /// ```
  234. static func clientMethodSignatureWithDefaults(
  235. accessLevel: AccessModifier? = nil,
  236. name: String,
  237. methods: [MethodDescriptor],
  238. serializer: (String) -> String,
  239. deserializer: (String) -> String
  240. ) -> Self {
  241. ExtensionDescription(
  242. onType: name,
  243. declarations: methods.map { method in
  244. .commentable(
  245. .preFormatted(docs(for: method, serializers: false)),
  246. .function(
  247. .clientMethodWithDefaults(
  248. accessLevel: accessLevel,
  249. name: method.name.functionName,
  250. input: method.inputType,
  251. output: method.outputType,
  252. streamingInput: method.isInputStreaming,
  253. streamingOutput: method.isOutputStreaming,
  254. serializer: .identifierPattern(serializer(method.inputType)),
  255. deserializer: .identifierPattern(deserializer(method.outputType))
  256. )
  257. )
  258. )
  259. }
  260. )
  261. }
  262. }
  263. extension FunctionSignatureDescription {
  264. /// ```
  265. /// func foo<Result>(
  266. /// _ message: <Input>,
  267. /// metadata: GRPCCore.Metadata = [:],
  268. /// options: GRPCCore.CallOptions = .defaults,
  269. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  270. /// try response.message
  271. /// }
  272. /// ) async throws -> Result where Result: Sendable
  273. /// ```
  274. static func clientMethodExploded(
  275. accessLevel: AccessModifier? = nil,
  276. name: String,
  277. input: String,
  278. output: String,
  279. streamingInput: Bool,
  280. streamingOutput: Bool
  281. ) -> Self {
  282. var signature = FunctionSignatureDescription(
  283. accessModifier: accessLevel,
  284. kind: .function(name: name),
  285. generics: [.member("Result")],
  286. parameters: [], // Populated below
  287. keywords: [.async, .throws],
  288. returnType: .identifierPattern("Result"),
  289. whereClause: WhereClause(requirements: [.conformance("Result", "Sendable")])
  290. )
  291. if !streamingInput {
  292. signature.parameters.append(
  293. ParameterDescription(label: "_", name: "message", type: .member(input))
  294. )
  295. }
  296. // metadata: GRPCCore.Metadata = [:]
  297. signature.parameters.append(
  298. ParameterDescription(
  299. label: "metadata",
  300. type: .metadata,
  301. defaultValue: .literal(.dictionary([]))
  302. )
  303. )
  304. // options: GRPCCore.CallOptions = .defaults
  305. signature.parameters.append(
  306. ParameterDescription(
  307. label: "options",
  308. type: .callOptions,
  309. defaultValue: .dot("defaults")
  310. )
  311. )
  312. if streamingInput {
  313. signature.parameters.append(
  314. ParameterDescription(
  315. label: "requestProducer",
  316. name: "producer",
  317. type: .closure(
  318. ClosureSignatureDescription(
  319. parameters: [ParameterDescription(type: .rpcWriter(forType: input))],
  320. keywords: [.async, .throws],
  321. returnType: .identifierPattern("Void"),
  322. sendable: true,
  323. escaping: true
  324. )
  325. )
  326. )
  327. )
  328. }
  329. signature.parameters.append(
  330. ParameterDescription(
  331. label: "onResponse",
  332. name: "handleResponse",
  333. type: .closure(
  334. ClosureSignatureDescription(
  335. parameters: [
  336. ParameterDescription(
  337. type: .clientResponse(forType: output, streaming: streamingOutput)
  338. )
  339. ],
  340. keywords: [.async, .throws],
  341. returnType: .identifierPattern("Result"),
  342. sendable: true,
  343. escaping: true
  344. )
  345. ),
  346. defaultValue: streamingOutput ? nil : .closureInvocation(.defaultClientUnaryResponseHandler)
  347. )
  348. )
  349. return signature
  350. }
  351. }
  352. extension [CodeBlock] {
  353. /// ```
  354. /// let request = GRPCCore.StreamingClientRequest<Input>(
  355. /// metadata: metadata,
  356. /// producer: producer
  357. /// )
  358. /// return try await self.foo(
  359. /// request: request,
  360. /// options: options,
  361. /// onResponse: handleResponse
  362. /// )
  363. /// ```
  364. static func clientMethodExploded(
  365. name: String,
  366. input: String,
  367. streamingInput: Bool
  368. ) -> Self {
  369. func arguments(streaming: Bool) -> [FunctionArgumentDescription] {
  370. let metadata = FunctionArgumentDescription(
  371. label: "metadata",
  372. expression: .identifierPattern("metadata")
  373. )
  374. if streaming {
  375. return [
  376. metadata,
  377. FunctionArgumentDescription(
  378. label: "producer",
  379. expression: .identifierPattern("producer")
  380. ),
  381. ]
  382. } else {
  383. return [
  384. FunctionArgumentDescription(label: "message", expression: .identifierPattern("message")),
  385. metadata,
  386. ]
  387. }
  388. }
  389. return [
  390. CodeBlock(
  391. item: .declaration(
  392. .variable(
  393. kind: .let,
  394. left: .identifierPattern("request"),
  395. right: .functionCall(
  396. calledExpression: .identifierType(
  397. .clientRequest(forType: input, streaming: streamingInput)
  398. ),
  399. arguments: arguments(streaming: streamingInput)
  400. )
  401. )
  402. )
  403. ),
  404. CodeBlock(
  405. item: .expression(
  406. .return(
  407. .try(
  408. .await(
  409. .functionCall(
  410. calledExpression: .identifierPattern("self").dot(name),
  411. arguments: [
  412. FunctionArgumentDescription(
  413. label: "request",
  414. expression: .identifierPattern("request")
  415. ),
  416. FunctionArgumentDescription(
  417. label: "options",
  418. expression: .identifierPattern("options")
  419. ),
  420. FunctionArgumentDescription(
  421. label: "onResponse",
  422. expression: .identifierPattern("handleResponse")
  423. ),
  424. ]
  425. )
  426. )
  427. )
  428. )
  429. )
  430. ),
  431. ]
  432. }
  433. }
  434. extension FunctionDescription {
  435. /// ```
  436. /// func foo<Result>(
  437. /// _ message: <Input>,
  438. /// metadata: GRPCCore.Metadata = [:],
  439. /// options: GRPCCore.CallOptions = .defaults,
  440. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  441. /// try response.message
  442. /// }
  443. /// ) async throws -> Result where Result: Sendable {
  444. /// // ...
  445. /// }
  446. /// ```
  447. static func clientMethodExploded(
  448. accessLevel: AccessModifier? = nil,
  449. name: String,
  450. input: String,
  451. output: String,
  452. streamingInput: Bool,
  453. streamingOutput: Bool
  454. ) -> Self {
  455. FunctionDescription(
  456. signature: .clientMethodExploded(
  457. accessLevel: accessLevel,
  458. name: name,
  459. input: input,
  460. output: output,
  461. streamingInput: streamingInput,
  462. streamingOutput: streamingOutput
  463. ),
  464. body: .clientMethodExploded(name: name, input: input, streamingInput: streamingInput)
  465. )
  466. }
  467. }
  468. extension ExtensionDescription {
  469. /// ```
  470. /// extension <Name> {
  471. /// // (exploded client methods)
  472. /// }
  473. /// ```
  474. static func explodedClientMethods(
  475. accessLevel: AccessModifier? = nil,
  476. on extensionName: String,
  477. methods: [MethodDescriptor]
  478. ) -> ExtensionDescription {
  479. return ExtensionDescription(
  480. onType: extensionName,
  481. declarations: methods.map { method in
  482. .commentable(
  483. .preFormatted(explodedDocs(for: method)),
  484. .function(
  485. .clientMethodExploded(
  486. accessLevel: accessLevel,
  487. name: method.name.functionName,
  488. input: method.inputType,
  489. output: method.outputType,
  490. streamingInput: method.isInputStreaming,
  491. streamingOutput: method.isOutputStreaming
  492. )
  493. )
  494. )
  495. }
  496. )
  497. }
  498. }
  499. extension FunctionDescription {
  500. /// ```
  501. /// func <Name><Result>(
  502. /// request: GRPCCore.ClientRequest<Input>,
  503. /// serializer: some GRPCCore.MessageSerializer<Input>,
  504. /// deserializer: some GRPCCore.MessageDeserializer<Output>,
  505. /// options: GRPCCore.CallOptions = .default,
  506. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result
  507. /// ) async throws -> Result where Result: Sendable {
  508. /// try await self.<Name>(...)
  509. /// }
  510. /// ```
  511. static func clientMethod(
  512. accessLevel: AccessModifier,
  513. name: String,
  514. input: String,
  515. output: String,
  516. serviceEnum: String,
  517. methodEnum: String,
  518. streamingInput: Bool,
  519. streamingOutput: Bool
  520. ) -> Self {
  521. let underlyingMethod: String
  522. switch (streamingInput, streamingOutput) {
  523. case (false, false):
  524. underlyingMethod = "unary"
  525. case (true, false):
  526. underlyingMethod = "clientStreaming"
  527. case (false, true):
  528. underlyingMethod = "serverStreaming"
  529. case (true, true):
  530. underlyingMethod = "bidirectionalStreaming"
  531. }
  532. return FunctionDescription(
  533. accessModifier: accessLevel,
  534. kind: .function(name: name),
  535. generics: [.member("Result")],
  536. parameters: [
  537. ParameterDescription(
  538. label: "request",
  539. type: .clientRequest(forType: input, streaming: streamingInput)
  540. ),
  541. ParameterDescription(
  542. label: "serializer",
  543. // Be explicit: 'type' is optional and '.some' resolves to Optional.some by default.
  544. type: ExistingTypeDescription.some(.serializer(forType: input))
  545. ),
  546. ParameterDescription(
  547. label: "deserializer",
  548. // Be explicit: 'type' is optional and '.some' resolves to Optional.some by default.
  549. type: ExistingTypeDescription.some(.deserializer(forType: output))
  550. ),
  551. ParameterDescription(
  552. label: "options",
  553. type: .callOptions,
  554. defaultValue: .dot("defaults")
  555. ),
  556. ParameterDescription(
  557. label: "onResponse",
  558. name: "handleResponse",
  559. type: .closure(
  560. ClosureSignatureDescription(
  561. parameters: [
  562. ParameterDescription(
  563. type: .clientResponse(forType: output, streaming: streamingOutput)
  564. )
  565. ],
  566. keywords: [.async, .throws],
  567. returnType: .identifierPattern("Result"),
  568. sendable: true,
  569. escaping: true
  570. )
  571. ),
  572. defaultValue: streamingOutput
  573. ? nil
  574. : .closureInvocation(.defaultClientUnaryResponseHandler)
  575. ),
  576. ],
  577. keywords: [.async, .throws],
  578. returnType: .identifierPattern("Result"),
  579. whereClause: WhereClause(requirements: [.conformance("Result", "Sendable")]),
  580. body: [
  581. .try(
  582. .await(
  583. .functionCall(
  584. calledExpression: .identifierPattern("self").dot("client").dot(underlyingMethod),
  585. arguments: [
  586. FunctionArgumentDescription(
  587. label: "request",
  588. expression: .identifierPattern("request")
  589. ),
  590. FunctionArgumentDescription(
  591. label: "descriptor",
  592. expression: .identifierPattern(serviceEnum)
  593. .dot("Method")
  594. .dot(methodEnum)
  595. .dot("descriptor")
  596. ),
  597. FunctionArgumentDescription(
  598. label: "serializer",
  599. expression: .identifierPattern("serializer")
  600. ),
  601. FunctionArgumentDescription(
  602. label: "deserializer",
  603. expression: .identifierPattern("deserializer")
  604. ),
  605. FunctionArgumentDescription(
  606. label: "options",
  607. expression: .identifierPattern("options")
  608. ),
  609. FunctionArgumentDescription(
  610. label: "onResponse",
  611. expression: .identifierPattern("handleResponse")
  612. ),
  613. ]
  614. )
  615. )
  616. )
  617. ]
  618. )
  619. }
  620. }
  621. extension StructDescription {
  622. /// ```
  623. /// struct <Name><Transport>: <ClientProtocol> where Transport: GRPCCore.ClientTransport {
  624. /// private let client: GRPCCore.GRPCClient<Transport>
  625. ///
  626. /// init(wrapping client: GRPCCore.GRPCClient<Transport>) {
  627. /// self.client = client
  628. /// }
  629. ///
  630. /// // ...
  631. /// }
  632. /// ```
  633. static func client(
  634. accessLevel: AccessModifier,
  635. name: String,
  636. serviceEnum: String,
  637. clientProtocol: String,
  638. methods: [MethodDescriptor]
  639. ) -> Self {
  640. StructDescription(
  641. accessModifier: accessLevel,
  642. name: name,
  643. generics: [.member("Transport")],
  644. conformances: [clientProtocol],
  645. whereClause: WhereClause(
  646. requirements: [.conformance("Transport", "GRPCCore.ClientTransport")]
  647. ),
  648. members: [
  649. .variable(
  650. accessModifier: .private,
  651. kind: .let,
  652. left: "client",
  653. type: .grpcClient(genericOver: "Transport")
  654. ),
  655. .commentable(
  656. .preFormatted(
  657. """
  658. /// Creates a new client wrapping the provided `GRPCCore.GRPCClient`.
  659. ///
  660. /// - Parameters:
  661. /// - client: A `GRPCCore.GRPCClient` providing a communication channel to the service.
  662. """
  663. ),
  664. .function(
  665. accessModifier: accessLevel,
  666. kind: .initializer,
  667. parameters: [
  668. ParameterDescription(
  669. label: "wrapping",
  670. name: "client",
  671. type: .grpcClient(
  672. genericOver: "Transport"
  673. )
  674. )
  675. ],
  676. whereClause: nil,
  677. body: [
  678. .expression(
  679. .assignment(
  680. left: .identifierPattern("self").dot("client"),
  681. right: .identifierPattern("client")
  682. )
  683. )
  684. ]
  685. )
  686. ),
  687. ]
  688. + methods.map { method in
  689. .commentable(
  690. .preFormatted(docs(for: method)),
  691. .function(
  692. .clientMethod(
  693. accessLevel: accessLevel,
  694. name: method.name.functionName,
  695. input: method.inputType,
  696. output: method.outputType,
  697. serviceEnum: serviceEnum,
  698. methodEnum: method.name.typeName,
  699. streamingInput: method.isInputStreaming,
  700. streamingOutput: method.isOutputStreaming
  701. )
  702. )
  703. )
  704. }
  705. )
  706. }
  707. }
  708. private func docs(
  709. for method: MethodDescriptor,
  710. serializers includeSerializers: Bool = true
  711. ) -> String {
  712. let summary = "/// Call the \"\(method.name.identifyingName)\" method."
  713. let request: String
  714. if method.isInputStreaming {
  715. request = "A streaming request producing `\(method.inputType)` messages."
  716. } else {
  717. request = "A request containing a single `\(method.inputType)` message."
  718. }
  719. let parameters = """
  720. /// - Parameters:
  721. /// - request: \(request)
  722. """
  723. let serializers = """
  724. /// - serializer: A serializer for `\(method.inputType)` messages.
  725. /// - deserializer: A deserializer for `\(method.outputType)` messages.
  726. """
  727. let otherParameters = """
  728. /// - options: Options to apply to this RPC.
  729. /// - handleResponse: A closure which handles the response, the result of which is
  730. /// returned to the caller. Returning from the closure will cancel the RPC if it
  731. /// hasn't already finished.
  732. /// - Returns: The result of `handleResponse`.
  733. """
  734. let allParameters: String
  735. if includeSerializers {
  736. allParameters = parameters + "\n" + serializers + "\n" + otherParameters
  737. } else {
  738. allParameters = parameters + "\n" + otherParameters
  739. }
  740. return Docs.interposeDocs(method.documentation, between: summary, and: allParameters)
  741. }
  742. private func explodedDocs(for method: MethodDescriptor) -> String {
  743. let summary = "/// Call the \"\(method.name.identifyingName)\" method."
  744. var parameters = """
  745. /// - Parameters:
  746. """
  747. if !method.isInputStreaming {
  748. parameters += "\n"
  749. parameters += """
  750. /// - message: request message to send.
  751. """
  752. }
  753. parameters += "\n"
  754. parameters += """
  755. /// - metadata: Additional metadata to send, defaults to empty.
  756. /// - options: Options to apply to this RPC, defaults to `.defaults`.
  757. """
  758. if method.isInputStreaming {
  759. parameters += "\n"
  760. parameters += """
  761. /// - producer: A closure producing request messages to send to the server. The request
  762. /// stream is closed when the closure returns.
  763. """
  764. }
  765. parameters += "\n"
  766. parameters += """
  767. /// - handleResponse: A closure which handles the response, the result of which is
  768. /// returned to the caller. Returning from the closure will cancel the RPC if it
  769. /// hasn't already finished.
  770. /// - Returns: The result of `handleResponse`.
  771. """
  772. return Docs.interposeDocs(method.documentation, between: summary, and: parameters)
  773. }