StructuredSwift+Client.swift 25 KB

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