ServerCodeTranslator.swift 16 KB

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