Generator-Client.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. * Copyright 2018, 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 Foundation
  17. import SwiftProtobuf
  18. import SwiftProtobufPluginLibrary
  19. extension Generator {
  20. internal func printClient() {
  21. println()
  22. printServiceClientProtocol()
  23. println()
  24. printClientProtocolExtension()
  25. println()
  26. printServiceClientImplementation()
  27. }
  28. private func printFunction(name: String, arguments: [String], returnType: String, access: String? = nil, bodyBuilder: (() -> ())?) {
  29. // Add a space after access, if it exists.
  30. let accessOrEmpty = access.map { $0 + " " } ?? ""
  31. let hasBody = bodyBuilder != nil
  32. if arguments.isEmpty {
  33. // Don't bother splitting across multiple lines if there are no arguments.
  34. self.println("\(accessOrEmpty)func \(name)() -> \(returnType)", newline: !hasBody)
  35. } else {
  36. self.println("\(accessOrEmpty)func \(name)(")
  37. self.withIndentation {
  38. // Add a comma after each argument except the last.
  39. arguments.forEach(beforeLast: {
  40. self.println($0 + ",")
  41. }, onLast: {
  42. self.println($0)
  43. })
  44. }
  45. self.println(") -> \(returnType)", newline: !hasBody)
  46. }
  47. if let bodyBuilder = bodyBuilder {
  48. self.println(" {")
  49. self.withIndentation {
  50. bodyBuilder()
  51. }
  52. self.println("}")
  53. }
  54. }
  55. private func printServiceClientProtocol() {
  56. self.println("/// Usage: instantiate \(self.clientClassName), then call methods of this protocol to make API calls.")
  57. self.println("\(self.access) protocol \(self.clientProtocolName): GRPCClient {")
  58. self.withIndentation {
  59. for method in service.methods {
  60. self.method = method
  61. self.printFunction(
  62. name: self.methodFunctionName,
  63. arguments: self.methodArguments,
  64. returnType: self.methodReturnType,
  65. bodyBuilder: nil
  66. )
  67. self.println()
  68. }
  69. }
  70. println("}")
  71. }
  72. private func printClientProtocolExtension() {
  73. self.println("extension \(self.clientProtocolName) {")
  74. self.withIndentation {
  75. for method in service.methods {
  76. self.method = method
  77. let body: () -> ()
  78. switch streamingType(method) {
  79. case .unary:
  80. body = {
  81. self.println("return self.\(self.methodFunctionName)(request, callOptions: self.defaultCallOptions)")
  82. }
  83. case .serverStreaming:
  84. body = {
  85. self.println("return self.\(self.methodFunctionName)(request, callOptions: self.defaultCallOptions, handler: handler)")
  86. }
  87. case .clientStreaming:
  88. body = {
  89. self.println("return self.\(self.methodFunctionName)(callOptions: self.defaultCallOptions)")
  90. }
  91. case .bidirectionalStreaming:
  92. body = {
  93. self.println("return self.\(self.methodFunctionName)(callOptions: self.defaultCallOptions, handler: handler)")
  94. }
  95. }
  96. self.printFunction(
  97. name: self.methodFunctionName,
  98. arguments: self.methodArgumentsWithoutCallOptions,
  99. returnType: self.methodReturnType,
  100. access: self.access,
  101. bodyBuilder: body
  102. )
  103. self.println()
  104. }
  105. }
  106. self.println("}")
  107. }
  108. private func printServiceClientImplementation() {
  109. println("\(access) final class \(clientClassName): \(clientProtocolName) {")
  110. indent()
  111. println("\(access) let channel: GRPCChannel")
  112. println("\(access) var defaultCallOptions: CallOptions")
  113. println()
  114. println("/// Creates a client for the \(servicePath) service.")
  115. println("///")
  116. printParameters()
  117. println("/// - channel: `GRPCChannel` to the service host.")
  118. println("/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.")
  119. println("\(access) init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {")
  120. indent()
  121. println("self.channel = channel")
  122. println("self.defaultCallOptions = defaultCallOptions")
  123. outdent()
  124. println("}")
  125. self.printMethods()
  126. outdent()
  127. println("}")
  128. }
  129. private func printMethods(callFactory: String = "self") {
  130. for method in self.service.methods {
  131. self.println()
  132. self.method = method
  133. switch self.streamType {
  134. case .unary:
  135. self.printUnaryCall(callFactory: callFactory)
  136. case .serverStreaming:
  137. self.printServerStreamingCall(callFactory: callFactory)
  138. case .clientStreaming:
  139. self.printClientStreamingCall(callFactory: callFactory)
  140. case .bidirectionalStreaming:
  141. self.printBidirectionalStreamingCall(callFactory: callFactory)
  142. }
  143. }
  144. }
  145. private func printUnaryCall(callFactory: String) {
  146. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  147. self.println("///")
  148. self.printParameters()
  149. self.printRequestParameter()
  150. self.printCallOptionsParameter()
  151. self.println("/// - Returns: A `UnaryCall` with futures for the metadata, status and response.")
  152. self.printFunction(
  153. name: self.methodFunctionName,
  154. arguments: self.methodArguments,
  155. returnType: self.methodReturnType,
  156. access: self.access
  157. ) {
  158. self.println("return \(callFactory).makeUnaryCall(")
  159. self.withIndentation {
  160. self.println("path: \(self.methodPath),")
  161. self.println("request: request,")
  162. self.println("callOptions: callOptions")
  163. }
  164. self.println(")")
  165. }
  166. }
  167. private func printServerStreamingCall(callFactory: String) {
  168. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  169. self.println("///")
  170. self.printParameters()
  171. self.printRequestParameter()
  172. self.printCallOptionsParameter()
  173. self.printHandlerParameter()
  174. self.println("/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.")
  175. self.printFunction(
  176. name: self.methodFunctionName,
  177. arguments: self.methodArguments,
  178. returnType: self.methodReturnType,
  179. access: self.access
  180. ) {
  181. self.println("return \(callFactory).makeServerStreamingCall(") // path: \"/\(servicePath)/\(method.name)\",")
  182. self.withIndentation {
  183. self.println("path: \(self.methodPath),")
  184. self.println("request: request,")
  185. self.println("callOptions: callOptions,")
  186. self.println("handler: handler")
  187. }
  188. self.println(")")
  189. }
  190. }
  191. private func printClientStreamingCall(callFactory: String) {
  192. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  193. self.println("///")
  194. self.printClientStreamingDetails()
  195. self.println("///")
  196. self.printParameters()
  197. self.printCallOptionsParameter()
  198. self.println("/// - Returns: A `ClientStreamingCall` with futures for the metadata, status and response.")
  199. self.printFunction(
  200. name: self.methodFunctionName,
  201. arguments: self.methodArguments,
  202. returnType: self.methodReturnType,
  203. access: self.access
  204. ) {
  205. self.println("return \(callFactory).makeClientStreamingCall(")
  206. self.withIndentation {
  207. self.println("path: \(self.methodPath),")
  208. self.println("callOptions: callOptions")
  209. }
  210. self.println(")")
  211. }
  212. }
  213. private func printBidirectionalStreamingCall(callFactory: String) {
  214. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  215. self.println("///")
  216. self.printClientStreamingDetails()
  217. self.println("///")
  218. self.printParameters()
  219. self.printCallOptionsParameter()
  220. self.printHandlerParameter()
  221. self.println("/// - Returns: A `ClientStreamingCall` with futures for the metadata and status.")
  222. self.printFunction(
  223. name: self.methodFunctionName,
  224. arguments: self.methodArguments,
  225. returnType: self.methodReturnType,
  226. access: self.access
  227. ) {
  228. self.println("return \(callFactory).makeBidirectionalStreamingCall(")
  229. self.withIndentation {
  230. self.println("path: \(self.methodPath),")
  231. self.println("callOptions: callOptions,")
  232. self.println("handler: handler")
  233. }
  234. self.println(")")
  235. }
  236. }
  237. private func printClientStreamingDetails() {
  238. println("/// Callers should use the `send` method on the returned object to send messages")
  239. println("/// to the server. The caller should send an `.end` after the final message has been sent.")
  240. }
  241. private func printParameters() {
  242. println("/// - Parameters:")
  243. }
  244. private func printRequestParameter() {
  245. println("/// - request: Request to send to \(method.name).")
  246. }
  247. private func printCallOptionsParameter() {
  248. println("/// - callOptions: Call options.")
  249. }
  250. private func printHandlerParameter() {
  251. println("/// - handler: A closure called when each response is received from the server.")
  252. }
  253. }
  254. fileprivate extension Generator {
  255. var streamType: StreamingType {
  256. return streamingType(self.method)
  257. }
  258. }
  259. extension Generator {
  260. fileprivate var methodArguments: [String] {
  261. switch self.streamType {
  262. case .unary:
  263. return [
  264. "_ request: \(self.methodInputName)",
  265. "callOptions: CallOptions"
  266. ]
  267. case .serverStreaming:
  268. return [
  269. "_ request: \(self.methodInputName)",
  270. "callOptions: CallOptions",
  271. "handler: @escaping (\(methodOutputName)) -> Void"
  272. ]
  273. case .clientStreaming:
  274. return ["callOptions: CallOptions"]
  275. case .bidirectionalStreaming:
  276. return [
  277. "callOptions: CallOptions",
  278. "handler: @escaping (\(methodOutputName)) -> Void"
  279. ]
  280. }
  281. }
  282. fileprivate var methodArgumentsWithoutCallOptions: [String] {
  283. return self.methodArguments.filter {
  284. !$0.hasPrefix("callOptions: ")
  285. }
  286. }
  287. fileprivate var methodReturnType: String {
  288. switch self.streamType {
  289. case .unary:
  290. return "UnaryCall<\(self.methodInputName), \(self.methodOutputName)>"
  291. case .serverStreaming:
  292. return "ServerStreamingCall<\(self.methodInputName), \(self.methodOutputName)>"
  293. case .clientStreaming:
  294. return "ClientStreamingCall<\(self.methodInputName), \(self.methodOutputName)>"
  295. case .bidirectionalStreaming:
  296. return "BidirectionalStreamingCall<\(self.methodInputName), \(self.methodOutputName)>"
  297. }
  298. }
  299. }
  300. fileprivate extension StreamingType {
  301. var name: String {
  302. switch self {
  303. case .unary:
  304. return "Unary"
  305. case .clientStreaming:
  306. return "Client streaming"
  307. case .serverStreaming:
  308. return "Server streaming"
  309. case .bidirectionalStreaming:
  310. return "Bidirectional streaming"
  311. }
  312. }
  313. }
  314. extension MethodDescriptor {
  315. var documentation: String? {
  316. let comments = self.protoSourceComments(commentPrefix: "")
  317. return comments.isEmpty ? nil : comments
  318. }
  319. fileprivate func documentation(streamingType: StreamingType) -> String {
  320. let sourceComments = self.protoSourceComments()
  321. if sourceComments.isEmpty {
  322. return "/// \(streamingType.name) call to \(self.name)\n" // comments end with "\n" already.
  323. } else {
  324. return sourceComments // already prefixed with "///"
  325. }
  326. }
  327. }
  328. extension Array {
  329. /// Like `forEach` except that the `body` closure operates on all elements except for the last,
  330. /// and the `last` closure only operates on the last element.
  331. fileprivate func forEach(beforeLast body: (Element) -> (), onLast last: (Element) -> ()) {
  332. for element in self.dropLast() {
  333. body(element)
  334. }
  335. if let lastElement = self.last {
  336. last(lastElement)
  337. }
  338. }
  339. }