2
0

ServerCodeTranslator.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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.StreamingServerRequest<Foo_Bar_Input>
  28. /// ) async throws -> GRPCCore.StreamingServerResponse<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<Foo_Bar_Input>
  46. /// ) async throws -> GRPCCore.ServerResponse<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.StreamingServerRequest<Foo_Bar_Input>
  53. /// ) async throws -> GRPCCore.StreamingServerResponse<Foo_Bar_Output> {
  54. /// let response = try await self.baz(request: GRPCCore.ServerRequest(stream: request))
  55. /// return GRPCCore.StreamingServerResponse(single: response)
  56. /// }
  57. /// }
  58. ///```
  59. struct ServerCodeTranslator: SpecializedTranslator {
  60. var accessLevel: SourceGenerator.Config.AccessLevel
  61. init(accessLevel: SourceGenerator.Config.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", "StreamingServerRequest"]),
  140. wrapped: .member(method.inputType)
  141. )
  142. ),
  143. .init(label: "context", type: .member(["GRPCCore", "ServerContext"])),
  144. ],
  145. keywords: [.async, .throws],
  146. returnType: .identifierType(
  147. .generic(
  148. wrapper: .member(["GRPCCore", "StreamingServerResponse"]),
  149. wrapped: .member(method.outputType)
  150. )
  151. )
  152. )
  153. }
  154. private func makeConformanceToRPCServiceExtension(
  155. for service: CodeGenerationRequest.ServiceDescriptor,
  156. in codeGenerationRequest: CodeGenerationRequest
  157. ) -> Declaration {
  158. let streamingProtocol = self.protocolNameTypealias(service: service, streaming: true)
  159. let registerRPCMethod = self.makeRegisterRPCsMethod(for: service, in: codeGenerationRequest)
  160. return .guarded(
  161. self.availabilityGuard,
  162. .extension(
  163. onType: streamingProtocol,
  164. declarations: [registerRPCMethod]
  165. )
  166. )
  167. }
  168. private func makeRegisterRPCsMethod(
  169. for service: CodeGenerationRequest.ServiceDescriptor,
  170. in codeGenerationRequest: CodeGenerationRequest
  171. ) -> Declaration {
  172. let registerRPCsSignature = FunctionSignatureDescription(
  173. accessModifier: self.accessModifier,
  174. kind: .function(name: "registerMethods"),
  175. parameters: [
  176. .init(
  177. label: "with",
  178. name: "router",
  179. type: .member(["GRPCCore", "RPCRouter"]),
  180. `inout`: true
  181. )
  182. ]
  183. )
  184. let registerRPCsBody = self.makeRegisterRPCsMethodBody(for: service, in: codeGenerationRequest)
  185. return .guarded(
  186. self.availabilityGuard,
  187. .function(signature: registerRPCsSignature, body: registerRPCsBody)
  188. )
  189. }
  190. private func makeRegisterRPCsMethodBody(
  191. for service: CodeGenerationRequest.ServiceDescriptor,
  192. in codeGenerationRequest: CodeGenerationRequest
  193. ) -> [CodeBlock] {
  194. let registerHandlerCalls = service.methods.compactMap {
  195. CodeBlock.expression(
  196. Expression.functionCall(
  197. calledExpression: .memberAccess(
  198. MemberAccessDescription(left: .identifierPattern("router"), right: "registerHandler")
  199. ),
  200. arguments: self.makeArgumentsForRegisterHandler(
  201. for: $0,
  202. in: service,
  203. from: codeGenerationRequest
  204. )
  205. )
  206. )
  207. }
  208. return registerHandlerCalls
  209. }
  210. private func makeArgumentsForRegisterHandler(
  211. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  212. in service: CodeGenerationRequest.ServiceDescriptor,
  213. from codeGenerationRequest: CodeGenerationRequest
  214. ) -> [FunctionArgumentDescription] {
  215. var arguments = [FunctionArgumentDescription]()
  216. arguments.append(
  217. FunctionArgumentDescription(
  218. label: "forMethod",
  219. expression: .identifierPattern(self.methodDescriptorPath(for: method, service: service))
  220. )
  221. )
  222. arguments.append(
  223. FunctionArgumentDescription(
  224. label: "deserializer",
  225. expression: .identifierPattern(codeGenerationRequest.lookupDeserializer(method.inputType))
  226. )
  227. )
  228. arguments.append(
  229. FunctionArgumentDescription(
  230. label: "serializer",
  231. expression: .identifierPattern(codeGenerationRequest.lookupSerializer(method.outputType))
  232. )
  233. )
  234. let rpcFunctionCall = Expression.functionCall(
  235. calledExpression: .memberAccess(
  236. MemberAccessDescription(
  237. left: .identifierPattern("self"),
  238. right: method.name.generatedLowerCase
  239. )
  240. ),
  241. arguments: [
  242. FunctionArgumentDescription(label: "request", expression: .identifierPattern("request")),
  243. FunctionArgumentDescription(label: "context", expression: .identifierPattern("context")),
  244. ]
  245. )
  246. let handlerClosureBody = Expression.unaryKeyword(
  247. kind: .try,
  248. expression: .unaryKeyword(kind: .await, expression: rpcFunctionCall)
  249. )
  250. arguments.append(
  251. FunctionArgumentDescription(
  252. label: "handler",
  253. expression: .closureInvocation(
  254. ClosureInvocationDescription(
  255. argumentNames: ["request", "context"],
  256. body: [.expression(handlerClosureBody)]
  257. )
  258. )
  259. )
  260. )
  261. return arguments
  262. }
  263. private func makeServiceProtocol(
  264. for service: CodeGenerationRequest.ServiceDescriptor
  265. ) -> Declaration {
  266. let methods = service.methods.compactMap {
  267. self.makeServiceProtocolMethod(for: $0, in: service)
  268. }
  269. let protocolName = self.protocolName(service: service, streaming: false)
  270. let streamingProtocol = self.protocolNameTypealias(service: service, streaming: true)
  271. return .commentable(
  272. .preFormatted(service.documentation),
  273. .guarded(
  274. self.availabilityGuard,
  275. .protocol(
  276. ProtocolDescription(
  277. accessModifier: self.accessModifier,
  278. name: protocolName,
  279. conformances: [streamingProtocol],
  280. members: methods
  281. )
  282. )
  283. )
  284. )
  285. }
  286. private func makeServiceProtocolMethod(
  287. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  288. in service: CodeGenerationRequest.ServiceDescriptor,
  289. accessModifier: AccessModifier? = nil
  290. ) -> Declaration {
  291. let inputStreaming = method.isInputStreaming ? "Streaming" : ""
  292. let outputStreaming = method.isOutputStreaming ? "Streaming" : ""
  293. let functionSignature = FunctionSignatureDescription(
  294. accessModifier: accessModifier,
  295. kind: .function(name: method.name.generatedLowerCase),
  296. parameters: [
  297. .init(
  298. label: "request",
  299. type:
  300. .generic(
  301. wrapper: .member(["GRPCCore", "\(inputStreaming)ServerRequest"]),
  302. wrapped: .member(method.inputType)
  303. )
  304. ),
  305. .init(label: "context", type: .member(["GRPCCore", "ServerContext"])),
  306. ],
  307. keywords: [.async, .throws],
  308. returnType: .identifierType(
  309. .generic(
  310. wrapper: .member(["GRPCCore", "\(outputStreaming)ServerResponse"]),
  311. wrapped: .member(method.outputType)
  312. )
  313. )
  314. )
  315. return .commentable(
  316. .preFormatted(method.documentation),
  317. .function(FunctionDescription(signature: functionSignature))
  318. )
  319. }
  320. private func makeExtensionServiceProtocol(
  321. for service: CodeGenerationRequest.ServiceDescriptor
  322. ) -> Declaration {
  323. let methods = service.methods.compactMap {
  324. self.makeServiceProtocolExtensionMethod(for: $0, in: service)
  325. }
  326. let protocolName = self.protocolNameTypealias(service: service, streaming: false)
  327. return .guarded(
  328. self.availabilityGuard,
  329. .extension(
  330. onType: protocolName,
  331. declarations: methods
  332. )
  333. )
  334. }
  335. private func makeServiceProtocolExtensionMethod(
  336. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  337. in service: CodeGenerationRequest.ServiceDescriptor
  338. ) -> Declaration? {
  339. // The method has the same definition in StreamingServiceProtocol and ServiceProtocol.
  340. if method.isInputStreaming && method.isOutputStreaming {
  341. return nil
  342. }
  343. let response = CodeBlock(item: .declaration(self.makeResponse(for: method)))
  344. let returnStatement = CodeBlock(item: .expression(self.makeReturnStatement(for: method)))
  345. return .function(
  346. signature: self.makeStreamingMethodSignature(
  347. for: method,
  348. in: service,
  349. accessModifier: self.accessModifier
  350. ),
  351. body: [response, returnStatement]
  352. )
  353. }
  354. private func makeResponse(
  355. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor
  356. ) -> Declaration {
  357. let serverRequest: Expression
  358. if !method.isInputStreaming {
  359. // Transform the streaming request into a unary request.
  360. serverRequest = Expression.functionCall(
  361. calledExpression: .identifierType(.member(["GRPCCore", "ServerRequest"])),
  362. arguments: [
  363. FunctionArgumentDescription(label: "stream", expression: .identifierPattern("request"))
  364. ]
  365. )
  366. } else {
  367. serverRequest = Expression.identifierPattern("request")
  368. }
  369. // Call to the corresponding ServiceProtocol method.
  370. let serviceProtocolMethod = Expression.functionCall(
  371. calledExpression: .memberAccess(
  372. MemberAccessDescription(
  373. left: .identifierPattern("self"),
  374. right: method.name.generatedLowerCase
  375. )
  376. ),
  377. arguments: [
  378. FunctionArgumentDescription(label: "request", expression: serverRequest),
  379. FunctionArgumentDescription(label: "context", expression: .identifier(.pattern("context"))),
  380. ]
  381. )
  382. let responseValue = Expression.unaryKeyword(
  383. kind: .try,
  384. expression: .unaryKeyword(kind: .await, expression: serviceProtocolMethod)
  385. )
  386. return .variable(kind: .let, left: "response", right: responseValue)
  387. }
  388. private func makeReturnStatement(
  389. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor
  390. ) -> Expression {
  391. let returnValue: Expression
  392. // Transforming the unary response into a streaming one.
  393. if !method.isOutputStreaming {
  394. returnValue = .functionCall(
  395. calledExpression: .identifier(.type(.member(["GRPCCore", "StreamingServerResponse"]))),
  396. arguments: [
  397. (FunctionArgumentDescription(label: "single", expression: .identifierPattern("response")))
  398. ]
  399. )
  400. } else {
  401. returnValue = .identifierPattern("response")
  402. }
  403. return .unaryKeyword(kind: .return, expression: returnValue)
  404. }
  405. fileprivate enum InputOutputType {
  406. case input
  407. case output
  408. }
  409. /// Generates the fully qualified name of a method descriptor.
  410. private func methodDescriptorPath(
  411. for method: CodeGenerationRequest.ServiceDescriptor.MethodDescriptor,
  412. service: CodeGenerationRequest.ServiceDescriptor
  413. ) -> String {
  414. return
  415. "\(service.namespacedGeneratedName).Method.\(method.name.generatedUpperCase).descriptor"
  416. }
  417. /// Generates the fully qualified name of the type alias for a service protocol.
  418. internal func protocolNameTypealias(
  419. service: CodeGenerationRequest.ServiceDescriptor,
  420. streaming: Bool
  421. ) -> String {
  422. if streaming {
  423. return "\(service.namespacedGeneratedName).StreamingServiceProtocol"
  424. }
  425. return "\(service.namespacedGeneratedName).ServiceProtocol"
  426. }
  427. /// Generates the name of a service protocol.
  428. internal func protocolName(
  429. service: CodeGenerationRequest.ServiceDescriptor,
  430. streaming: Bool
  431. ) -> String {
  432. if streaming {
  433. return "\(service.namespacedGeneratedName)_StreamingServiceProtocol"
  434. }
  435. return "\(service.namespacedGeneratedName)_ServiceProtocol"
  436. }
  437. }