StructuredSwift+ClientTests.swift 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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. import Testing
  17. @testable import GRPCCodeGen
  18. extension StructuredSwiftTests {
  19. @Suite("Client")
  20. struct Client {
  21. @Test(
  22. "func <Method>(request:serializer:deserializer:options:onResponse:)",
  23. arguments: AccessModifier.allCases,
  24. RPCKind.allCases
  25. )
  26. func clientMethodSignature(access: AccessModifier, kind: RPCKind) {
  27. let decl: FunctionSignatureDescription = .clientMethod(
  28. accessLevel: access,
  29. name: "foo",
  30. input: "FooInput",
  31. output: "FooOutput",
  32. streamingInput: kind.streamsInput,
  33. streamingOutput: kind.streamsOutput,
  34. includeDefaults: false,
  35. includeSerializers: true
  36. )
  37. let requestType = kind.streamsInput ? "StreamingClientRequest" : "ClientRequest"
  38. let responseType = kind.streamsOutput ? "StreamingClientResponse" : "ClientResponse"
  39. let expected = """
  40. \(access) func foo<Result>(
  41. request: GRPCCore.\(requestType)<FooInput>,
  42. serializer: some GRPCCore.MessageSerializer<FooInput>,
  43. deserializer: some GRPCCore.MessageDeserializer<FooOutput>,
  44. options: GRPCCore.CallOptions,
  45. onResponse handleResponse: @Sendable @escaping (GRPCCore.\(responseType)<FooOutput>) async throws -> Result
  46. ) async throws -> Result where Result: Sendable
  47. """
  48. #expect(render(.function(signature: decl)) == expected)
  49. }
  50. @Test(
  51. "func <Method>(request:serializer:deserializer:options:onResponse:) (with defaults)",
  52. arguments: AccessModifier.allCases,
  53. [true, false]
  54. )
  55. func clientMethodSignatureWithDefaults(access: AccessModifier, streamsOutput: Bool) {
  56. let decl: FunctionSignatureDescription = .clientMethod(
  57. accessLevel: access,
  58. name: "foo",
  59. input: "FooInput",
  60. output: "FooOutput",
  61. streamingInput: false,
  62. streamingOutput: streamsOutput,
  63. includeDefaults: true,
  64. includeSerializers: false
  65. )
  66. let expected: String
  67. if streamsOutput {
  68. expected = """
  69. \(access) func foo<Result>(
  70. request: GRPCCore.ClientRequest<FooInput>,
  71. options: GRPCCore.CallOptions = .defaults,
  72. onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse<FooOutput>) async throws -> Result
  73. ) async throws -> Result where Result: Sendable
  74. """
  75. } else {
  76. expected = """
  77. \(access) func foo<Result>(
  78. request: GRPCCore.ClientRequest<FooInput>,
  79. options: GRPCCore.CallOptions = .defaults,
  80. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<FooOutput>) async throws -> Result = { response in
  81. try response.message
  82. }
  83. ) async throws -> Result where Result: Sendable
  84. """
  85. }
  86. #expect(render(.function(signature: decl)) == expected)
  87. }
  88. @Test("protocol Foo_ClientProtocol: Sendable { ... }", arguments: AccessModifier.allCases)
  89. func clientProtocol(access: AccessModifier) {
  90. let decl: ProtocolDescription = .clientProtocol(
  91. accessLevel: access,
  92. name: "Foo_ClientProtocol",
  93. methods: [
  94. .init(
  95. documentation: "/// Some docs",
  96. name: .init(base: "Bar", generatedUpperCase: "Bar", generatedLowerCase: "bar"),
  97. isInputStreaming: false,
  98. isOutputStreaming: false,
  99. inputType: "BarInput",
  100. outputType: "BarOutput"
  101. )
  102. ]
  103. )
  104. let expected = """
  105. \(access) protocol Foo_ClientProtocol: Sendable {
  106. /// Call the "Bar" method.
  107. ///
  108. /// > Source IDL Documentation:
  109. /// >
  110. /// > Some docs
  111. ///
  112. /// - Parameters:
  113. /// - request: A request containing a single `BarInput` message.
  114. /// - serializer: A serializer for `BarInput` messages.
  115. /// - deserializer: A deserializer for `BarOutput` messages.
  116. /// - options: Options to apply to this RPC.
  117. /// - handleResponse: A closure which handles the response, the result of which is
  118. /// returned to the caller. Returning from the closure will cancel the RPC if it
  119. /// hasn't already finished.
  120. /// - Returns: The result of `handleResponse`.
  121. func bar<Result>(
  122. request: GRPCCore.ClientRequest<BarInput>,
  123. serializer: some GRPCCore.MessageSerializer<BarInput>,
  124. deserializer: some GRPCCore.MessageDeserializer<BarOutput>,
  125. options: GRPCCore.CallOptions,
  126. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<BarOutput>) async throws -> Result
  127. ) async throws -> Result where Result: Sendable
  128. }
  129. """
  130. #expect(render(.protocol(decl)) == expected)
  131. }
  132. @Test("func foo(...) { try await self.foo(...) }", arguments: AccessModifier.allCases)
  133. func clientMethodFunctionWithDefaults(access: AccessModifier) {
  134. let decl: FunctionDescription = .clientMethodWithDefaults(
  135. accessLevel: access,
  136. name: "foo",
  137. input: "FooInput",
  138. output: "FooOutput",
  139. streamingInput: false,
  140. streamingOutput: false,
  141. serializer: .identifierPattern("Serialize<FooInput>()"),
  142. deserializer: .identifierPattern("Deserialize<FooOutput>()")
  143. )
  144. let expected = """
  145. \(access) func foo<Result>(
  146. request: GRPCCore.ClientRequest<FooInput>,
  147. options: GRPCCore.CallOptions = .defaults,
  148. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<FooOutput>) async throws -> Result = { response in
  149. try response.message
  150. }
  151. ) async throws -> Result where Result: Sendable {
  152. try await self.foo(
  153. request: request,
  154. serializer: Serialize<FooInput>(),
  155. deserializer: Deserialize<FooOutput>(),
  156. options: options,
  157. onResponse: handleResponse
  158. )
  159. }
  160. """
  161. #expect(render(.function(decl)) == expected)
  162. }
  163. @Test(
  164. "extension Foo_ClientProtocol { ... } (methods with defaults)",
  165. arguments: AccessModifier.allCases
  166. )
  167. func extensionWithDefaultClientMethods(access: AccessModifier) {
  168. let decl: ExtensionDescription = .clientMethodSignatureWithDefaults(
  169. accessLevel: access,
  170. name: "Foo_ClientProtocol",
  171. methods: [
  172. MethodDescriptor(
  173. documentation: "",
  174. name: .init(base: "Bar", generatedUpperCase: "Bar", generatedLowerCase: "bar"),
  175. isInputStreaming: false,
  176. isOutputStreaming: false,
  177. inputType: "BarInput",
  178. outputType: "BarOutput"
  179. )
  180. ],
  181. serializer: { "Serialize<\($0)>()" },
  182. deserializer: { "Deserialize<\($0)>()" }
  183. )
  184. let expected = """
  185. extension Foo_ClientProtocol {
  186. /// Call the "Bar" method.
  187. ///
  188. /// - Parameters:
  189. /// - request: A request containing a single `BarInput` message.
  190. /// - options: Options to apply to this RPC.
  191. /// - handleResponse: A closure which handles the response, the result of which is
  192. /// returned to the caller. Returning from the closure will cancel the RPC if it
  193. /// hasn't already finished.
  194. /// - Returns: The result of `handleResponse`.
  195. \(access) func bar<Result>(
  196. request: GRPCCore.ClientRequest<BarInput>,
  197. options: GRPCCore.CallOptions = .defaults,
  198. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<BarOutput>) async throws -> Result = { response in
  199. try response.message
  200. }
  201. ) async throws -> Result where Result: Sendable {
  202. try await self.bar(
  203. request: request,
  204. serializer: Serialize<BarInput>(),
  205. deserializer: Deserialize<BarOutput>(),
  206. options: options,
  207. onResponse: handleResponse
  208. )
  209. }
  210. }
  211. """
  212. #expect(render(.extension(decl)) == expected)
  213. }
  214. @Test(
  215. "func foo<Result>(_:metadata:options:onResponse:) -> Result (exploded signature)",
  216. arguments: AccessModifier.allCases,
  217. RPCKind.allCases
  218. )
  219. func explodedClientMethodSignature(access: AccessModifier, kind: RPCKind) {
  220. let decl: FunctionSignatureDescription = .clientMethodExploded(
  221. accessLevel: access,
  222. name: "foo",
  223. input: "Input",
  224. output: "Output",
  225. streamingInput: kind.streamsInput,
  226. streamingOutput: kind.streamsOutput
  227. )
  228. let expected: String
  229. switch kind {
  230. case .unary:
  231. expected = """
  232. \(access) func foo<Result>(
  233. _ message: Input,
  234. metadata: GRPCCore.Metadata = [:],
  235. options: GRPCCore.CallOptions = .defaults,
  236. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  237. try response.message
  238. }
  239. ) async throws -> Result where Result: Sendable
  240. """
  241. case .clientStreaming:
  242. expected = """
  243. \(access) func foo<Result>(
  244. metadata: GRPCCore.Metadata = [:],
  245. options: GRPCCore.CallOptions = .defaults,
  246. requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter<Input>) async throws -> Void,
  247. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  248. try response.message
  249. }
  250. ) async throws -> Result where Result: Sendable
  251. """
  252. case .serverStreaming:
  253. expected = """
  254. \(access) func foo<Result>(
  255. _ message: Input,
  256. metadata: GRPCCore.Metadata = [:],
  257. options: GRPCCore.CallOptions = .defaults,
  258. onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse<Output>) async throws -> Result
  259. ) async throws -> Result where Result: Sendable
  260. """
  261. case .bidirectionalStreaming:
  262. expected = """
  263. \(access) func foo<Result>(
  264. metadata: GRPCCore.Metadata = [:],
  265. options: GRPCCore.CallOptions = .defaults,
  266. requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter<Input>) async throws -> Void,
  267. onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse<Output>) async throws -> Result
  268. ) async throws -> Result where Result: Sendable
  269. """
  270. }
  271. #expect(render(.function(signature: decl)) == expected)
  272. }
  273. @Test(
  274. "func foo<Result>(_:metadata:options:onResponse:) -> Result (exploded body)",
  275. arguments: [true, false]
  276. )
  277. func explodedClientMethodBody(streamingInput: Bool) {
  278. let blocks: [CodeBlock] = .clientMethodExploded(
  279. name: "foo",
  280. input: "Input",
  281. streamingInput: streamingInput
  282. )
  283. let expected: String
  284. if streamingInput {
  285. expected = """
  286. let request = GRPCCore.StreamingClientRequest<Input>(
  287. metadata: metadata,
  288. producer: producer
  289. )
  290. return try await self.foo(
  291. request: request,
  292. options: options,
  293. onResponse: handleResponse
  294. )
  295. """
  296. } else {
  297. expected = """
  298. let request = GRPCCore.ClientRequest<Input>(
  299. message: message,
  300. metadata: metadata
  301. )
  302. return try await self.foo(
  303. request: request,
  304. options: options,
  305. onResponse: handleResponse
  306. )
  307. """
  308. }
  309. #expect(render(blocks) == expected)
  310. }
  311. @Test("extension Foo_ClientProtocol { ... } (exploded)", arguments: AccessModifier.allCases)
  312. func explodedClientMethodExtension(access: AccessModifier) {
  313. let decl: ExtensionDescription = .explodedClientMethods(
  314. accessLevel: access,
  315. on: "Foo_ClientProtocol",
  316. methods: [
  317. .init(
  318. documentation: "/// Some docs",
  319. name: .init(base: "Bar", generatedUpperCase: "Bar", generatedLowerCase: "bar"),
  320. isInputStreaming: false,
  321. isOutputStreaming: true,
  322. inputType: "Input",
  323. outputType: "Output"
  324. )
  325. ]
  326. )
  327. let expected = """
  328. extension Foo_ClientProtocol {
  329. /// Call the "Bar" method.
  330. ///
  331. /// > Source IDL Documentation:
  332. /// >
  333. /// > Some docs
  334. ///
  335. /// - Parameters:
  336. /// - message: request message to send.
  337. /// - metadata: Additional metadata to send, defaults to empty.
  338. /// - options: Options to apply to this RPC, defaults to `.defaults`.
  339. /// - handleResponse: A closure which handles the response, the result of which is
  340. /// returned to the caller. Returning from the closure will cancel the RPC if it
  341. /// hasn't already finished.
  342. /// - Returns: The result of `handleResponse`.
  343. \(access) func bar<Result>(
  344. _ message: Input,
  345. metadata: GRPCCore.Metadata = [:],
  346. options: GRPCCore.CallOptions = .defaults,
  347. onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse<Output>) async throws -> Result
  348. ) async throws -> Result where Result: Sendable {
  349. let request = GRPCCore.ClientRequest<Input>(
  350. message: message,
  351. metadata: metadata
  352. )
  353. return try await self.bar(
  354. request: request,
  355. options: options,
  356. onResponse: handleResponse
  357. )
  358. }
  359. }
  360. """
  361. #expect(render(.extension(decl)) == expected)
  362. }
  363. @Test(
  364. "func foo(request:serializer:deserializer:options:onResponse:) (client method impl.)",
  365. arguments: AccessModifier.allCases
  366. )
  367. func clientMethodImplementation(access: AccessModifier) {
  368. let decl: FunctionDescription = .clientMethod(
  369. accessLevel: access,
  370. name: "foo",
  371. input: "Input",
  372. output: "Output",
  373. serviceEnum: "BarService",
  374. methodEnum: "Foo",
  375. streamingInput: false,
  376. streamingOutput: false
  377. )
  378. let expected = """
  379. \(access) func foo<Result>(
  380. request: GRPCCore.ClientRequest<Input>,
  381. serializer: some GRPCCore.MessageSerializer<Input>,
  382. deserializer: some GRPCCore.MessageDeserializer<Output>,
  383. options: GRPCCore.CallOptions = .defaults,
  384. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  385. try response.message
  386. }
  387. ) async throws -> Result where Result: Sendable {
  388. try await self.client.unary(
  389. request: request,
  390. descriptor: BarService.Method.Foo.descriptor,
  391. serializer: serializer,
  392. deserializer: deserializer,
  393. options: options,
  394. onResponse: handleResponse
  395. )
  396. }
  397. """
  398. #expect(render(.function(decl)) == expected)
  399. }
  400. @Test("struct FooClient: Foo_ClientProtocol { ... }", arguments: AccessModifier.allCases)
  401. func client(access: AccessModifier) {
  402. let decl: StructDescription = .client(
  403. accessLevel: access,
  404. name: "FooClient",
  405. serviceEnum: "BarService",
  406. clientProtocol: "Foo_ClientProtocol",
  407. methods: [
  408. .init(
  409. documentation: "/// Unary docs",
  410. name: .init(
  411. base: "Unary",
  412. generatedUpperCase: "Unary",
  413. generatedLowerCase: "unary"
  414. ),
  415. isInputStreaming: false,
  416. isOutputStreaming: false,
  417. inputType: "Input",
  418. outputType: "Output"
  419. ),
  420. .init(
  421. documentation: "/// ClientStreaming docs",
  422. name: .init(
  423. base: "ClientStreaming",
  424. generatedUpperCase: "ClientStreaming",
  425. generatedLowerCase: "clientStreaming"
  426. ),
  427. isInputStreaming: true,
  428. isOutputStreaming: false,
  429. inputType: "Input",
  430. outputType: "Output"
  431. ),
  432. .init(
  433. documentation: "/// ServerStreaming docs",
  434. name: .init(
  435. base: "ServerStreaming",
  436. generatedUpperCase: "ServerStreaming",
  437. generatedLowerCase: "serverStreaming"
  438. ),
  439. isInputStreaming: false,
  440. isOutputStreaming: true,
  441. inputType: "Input",
  442. outputType: "Output"
  443. ),
  444. .init(
  445. documentation: "/// BidiStreaming docs",
  446. name: .init(
  447. base: "BidiStreaming",
  448. generatedUpperCase: "BidiStreaming",
  449. generatedLowerCase: "bidiStreaming"
  450. ),
  451. isInputStreaming: true,
  452. isOutputStreaming: true,
  453. inputType: "Input",
  454. outputType: "Output"
  455. ),
  456. ]
  457. )
  458. let expected = """
  459. \(access) struct FooClient<Transport>: Foo_ClientProtocol where Transport: GRPCCore.ClientTransport {
  460. private let client: GRPCCore.GRPCClient<Transport>
  461. /// Creates a new client wrapping the provided `GRPCCore.GRPCClient`.
  462. ///
  463. /// - Parameters:
  464. /// - client: A `GRPCCore.GRPCClient` providing a communication channel to the service.
  465. \(access) init(wrapping client: GRPCCore.GRPCClient<Transport>) {
  466. self.client = client
  467. }
  468. /// Call the "Unary" method.
  469. ///
  470. /// > Source IDL Documentation:
  471. /// >
  472. /// > Unary docs
  473. ///
  474. /// - Parameters:
  475. /// - request: A request containing a single `Input` message.
  476. /// - serializer: A serializer for `Input` messages.
  477. /// - deserializer: A deserializer for `Output` messages.
  478. /// - options: Options to apply to this RPC.
  479. /// - handleResponse: A closure which handles the response, the result of which is
  480. /// returned to the caller. Returning from the closure will cancel the RPC if it
  481. /// hasn't already finished.
  482. /// - Returns: The result of `handleResponse`.
  483. \(access) func unary<Result>(
  484. request: GRPCCore.ClientRequest<Input>,
  485. serializer: some GRPCCore.MessageSerializer<Input>,
  486. deserializer: some GRPCCore.MessageDeserializer<Output>,
  487. options: GRPCCore.CallOptions = .defaults,
  488. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  489. try response.message
  490. }
  491. ) async throws -> Result where Result: Sendable {
  492. try await self.client.unary(
  493. request: request,
  494. descriptor: BarService.Method.Unary.descriptor,
  495. serializer: serializer,
  496. deserializer: deserializer,
  497. options: options,
  498. onResponse: handleResponse
  499. )
  500. }
  501. /// Call the "ClientStreaming" method.
  502. ///
  503. /// > Source IDL Documentation:
  504. /// >
  505. /// > ClientStreaming docs
  506. ///
  507. /// - Parameters:
  508. /// - request: A streaming request producing `Input` messages.
  509. /// - serializer: A serializer for `Input` messages.
  510. /// - deserializer: A deserializer for `Output` messages.
  511. /// - options: Options to apply to this RPC.
  512. /// - handleResponse: A closure which handles the response, the result of which is
  513. /// returned to the caller. Returning from the closure will cancel the RPC if it
  514. /// hasn't already finished.
  515. /// - Returns: The result of `handleResponse`.
  516. \(access) func clientStreaming<Result>(
  517. request: GRPCCore.StreamingClientRequest<Input>,
  518. serializer: some GRPCCore.MessageSerializer<Input>,
  519. deserializer: some GRPCCore.MessageDeserializer<Output>,
  520. options: GRPCCore.CallOptions = .defaults,
  521. onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  522. try response.message
  523. }
  524. ) async throws -> Result where Result: Sendable {
  525. try await self.client.clientStreaming(
  526. request: request,
  527. descriptor: BarService.Method.ClientStreaming.descriptor,
  528. serializer: serializer,
  529. deserializer: deserializer,
  530. options: options,
  531. onResponse: handleResponse
  532. )
  533. }
  534. /// Call the "ServerStreaming" method.
  535. ///
  536. /// > Source IDL Documentation:
  537. /// >
  538. /// > ServerStreaming docs
  539. ///
  540. /// - Parameters:
  541. /// - request: A request containing a single `Input` message.
  542. /// - serializer: A serializer for `Input` messages.
  543. /// - deserializer: A deserializer for `Output` messages.
  544. /// - options: Options to apply to this RPC.
  545. /// - handleResponse: A closure which handles the response, the result of which is
  546. /// returned to the caller. Returning from the closure will cancel the RPC if it
  547. /// hasn't already finished.
  548. /// - Returns: The result of `handleResponse`.
  549. \(access) func serverStreaming<Result>(
  550. request: GRPCCore.ClientRequest<Input>,
  551. serializer: some GRPCCore.MessageSerializer<Input>,
  552. deserializer: some GRPCCore.MessageDeserializer<Output>,
  553. options: GRPCCore.CallOptions = .defaults,
  554. onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse<Output>) async throws -> Result
  555. ) async throws -> Result where Result: Sendable {
  556. try await self.client.serverStreaming(
  557. request: request,
  558. descriptor: BarService.Method.ServerStreaming.descriptor,
  559. serializer: serializer,
  560. deserializer: deserializer,
  561. options: options,
  562. onResponse: handleResponse
  563. )
  564. }
  565. /// Call the "BidiStreaming" method.
  566. ///
  567. /// > Source IDL Documentation:
  568. /// >
  569. /// > BidiStreaming docs
  570. ///
  571. /// - Parameters:
  572. /// - request: A streaming request producing `Input` messages.
  573. /// - serializer: A serializer for `Input` messages.
  574. /// - deserializer: A deserializer for `Output` messages.
  575. /// - options: Options to apply to this RPC.
  576. /// - handleResponse: A closure which handles the response, the result of which is
  577. /// returned to the caller. Returning from the closure will cancel the RPC if it
  578. /// hasn't already finished.
  579. /// - Returns: The result of `handleResponse`.
  580. \(access) func bidiStreaming<Result>(
  581. request: GRPCCore.StreamingClientRequest<Input>,
  582. serializer: some GRPCCore.MessageSerializer<Input>,
  583. deserializer: some GRPCCore.MessageDeserializer<Output>,
  584. options: GRPCCore.CallOptions = .defaults,
  585. onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse<Output>) async throws -> Result
  586. ) async throws -> Result where Result: Sendable {
  587. try await self.client.bidirectionalStreaming(
  588. request: request,
  589. descriptor: BarService.Method.BidiStreaming.descriptor,
  590. serializer: serializer,
  591. deserializer: deserializer,
  592. options: options,
  593. onResponse: handleResponse
  594. )
  595. }
  596. }
  597. """
  598. #expect(render(.struct(decl)) == expected)
  599. }
  600. }
  601. }