StructuredSwift+Client.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  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(method.documentation),
  205. .function(
  206. signature: .clientMethod(
  207. name: method.name.generatedLowerCase,
  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. .function(
  245. .clientMethodWithDefaults(
  246. accessLevel: accessLevel,
  247. name: method.name.generatedLowerCase,
  248. input: method.inputType,
  249. output: method.outputType,
  250. streamingInput: method.isInputStreaming,
  251. streamingOutput: method.isOutputStreaming,
  252. serializer: .identifierPattern(serializer(method.inputType)),
  253. deserializer: .identifierPattern(deserializer(method.outputType))
  254. )
  255. )
  256. }
  257. )
  258. }
  259. }
  260. extension FunctionSignatureDescription {
  261. /// ```
  262. /// func foo<Result>(
  263. /// _ message: <Input>,
  264. /// metadata: GRPCCore.Metadata = [:],
  265. /// options: GRPCCore.CallOptions = .defaults,
  266. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  267. /// try response.message
  268. /// }
  269. /// ) async throws -> Result where Result: Sendable
  270. /// ```
  271. static func clientMethodExploded(
  272. accessLevel: AccessModifier? = nil,
  273. name: String,
  274. input: String,
  275. output: String,
  276. streamingInput: Bool,
  277. streamingOutput: Bool
  278. ) -> Self {
  279. var signature = FunctionSignatureDescription(
  280. accessModifier: accessLevel,
  281. kind: .function(name: name),
  282. generics: [.member("Result")],
  283. parameters: [], // Populated below
  284. keywords: [.async, .throws],
  285. returnType: .identifierPattern("Result"),
  286. whereClause: WhereClause(requirements: [.conformance("Result", "Sendable")])
  287. )
  288. if !streamingInput {
  289. signature.parameters.append(
  290. ParameterDescription(label: "_", name: "message", type: .member(input))
  291. )
  292. }
  293. // metadata: GRPCCore.Metadata = [:]
  294. signature.parameters.append(
  295. ParameterDescription(
  296. label: "metadata",
  297. type: .metadata,
  298. defaultValue: .literal(.dictionary([]))
  299. )
  300. )
  301. // options: GRPCCore.CallOptions = .defaults
  302. signature.parameters.append(
  303. ParameterDescription(
  304. label: "options",
  305. type: .callOptions,
  306. defaultValue: .dot("defaults")
  307. )
  308. )
  309. if streamingInput {
  310. signature.parameters.append(
  311. ParameterDescription(
  312. label: "requestProducer",
  313. name: "producer",
  314. type: .closure(
  315. ClosureSignatureDescription(
  316. parameters: [ParameterDescription(type: .rpcWriter(forType: input))],
  317. keywords: [.async, .throws],
  318. returnType: .identifierPattern("Void"),
  319. sendable: true,
  320. escaping: true
  321. )
  322. )
  323. )
  324. )
  325. }
  326. signature.parameters.append(
  327. ParameterDescription(
  328. label: "onResponse",
  329. name: "handleResponse",
  330. type: .closure(
  331. ClosureSignatureDescription(
  332. parameters: [
  333. ParameterDescription(
  334. type: .clientResponse(forType: output, streaming: streamingOutput)
  335. )
  336. ],
  337. keywords: [.async, .throws],
  338. returnType: .identifierPattern("Result"),
  339. sendable: true,
  340. escaping: true
  341. )
  342. ),
  343. defaultValue: streamingOutput ? nil : .closureInvocation(.defaultClientUnaryResponseHandler)
  344. )
  345. )
  346. return signature
  347. }
  348. }
  349. extension [CodeBlock] {
  350. /// ```
  351. /// let request = GRPCCore.StreamingClientRequest<Input>(
  352. /// metadata: metadata,
  353. /// producer: producer
  354. /// )
  355. /// return try await self.foo(
  356. /// request: request,
  357. /// options: options,
  358. /// onResponse: handleResponse
  359. /// )
  360. /// ```
  361. static func clientMethodExploded(
  362. name: String,
  363. input: String,
  364. streamingInput: Bool
  365. ) -> Self {
  366. func arguments(streaming: Bool) -> [FunctionArgumentDescription] {
  367. let metadata = FunctionArgumentDescription(
  368. label: "metadata",
  369. expression: .identifierPattern("metadata")
  370. )
  371. if streaming {
  372. return [
  373. metadata,
  374. FunctionArgumentDescription(
  375. label: "producer",
  376. expression: .identifierPattern("producer")
  377. ),
  378. ]
  379. } else {
  380. return [
  381. FunctionArgumentDescription(label: "message", expression: .identifierPattern("message")),
  382. metadata,
  383. ]
  384. }
  385. }
  386. return [
  387. CodeBlock(
  388. item: .declaration(
  389. .variable(
  390. kind: .let,
  391. left: .identifierPattern("request"),
  392. right: .functionCall(
  393. calledExpression: .identifierType(
  394. .clientRequest(forType: input, streaming: streamingInput)
  395. ),
  396. arguments: arguments(streaming: streamingInput)
  397. )
  398. )
  399. )
  400. ),
  401. CodeBlock(
  402. item: .expression(
  403. .return(
  404. .try(
  405. .await(
  406. .functionCall(
  407. calledExpression: .identifierPattern("self").dot(name),
  408. arguments: [
  409. FunctionArgumentDescription(
  410. label: "request",
  411. expression: .identifierPattern("request")
  412. ),
  413. FunctionArgumentDescription(
  414. label: "options",
  415. expression: .identifierPattern("options")
  416. ),
  417. FunctionArgumentDescription(
  418. label: "onResponse",
  419. expression: .identifierPattern("handleResponse")
  420. ),
  421. ]
  422. )
  423. )
  424. )
  425. )
  426. )
  427. ),
  428. ]
  429. }
  430. }
  431. extension FunctionDescription {
  432. /// ```
  433. /// func foo<Result>(
  434. /// _ message: <Input>,
  435. /// metadata: GRPCCore.Metadata = [:],
  436. /// options: GRPCCore.CallOptions = .defaults,
  437. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result = { response in
  438. /// try response.message
  439. /// }
  440. /// ) async throws -> Result where Result: Sendable {
  441. /// // ...
  442. /// }
  443. /// ```
  444. static func clientMethodExploded(
  445. accessLevel: AccessModifier? = nil,
  446. name: String,
  447. input: String,
  448. output: String,
  449. streamingInput: Bool,
  450. streamingOutput: Bool
  451. ) -> Self {
  452. FunctionDescription(
  453. signature: .clientMethodExploded(
  454. accessLevel: accessLevel,
  455. name: name,
  456. input: input,
  457. output: output,
  458. streamingInput: streamingInput,
  459. streamingOutput: streamingOutput
  460. ),
  461. body: .clientMethodExploded(name: name, input: input, streamingInput: streamingInput)
  462. )
  463. }
  464. }
  465. extension ExtensionDescription {
  466. /// ```
  467. /// extension <Name> {
  468. /// // (exploded client methods)
  469. /// }
  470. /// ```
  471. static func explodedClientMethods(
  472. accessLevel: AccessModifier? = nil,
  473. on extensionName: String,
  474. methods: [MethodDescriptor]
  475. ) -> ExtensionDescription {
  476. ExtensionDescription(
  477. onType: extensionName,
  478. declarations: methods.map { method in
  479. .commentable(
  480. .preFormatted(method.documentation),
  481. .function(
  482. .clientMethodExploded(
  483. accessLevel: accessLevel,
  484. name: method.name.generatedLowerCase,
  485. input: method.inputType,
  486. output: method.outputType,
  487. streamingInput: method.isInputStreaming,
  488. streamingOutput: method.isOutputStreaming
  489. )
  490. )
  491. )
  492. }
  493. )
  494. }
  495. }
  496. extension FunctionDescription {
  497. /// ```
  498. /// func <Name><Result>(
  499. /// request: GRPCCore.ClientRequest<Input>,
  500. /// serializer: some GRPCCore.MessageSerializer<Input>,
  501. /// deserializer: some GRPCCore.MessageDeserializer<Output>,
  502. /// options: GRPCCore.CallOptions = .default,
  503. /// onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse<Output>) async throws -> Result
  504. /// ) async throws -> Result where Result: Sendable {
  505. /// try await self.<Name>(...)
  506. /// }
  507. /// ```
  508. static func clientMethod(
  509. accessLevel: AccessModifier,
  510. name: String,
  511. input: String,
  512. output: String,
  513. serviceEnum: String,
  514. methodEnum: String,
  515. streamingInput: Bool,
  516. streamingOutput: Bool
  517. ) -> Self {
  518. let underlyingMethod: String
  519. switch (streamingInput, streamingOutput) {
  520. case (false, false):
  521. underlyingMethod = "unary"
  522. case (true, false):
  523. underlyingMethod = "clientStreaming"
  524. case (false, true):
  525. underlyingMethod = "serverStreaming"
  526. case (true, true):
  527. underlyingMethod = "bidirectionalStreaming"
  528. }
  529. return FunctionDescription(
  530. accessModifier: accessLevel,
  531. kind: .function(name: name),
  532. generics: [.member("Result")],
  533. parameters: [
  534. ParameterDescription(
  535. label: "request",
  536. type: .clientRequest(forType: input, streaming: streamingInput)
  537. ),
  538. ParameterDescription(
  539. label: "serializer",
  540. // Be explicit: 'type' is optional and '.some' resolves to Optional.some by default.
  541. type: ExistingTypeDescription.some(.serializer(forType: input))
  542. ),
  543. ParameterDescription(
  544. label: "deserializer",
  545. // Be explicit: 'type' is optional and '.some' resolves to Optional.some by default.
  546. type: ExistingTypeDescription.some(.deserializer(forType: output))
  547. ),
  548. ParameterDescription(
  549. label: "options",
  550. type: .callOptions,
  551. defaultValue: .dot("defaults")
  552. ),
  553. ParameterDescription(
  554. label: "onResponse",
  555. name: "handleResponse",
  556. type: .closure(
  557. ClosureSignatureDescription(
  558. parameters: [
  559. ParameterDescription(
  560. type: .clientResponse(forType: output, streaming: streamingOutput)
  561. )
  562. ],
  563. keywords: [.async, .throws],
  564. returnType: .identifierPattern("Result"),
  565. sendable: true,
  566. escaping: true
  567. )
  568. ),
  569. defaultValue: streamingOutput
  570. ? nil
  571. : .closureInvocation(.defaultClientUnaryResponseHandler)
  572. ),
  573. ],
  574. keywords: [.async, .throws],
  575. returnType: .identifierPattern("Result"),
  576. whereClause: WhereClause(requirements: [.conformance("Result", "Sendable")]),
  577. body: [
  578. .try(
  579. .await(
  580. .functionCall(
  581. calledExpression: .identifierPattern("self").dot("client").dot(underlyingMethod),
  582. arguments: [
  583. FunctionArgumentDescription(
  584. label: "request",
  585. expression: .identifierPattern("request")
  586. ),
  587. FunctionArgumentDescription(
  588. label: "descriptor",
  589. expression: .identifierPattern(serviceEnum)
  590. .dot("Method")
  591. .dot(methodEnum)
  592. .dot("descriptor")
  593. ),
  594. FunctionArgumentDescription(
  595. label: "serializer",
  596. expression: .identifierPattern("serializer")
  597. ),
  598. FunctionArgumentDescription(
  599. label: "deserializer",
  600. expression: .identifierPattern("deserializer")
  601. ),
  602. FunctionArgumentDescription(
  603. label: "options",
  604. expression: .identifierPattern("options")
  605. ),
  606. FunctionArgumentDescription(
  607. label: "onResponse",
  608. expression: .identifierPattern("handleResponse")
  609. ),
  610. ]
  611. )
  612. )
  613. )
  614. ]
  615. )
  616. }
  617. }
  618. extension StructDescription {
  619. /// ```
  620. /// struct <Name>: <ClientProtocol> {
  621. /// private let client: GRPCCore.GRPCClient
  622. ///
  623. /// init(wrapping client: GRPCCore.GRPCClient) {
  624. /// self.client = client
  625. /// }
  626. ///
  627. /// // ...
  628. /// }
  629. /// ```
  630. static func client(
  631. accessLevel: AccessModifier,
  632. name: String,
  633. serviceEnum: String,
  634. clientProtocol: String,
  635. methods: [MethodDescriptor]
  636. ) -> Self {
  637. StructDescription(
  638. accessModifier: accessLevel,
  639. name: name,
  640. conformances: [clientProtocol],
  641. members: [
  642. .variable(accessModifier: .private, kind: .let, left: "client", type: .grpcClient),
  643. .function(
  644. accessModifier: accessLevel,
  645. kind: .initializer,
  646. parameters: [
  647. ParameterDescription(label: "wrapping", name: "client", type: .grpcClient)
  648. ],
  649. whereClause: nil,
  650. body: [
  651. .expression(
  652. .assignment(
  653. left: .identifierPattern("self").dot("client"),
  654. right: .identifierPattern("client")
  655. )
  656. )
  657. ]
  658. ),
  659. ]
  660. + methods.map { method in
  661. .commentable(
  662. .preFormatted(method.documentation),
  663. .function(
  664. .clientMethod(
  665. accessLevel: accessLevel,
  666. name: method.name.generatedLowerCase,
  667. input: method.inputType,
  668. output: method.outputType,
  669. serviceEnum: serviceEnum,
  670. methodEnum: method.name.generatedUpperCase,
  671. streamingInput: method.isInputStreaming,
  672. streamingOutput: method.isOutputStreaming
  673. )
  674. )
  675. )
  676. }
  677. )
  678. }
  679. }