ServerCodeTranslator.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /*
  2. * Copyright 2023, 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. /// Creates a representation for the server code that will be generated based on the ``CodeGenerationRequest`` object
  17. /// specifications, using types from ``StructuredSwiftRepresentation``.
  18. ///
  19. /// For example, in the case of a service called "Bar", in the "foo" namespace which has
  20. /// one method "baz" with input type "Input" and output type "Output", the ``ServerCodeTranslator`` will create
  21. /// a representation for the following generated code:
  22. ///
  23. /// ```swift
  24. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  25. /// public protocol Foo_BarStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
  26. /// func baz(
  27. /// request: GRPCCore.ServerRequest.Stream<Foo_Bar_Input>
  28. /// ) async throws -> GRPCCore.ServerResponse.Stream<Foo_Bar_Output>
  29. /// }
  30. /// // Conformance to `GRPCCore.RegistrableRPCService`.
  31. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  32. /// extension Foo_Bar.StreamingServiceProtocol {
  33. /// public func registerMethods(with router: inout GRPCCore.RPCRouter) {
  34. /// router.registerHandler(
  35. /// forMethod: Foo_Bar.Method.baz.descriptor,
  36. /// deserializer: GRPCProtobuf.ProtobufDeserializer<Foo_Bar_Input>(),
  37. /// serializer: GRPCProtobuf.ProtobufSerializer<Foo_Bar_Output>(),
  38. /// handler: { request in try await self.baz(request: request) }
  39. /// )
  40. /// }
  41. /// }
  42. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  43. /// public protocol Foo_BarServiceProtocol: Foo_Bar.StreamingServiceProtocol {
  44. /// func baz(
  45. /// request: GRPCCore.ServerRequest.Single<Foo_Bar_Input>
  46. /// ) async throws -> GRPCCore.ServerResponse.Single<Foo_Bar_Output>
  47. /// }
  48. /// // Partial conformance to `Foo_BarStreamingServiceProtocol`.
  49. /// @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
  50. /// extension Foo_Bar.ServiceProtocol {
  51. /// public func baz(
  52. /// request: GRPCCore.ServerRequest.Stream<Foo_Bar_Input>
  53. /// ) async throws -> GRPCCore.ServerResponse.Stream<Foo_Bar_Output> {
  54. /// let response = try await self.baz(request: GRPCCore.ServerRequest.Single(stream: request))
  55. /// return GRPCCore.ServerResponse.Stream(single: response)
  56. /// }
  57. /// }
  58. ///```
  59. struct ServerCodeTranslator: SpecializedTranslator {
  60. var accessLevel: SourceGenerator.Configuration.AccessLevel
  61. init(accessLevel: SourceGenerator.Configuration.AccessLevel) {
  62. self.accessLevel = accessLevel
  63. }
  64. func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] {
  65. var codeBlocks = [CodeBlock]()
  66. for service in codeGenerationRequest.services {
  67. // Create the streaming protocol that declares the service methods as bidirectional streaming.
  68. let streamingProtocol = CodeBlockItem.declaration(self.makeStreamingProtocol(for: service))
  69. codeBlocks.append(CodeBlock(item: streamingProtocol))
  70. // Create extension for implementing the 'registerRPCs' function which is a 'RegistrableRPCService' requirement.
  71. let conformanceToRPCServiceExtension = CodeBlockItem.declaration(
  72. self.makeConformanceToRPCServiceExtension(for: service, in: codeGenerationRequest)
  73. )
  74. codeBlocks.append(
  75. CodeBlock(
  76. comment: .doc("Conformance to `GRPCCore.RegistrableRPCService`."),
  77. item: conformanceToRPCServiceExtension
  78. )
  79. )
  80. // Create the service protocol that declares the service methods as they are described in the Source IDL (unary,
  81. // client/server streaming or bidirectional streaming).
  82. let serviceProtocol = CodeBlockItem.declaration(self.makeServiceProtocol(for: service))
  83. codeBlocks.append(CodeBlock(item: serviceProtocol))
  84. // Create extension for partial conformance to the streaming protocol.
  85. let extensionServiceProtocol = CodeBlockItem.declaration(
  86. self.makeExtensionServiceProtocol(for: service)
  87. )
  88. codeBlocks.append(
  89. CodeBlock(
  90. comment: .doc(
  91. "Partial conformance to `\(self.protocolName(service: service, streaming: true))`."
  92. ),
  93. item: extensionServiceProtocol
  94. )
  95. )
  96. }
  97. return codeBlocks
  98. }
  99. }
  100. extension ServerCodeTranslator {
  101. private func makeStreamingProtocol(
  102. for service: CodeGenerationRequest.ServiceDescriptor
  103. ) -> Declaration {
  104. let methods = service.methods.compactMap {
  105. Declaration.commentable(
  106. .preFormatted($0.documentation),
  107. .function(
  108. FunctionDescription(
  109. signature: self.makeStreamingMethodSignature(for: $0, in: service)
  110. )
  111. )
  112. )
  113. }
  114. let streamingProtocol = Declaration.protocol(
  115. .init(
  116. accessModifier: self.accessModifier,
  117. name: self.protocolName(service: service, streaming: true),
  118. conformances: ["GRPCCore.RegistrableRPCService"],
  119. members: methods
  120. )
  121. )
  122. return .commentable(
  123. .preFormatted(service.documentation),
  124. .guarded(self.availabilityGuard, streamingProtocol)
  125. )
  126. }
  127. private func makeStreamingMethodSignature(
  128. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  129. in service: CodeGenerationRequest.ServiceDescriptor,
  130. accessModifier: AccessModifier? = nil
  131. ) -> FunctionSignatureDescription {
  132. return FunctionSignatureDescription(
  133. accessModifier: accessModifier,
  134. kind: .function(name: method.name.generatedLowerCase),
  135. parameters: [
  136. .init(
  137. label: "request",
  138. type: .generic(
  139. wrapper: .member(["GRPCCore", "ServerRequest", "Stream"]),
  140. wrapped: .member(method.inputType)
  141. )
  142. )
  143. ],
  144. keywords: [.async, .throws],
  145. returnType: .identifierType(
  146. .generic(
  147. wrapper: .member(["GRPCCore", "ServerResponse", "Stream"]),
  148. wrapped: .member(method.outputType)
  149. )
  150. )
  151. )
  152. }
  153. private func makeConformanceToRPCServiceExtension(
  154. for service: CodeGenerationRequest.ServiceDescriptor,
  155. in codeGenerationRequest: CodeGenerationRequest
  156. ) -> Declaration {
  157. let streamingProtocol = self.protocolNameTypealias(service: service, streaming: true)
  158. let registerRPCMethod = self.makeRegisterRPCsMethod(for: service, in: codeGenerationRequest)
  159. return .guarded(
  160. self.availabilityGuard,
  161. .extension(
  162. onType: streamingProtocol,
  163. declarations: [registerRPCMethod]
  164. )
  165. )
  166. }
  167. private func makeRegisterRPCsMethod(
  168. for service: CodeGenerationRequest.ServiceDescriptor,
  169. in codeGenerationRequest: CodeGenerationRequest
  170. ) -> Declaration {
  171. let registerRPCsSignature = FunctionSignatureDescription(
  172. accessModifier: self.accessModifier,
  173. kind: .function(name: "registerMethods"),
  174. parameters: [
  175. .init(
  176. label: "with",
  177. name: "router",
  178. type: .member(["GRPCCore", "RPCRouter"]),
  179. `inout`: true
  180. )
  181. ]
  182. )
  183. let registerRPCsBody = self.makeRegisterRPCsMethodBody(for: service, in: codeGenerationRequest)
  184. return .guarded(
  185. self.availabilityGuard,
  186. .function(signature: registerRPCsSignature, body: registerRPCsBody)
  187. )
  188. }
  189. private func makeRegisterRPCsMethodBody(
  190. for service: CodeGenerationRequest.ServiceDescriptor,
  191. in codeGenerationRequest: CodeGenerationRequest
  192. ) -> [CodeBlock] {
  193. let registerHandlerCalls = service.methods.compactMap {
  194. CodeBlock.expression(
  195. Expression.functionCall(
  196. calledExpression: .memberAccess(
  197. MemberAccessDescription(left: .identifierPattern("router"), right: "registerHandler")
  198. ),
  199. arguments: self.makeArgumentsForRegisterHandler(
  200. for: $0,
  201. in: service,
  202. from: codeGenerationRequest
  203. )
  204. )
  205. )
  206. }
  207. return registerHandlerCalls
  208. }
  209. private func makeArgumentsForRegisterHandler(
  210. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  211. in service: CodeGenerationRequest.ServiceDescriptor,
  212. from codeGenerationRequest: CodeGenerationRequest
  213. ) -> [FunctionArgumentDescription] {
  214. var arguments = [FunctionArgumentDescription]()
  215. arguments.append(
  216. .init(
  217. label: "forMethod",
  218. expression: .identifierPattern(
  219. self.methodDescriptorPath(for: method, service: service)
  220. )
  221. )
  222. )
  223. arguments.append(
  224. .init(
  225. label: "deserializer",
  226. expression: .identifierPattern(codeGenerationRequest.lookupDeserializer(method.inputType))
  227. )
  228. )
  229. arguments.append(
  230. .init(
  231. label: "serializer",
  232. expression:
  233. .identifierPattern(codeGenerationRequest.lookupSerializer(method.outputType))
  234. )
  235. )
  236. let getFunctionCall = Expression.functionCall(
  237. calledExpression: .memberAccess(
  238. MemberAccessDescription(
  239. left: .identifierPattern("self"),
  240. right: method.name.generatedLowerCase
  241. )
  242. ),
  243. arguments: [
  244. FunctionArgumentDescription(label: "request", expression: .identifierPattern("request"))
  245. ]
  246. )
  247. let handlerClosureBody = Expression.unaryKeyword(
  248. kind: .try,
  249. expression: .unaryKeyword(kind: .await, expression: getFunctionCall)
  250. )
  251. arguments.append(
  252. .init(
  253. label: "handler",
  254. expression: .closureInvocation(
  255. .init(argumentNames: ["request"], body: [.expression(handlerClosureBody)])
  256. )
  257. )
  258. )
  259. return arguments
  260. }
  261. private func makeServiceProtocol(
  262. for service: CodeGenerationRequest.ServiceDescriptor
  263. ) -> Declaration {
  264. let methods = service.methods.compactMap {
  265. self.makeServiceProtocolMethod(for: $0, in: service)
  266. }
  267. let protocolName = self.protocolName(service: service, streaming: false)
  268. let streamingProtocol = self.protocolNameTypealias(service: service, streaming: true)
  269. return .commentable(
  270. .preFormatted(service.documentation),
  271. .guarded(
  272. self.availabilityGuard,
  273. .protocol(
  274. ProtocolDescription(
  275. accessModifier: self.accessModifier,
  276. name: protocolName,
  277. conformances: [streamingProtocol],
  278. members: methods
  279. )
  280. )
  281. )
  282. )
  283. }
  284. private func makeServiceProtocolMethod(
  285. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  286. in service: CodeGenerationRequest.ServiceDescriptor,
  287. accessModifier: AccessModifier? = nil
  288. ) -> Declaration {
  289. let inputStreaming = method.isInputStreaming ? "Stream" : "Single"
  290. let outputStreaming = method.isOutputStreaming ? "Stream" : "Single"
  291. let functionSignature = FunctionSignatureDescription(
  292. accessModifier: accessModifier,
  293. kind: .function(name: method.name.generatedLowerCase),
  294. parameters: [
  295. .init(
  296. label: "request",
  297. type:
  298. .generic(
  299. wrapper: .member(["GRPCCore", "ServerRequest", inputStreaming]),
  300. wrapped: .member(method.inputType)
  301. )
  302. )
  303. ],
  304. keywords: [.async, .throws],
  305. returnType: .identifierType(
  306. .generic(
  307. wrapper: .member(["GRPCCore", "ServerResponse", outputStreaming]),
  308. wrapped: .member(method.outputType)
  309. )
  310. )
  311. )
  312. return .commentable(
  313. .preFormatted(method.documentation),
  314. .function(FunctionDescription(signature: functionSignature))
  315. )
  316. }
  317. private func makeExtensionServiceProtocol(
  318. for service: CodeGenerationRequest.ServiceDescriptor
  319. ) -> Declaration {
  320. let methods = service.methods.compactMap {
  321. self.makeServiceProtocolExtensionMethod(for: $0, in: service)
  322. }
  323. let protocolName = self.protocolNameTypealias(service: service, streaming: false)
  324. return .guarded(
  325. self.availabilityGuard,
  326. .extension(
  327. onType: protocolName,
  328. declarations: methods
  329. )
  330. )
  331. }
  332. private func makeServiceProtocolExtensionMethod(
  333. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  334. in service: CodeGenerationRequest.ServiceDescriptor
  335. ) -> Declaration? {
  336. // The method has the same definition in StreamingServiceProtocol and ServiceProtocol.
  337. if method.isInputStreaming && method.isOutputStreaming {
  338. return nil
  339. }
  340. let response = CodeBlock(item: .declaration(self.makeResponse(for: method)))
  341. let returnStatement = CodeBlock(item: .expression(self.makeReturnStatement(for: method)))
  342. return .function(
  343. signature: self.makeStreamingMethodSignature(
  344. for: method,
  345. in: service,
  346. accessModifier: self.accessModifier
  347. ),
  348. body: [response, returnStatement]
  349. )
  350. }
  351. private func makeResponse(
  352. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor
  353. ) -> Declaration {
  354. let serverRequest: Expression
  355. if !method.isInputStreaming {
  356. // Transform the streaming request into a unary request.
  357. serverRequest = Expression.functionCall(
  358. calledExpression: .memberAccess(
  359. MemberAccessDescription(
  360. left: .identifierPattern("GRPCCore.ServerRequest"),
  361. right: "Single"
  362. )
  363. ),
  364. arguments: [
  365. FunctionArgumentDescription(label: "stream", expression: .identifierPattern("request"))
  366. ]
  367. )
  368. } else {
  369. serverRequest = Expression.identifierPattern("request")
  370. }
  371. // Call to the corresponding ServiceProtocol method.
  372. let serviceProtocolMethod = Expression.functionCall(
  373. calledExpression: .memberAccess(
  374. MemberAccessDescription(
  375. left: .identifierPattern("self"),
  376. right: method.name.generatedLowerCase
  377. )
  378. ),
  379. arguments: [FunctionArgumentDescription(label: "request", expression: serverRequest)]
  380. )
  381. let responseValue = Expression.unaryKeyword(
  382. kind: .try,
  383. expression: .unaryKeyword(kind: .await, expression: serviceProtocolMethod)
  384. )
  385. return .variable(kind: .let, left: "response", right: responseValue)
  386. }
  387. private func makeReturnStatement(
  388. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor
  389. ) -> Expression {
  390. let returnValue: Expression
  391. // Transforming the unary response into a streaming one.
  392. if !method.isOutputStreaming {
  393. returnValue = .functionCall(
  394. calledExpression: .memberAccess(
  395. MemberAccessDescription(
  396. left: .identifierType(.member(["GRPCCore", "ServerResponse"])),
  397. right: "Stream"
  398. )
  399. ),
  400. arguments: [
  401. (FunctionArgumentDescription(label: "single", expression: .identifierPattern("response")))
  402. ]
  403. )
  404. } else {
  405. returnValue = .identifierPattern("response")
  406. }
  407. return .unaryKeyword(kind: .return, expression: returnValue)
  408. }
  409. fileprivate enum InputOutputType {
  410. case input
  411. case output
  412. }
  413. /// Generates the fully qualified name of a method descriptor.
  414. private func methodDescriptorPath(
  415. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  416. service: CodeGenerationRequest.ServiceDescriptor
  417. ) -> String {
  418. return
  419. "\(service.namespacedGeneratedName).Method.\(method.name.generatedUpperCase).descriptor"
  420. }
  421. /// Generates the fully qualified name of the type alias for a service protocol.
  422. internal func protocolNameTypealias(
  423. service: CodeGenerationRequest.ServiceDescriptor,
  424. streaming: Bool
  425. ) -> String {
  426. if streaming {
  427. return "\(service.namespacedGeneratedName).StreamingServiceProtocol"
  428. }
  429. return "\(service.namespacedGeneratedName).ServiceProtocol"
  430. }
  431. /// Generates the name of a service protocol.
  432. internal func protocolName(
  433. service: CodeGenerationRequest.ServiceDescriptor,
  434. streaming: Bool
  435. ) -> String {
  436. if streaming {
  437. return "\(service.namespacedGeneratedName)StreamingServiceProtocol"
  438. }
  439. return "\(service.namespacedGeneratedName)ServiceProtocol"
  440. }
  441. }