StructuredSwift+Client.swift 25 KB

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