Generator-Client.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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. if self.options.generateClient {
  22. self.println()
  23. self.printServiceClientProtocol()
  24. self.println()
  25. self.printClientProtocolExtension()
  26. self.println()
  27. self.printClassBackedServiceClientImplementation()
  28. self.println()
  29. self.printStructBackedServiceClientImplementation()
  30. self.println()
  31. self.printAsyncServiceClientProtocol()
  32. self.println()
  33. self.printAsyncClientProtocolExtension()
  34. self.println()
  35. self.printAsyncClientProtocolSafeWrappersExtension()
  36. self.println()
  37. self.printAsyncServiceClientImplementation()
  38. self.println()
  39. // Both implementations share definitions for interceptors and metadata.
  40. self.printServiceClientInterceptorFactoryProtocol()
  41. self.println()
  42. self.printClientMetadata()
  43. }
  44. if self.options.generateTestClient {
  45. self.println()
  46. self.printTestClient()
  47. }
  48. }
  49. internal func printFunction(
  50. name: String,
  51. arguments: [String],
  52. returnType: String?,
  53. access: String? = nil,
  54. sendable: Bool = false,
  55. async: Bool = false,
  56. throws: Bool = false,
  57. genericWhereClause: String? = nil,
  58. bodyBuilder: (() -> Void)?
  59. ) {
  60. // Add a space after access, if it exists.
  61. let functionHead = (access.map { $0 + " " } ?? "") + (sendable ? "@Sendable " : "")
  62. let `return` = returnType.map { " -> " + $0 } ?? ""
  63. let genericWhere = genericWhereClause.map { " " + $0 } ?? ""
  64. let asyncThrows: String
  65. switch (async, `throws`) {
  66. case (true, true):
  67. asyncThrows = " async throws"
  68. case (true, false):
  69. asyncThrows = " async"
  70. case (false, true):
  71. asyncThrows = " throws"
  72. case (false, false):
  73. asyncThrows = ""
  74. }
  75. let hasBody = bodyBuilder != nil
  76. if arguments.isEmpty {
  77. // Don't bother splitting across multiple lines if there are no arguments.
  78. self.println(
  79. "\(functionHead)func \(name)()\(asyncThrows)\(`return`)\(genericWhere)",
  80. newline: !hasBody
  81. )
  82. } else {
  83. self.println("\(functionHead)func \(name)(")
  84. self.withIndentation {
  85. // Add a comma after each argument except the last.
  86. arguments.forEach(beforeLast: {
  87. self.println($0 + ",")
  88. }, onLast: {
  89. self.println($0)
  90. })
  91. }
  92. self.println(")\(asyncThrows)\(`return`)\(genericWhere)", newline: !hasBody)
  93. }
  94. if let bodyBuilder = bodyBuilder {
  95. self.println(" {")
  96. self.withIndentation {
  97. bodyBuilder()
  98. }
  99. self.println("}")
  100. }
  101. }
  102. private func printServiceClientProtocol() {
  103. let comments = self.service.protoSourceComments()
  104. if !comments.isEmpty {
  105. // Source comments already have the leading '///'
  106. self.println(comments, newline: false)
  107. self.println("///")
  108. }
  109. self.println(
  110. "/// Usage: instantiate `\(self.clientClassName)`, then call methods of this protocol to make API calls."
  111. )
  112. self.println("\(self.access) protocol \(self.clientProtocolName): GRPCClient {")
  113. self.withIndentation {
  114. self.println("var serviceName: String { get }")
  115. self.println("var interceptors: \(self.clientInterceptorProtocolName)? { get }")
  116. for method in service.methods {
  117. self.println()
  118. self.method = method
  119. self.printFunction(
  120. name: self.methodFunctionName,
  121. arguments: self.methodArgumentsWithoutDefaults,
  122. returnType: self.methodReturnType,
  123. bodyBuilder: nil
  124. )
  125. }
  126. }
  127. println("}")
  128. }
  129. private func printClientProtocolExtension() {
  130. self.println("extension \(self.clientProtocolName) {")
  131. self.withIndentation {
  132. // Service name.
  133. self.println("\(self.access) var serviceName: String {")
  134. self.withIndentation {
  135. self.println("return \"\(self.servicePath)\"")
  136. }
  137. self.println("}")
  138. // Default method implementations.
  139. self.printMethods()
  140. }
  141. self.println("}")
  142. }
  143. private func printServiceClientInterceptorFactoryProtocol() {
  144. self.println("\(self.access) protocol \(self.clientInterceptorProtocolName): Sendable {")
  145. self.withIndentation {
  146. // Method specific interceptors.
  147. for method in service.methods {
  148. self.println()
  149. self.method = method
  150. self.println(
  151. "/// - Returns: Interceptors to use when invoking '\(self.methodFunctionName)'."
  152. )
  153. // Skip the access, we're defining a protocol.
  154. self.printMethodInterceptorFactory(access: nil)
  155. }
  156. }
  157. self.println("}")
  158. }
  159. private func printMethodInterceptorFactory(
  160. access: String?,
  161. bodyBuilder: (() -> Void)? = nil
  162. ) {
  163. self.printFunction(
  164. name: self.methodInterceptorFactoryName,
  165. arguments: [],
  166. returnType: "[ClientInterceptor<\(self.methodInputName), \(self.methodOutputName)>]",
  167. access: access,
  168. bodyBuilder: bodyBuilder
  169. )
  170. }
  171. private func printClassBackedServiceClientImplementation() {
  172. self.println("@available(*, deprecated)")
  173. self.println("extension \(clientClassName): @unchecked Sendable {}")
  174. self.println()
  175. self.println("@available(*, deprecated, renamed: \"\(clientStructName)\")")
  176. println("\(access) final class \(clientClassName): \(clientProtocolName) {")
  177. self.withIndentation {
  178. println("private let lock = Lock()")
  179. println("private var _defaultCallOptions: CallOptions")
  180. println("private var _interceptors: \(clientInterceptorProtocolName)?")
  181. println("\(access) let channel: GRPCChannel")
  182. println("\(access) var defaultCallOptions: CallOptions {")
  183. self.withIndentation {
  184. println("get { self.lock.withLock { return self._defaultCallOptions } }")
  185. println("set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }")
  186. }
  187. self.println("}")
  188. println("\(access) var interceptors: \(clientInterceptorProtocolName)? {")
  189. self.withIndentation {
  190. println("get { self.lock.withLock { return self._interceptors } }")
  191. println("set { self.lock.withLockVoid { self._interceptors = newValue } }")
  192. }
  193. println("}")
  194. println()
  195. println("/// Creates a client for the \(servicePath) service.")
  196. println("///")
  197. self.printParameters()
  198. println("/// - channel: `GRPCChannel` to the service host.")
  199. println(
  200. "/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them."
  201. )
  202. println("/// - interceptors: A factory providing interceptors for each RPC.")
  203. println("\(access) init(")
  204. self.withIndentation {
  205. println("channel: GRPCChannel,")
  206. println("defaultCallOptions: CallOptions = CallOptions(),")
  207. println("interceptors: \(clientInterceptorProtocolName)? = nil")
  208. }
  209. self.println(") {")
  210. self.withIndentation {
  211. println("self.channel = channel")
  212. println("self._defaultCallOptions = defaultCallOptions")
  213. println("self._interceptors = interceptors")
  214. }
  215. self.println("}")
  216. }
  217. println("}")
  218. }
  219. private func printStructBackedServiceClientImplementation() {
  220. println("\(access) struct \(clientStructName): \(clientProtocolName) {")
  221. self.withIndentation {
  222. println("\(access) var channel: GRPCChannel")
  223. println("\(access) var defaultCallOptions: CallOptions")
  224. println("\(access) var interceptors: \(clientInterceptorProtocolName)?")
  225. println()
  226. println("/// Creates a client for the \(servicePath) service.")
  227. println("///")
  228. self.printParameters()
  229. println("/// - channel: `GRPCChannel` to the service host.")
  230. println(
  231. "/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them."
  232. )
  233. println("/// - interceptors: A factory providing interceptors for each RPC.")
  234. println("\(access) init(")
  235. self.withIndentation {
  236. println("channel: GRPCChannel,")
  237. println("defaultCallOptions: CallOptions = CallOptions(),")
  238. println("interceptors: \(clientInterceptorProtocolName)? = nil")
  239. }
  240. self.println(") {")
  241. self.withIndentation {
  242. println("self.channel = channel")
  243. println("self.defaultCallOptions = defaultCallOptions")
  244. println("self.interceptors = interceptors")
  245. }
  246. self.println("}")
  247. }
  248. println("}")
  249. }
  250. private func printMethods() {
  251. for method in self.service.methods {
  252. self.println()
  253. self.method = method
  254. switch self.streamType {
  255. case .unary:
  256. self.printUnaryCall()
  257. case .serverStreaming:
  258. self.printServerStreamingCall()
  259. case .clientStreaming:
  260. self.printClientStreamingCall()
  261. case .bidirectionalStreaming:
  262. self.printBidirectionalStreamingCall()
  263. }
  264. }
  265. }
  266. private func printUnaryCall() {
  267. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  268. self.println("///")
  269. self.printParameters()
  270. self.printRequestParameter()
  271. self.printCallOptionsParameter()
  272. self.println("/// - Returns: A `UnaryCall` with futures for the metadata, status and response.")
  273. self.printFunction(
  274. name: self.methodFunctionName,
  275. arguments: self.methodArguments,
  276. returnType: self.methodReturnType,
  277. access: self.access
  278. ) {
  279. self.println("return self.makeUnaryCall(")
  280. self.withIndentation {
  281. self.println("path: \(self.methodPathUsingClientMetadata),")
  282. self.println("request: request,")
  283. self.println("callOptions: callOptions ?? self.defaultCallOptions,")
  284. self.println(
  285. "interceptors: self.interceptors?.\(self.methodInterceptorFactoryName)() ?? []"
  286. )
  287. }
  288. self.println(")")
  289. }
  290. }
  291. private func printServerStreamingCall() {
  292. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  293. self.println("///")
  294. self.printParameters()
  295. self.printRequestParameter()
  296. self.printCallOptionsParameter()
  297. self.printHandlerParameter()
  298. self.println("/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.")
  299. self.printFunction(
  300. name: self.methodFunctionName,
  301. arguments: self.methodArguments,
  302. returnType: self.methodReturnType,
  303. access: self.access
  304. ) {
  305. self.println("return self.makeServerStreamingCall(")
  306. self.withIndentation {
  307. self.println("path: \(self.methodPathUsingClientMetadata),")
  308. self.println("request: request,")
  309. self.println("callOptions: callOptions ?? self.defaultCallOptions,")
  310. self.println(
  311. "interceptors: self.interceptors?.\(self.methodInterceptorFactoryName)() ?? [],"
  312. )
  313. self.println("handler: handler")
  314. }
  315. self.println(")")
  316. }
  317. }
  318. private func printClientStreamingCall() {
  319. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  320. self.println("///")
  321. self.printClientStreamingDetails()
  322. self.println("///")
  323. self.printParameters()
  324. self.printCallOptionsParameter()
  325. self
  326. .println(
  327. "/// - Returns: A `ClientStreamingCall` with futures for the metadata, status and response."
  328. )
  329. self.printFunction(
  330. name: self.methodFunctionName,
  331. arguments: self.methodArguments,
  332. returnType: self.methodReturnType,
  333. access: self.access
  334. ) {
  335. self.println("return self.makeClientStreamingCall(")
  336. self.withIndentation {
  337. self.println("path: \(self.methodPathUsingClientMetadata),")
  338. self.println("callOptions: callOptions ?? self.defaultCallOptions,")
  339. self.println(
  340. "interceptors: self.interceptors?.\(self.methodInterceptorFactoryName)() ?? []"
  341. )
  342. }
  343. self.println(")")
  344. }
  345. }
  346. private func printBidirectionalStreamingCall() {
  347. self.println(self.method.documentation(streamingType: self.streamType), newline: false)
  348. self.println("///")
  349. self.printClientStreamingDetails()
  350. self.println("///")
  351. self.printParameters()
  352. self.printCallOptionsParameter()
  353. self.printHandlerParameter()
  354. self.println("/// - Returns: A `ClientStreamingCall` with futures for the metadata and status.")
  355. self.printFunction(
  356. name: self.methodFunctionName,
  357. arguments: self.methodArguments,
  358. returnType: self.methodReturnType,
  359. access: self.access
  360. ) {
  361. self.println("return self.makeBidirectionalStreamingCall(")
  362. self.withIndentation {
  363. self.println("path: \(self.methodPathUsingClientMetadata),")
  364. self.println("callOptions: callOptions ?? self.defaultCallOptions,")
  365. self.println(
  366. "interceptors: self.interceptors?.\(self.methodInterceptorFactoryName)() ?? [],"
  367. )
  368. self.println("handler: handler")
  369. }
  370. self.println(")")
  371. }
  372. }
  373. private func printClientStreamingDetails() {
  374. println("/// Callers should use the `send` method on the returned object to send messages")
  375. println(
  376. "/// to the server. The caller should send an `.end` after the final message has been sent."
  377. )
  378. }
  379. private func printParameters() {
  380. println("/// - Parameters:")
  381. }
  382. private func printRequestParameter() {
  383. println("/// - request: Request to send to \(method.name).")
  384. }
  385. private func printCallOptionsParameter() {
  386. println("/// - callOptions: Call options.")
  387. }
  388. private func printHandlerParameter() {
  389. println("/// - handler: A closure called when each response is received from the server.")
  390. }
  391. }
  392. extension Generator {
  393. private func printFakeResponseStreams() {
  394. for method in self.service.methods {
  395. self.println()
  396. self.method = method
  397. switch self.streamType {
  398. case .unary, .clientStreaming:
  399. self.printUnaryResponse()
  400. case .serverStreaming, .bidirectionalStreaming:
  401. self.printStreamingResponse()
  402. }
  403. }
  404. }
  405. private func printUnaryResponse() {
  406. self.printResponseStream(isUnary: true)
  407. self.println()
  408. self.printEnqueueUnaryResponse(isUnary: true)
  409. self.println()
  410. self.printHasResponseStreamEnqueued()
  411. }
  412. private func printStreamingResponse() {
  413. self.printResponseStream(isUnary: false)
  414. self.println()
  415. self.printEnqueueUnaryResponse(isUnary: false)
  416. self.println()
  417. self.printHasResponseStreamEnqueued()
  418. }
  419. private func printEnqueueUnaryResponse(isUnary: Bool) {
  420. let name: String
  421. let responseArg: String
  422. let responseArgAndType: String
  423. if isUnary {
  424. name = "enqueue\(self.method.name)Response"
  425. responseArg = "response"
  426. responseArgAndType = "_ \(responseArg): \(self.methodOutputName)"
  427. } else {
  428. name = "enqueue\(self.method.name)Responses"
  429. responseArg = "responses"
  430. responseArgAndType = "_ \(responseArg): [\(self.methodOutputName)]"
  431. }
  432. self.printFunction(
  433. name: name,
  434. arguments: [
  435. responseArgAndType,
  436. "_ requestHandler: @escaping (FakeRequestPart<\(self.methodInputName)>) -> () = { _ in }",
  437. ],
  438. returnType: nil,
  439. access: self.access
  440. ) {
  441. self.println("let stream = self.make\(self.method.name)ResponseStream(requestHandler)")
  442. if isUnary {
  443. self.println("// This is the only operation on the stream; try! is fine.")
  444. self.println("try! stream.sendMessage(\(responseArg))")
  445. } else {
  446. self.println("// These are the only operation on the stream; try! is fine.")
  447. self.println("\(responseArg).forEach { try! stream.sendMessage($0) }")
  448. self.println("try! stream.sendEnd()")
  449. }
  450. }
  451. }
  452. private func printResponseStream(isUnary: Bool) {
  453. let type = isUnary ? "FakeUnaryResponse" : "FakeStreamingResponse"
  454. let factory = isUnary ? "makeFakeUnaryResponse" : "makeFakeStreamingResponse"
  455. self
  456. .println(
  457. "/// Make a \(isUnary ? "unary" : "streaming") response for the \(self.method.name) RPC. This must be called"
  458. )
  459. self.println("/// before calling '\(self.methodFunctionName)'. See also '\(type)'.")
  460. self.println("///")
  461. self.println("/// - Parameter requestHandler: a handler for request parts sent by the RPC.")
  462. self.printFunction(
  463. name: "make\(self.method.name)ResponseStream",
  464. arguments: [
  465. "_ requestHandler: @escaping (FakeRequestPart<\(self.methodInputName)>) -> () = { _ in }",
  466. ],
  467. returnType: "\(type)<\(self.methodInputName), \(self.methodOutputName)>",
  468. access: self.access
  469. ) {
  470. self
  471. .println(
  472. "return self.fakeChannel.\(factory)(path: \(self.methodPathUsingClientMetadata), requestHandler: requestHandler)"
  473. )
  474. }
  475. }
  476. private func printHasResponseStreamEnqueued() {
  477. self
  478. .println("/// Returns true if there are response streams enqueued for '\(self.method.name)'")
  479. self.println("\(self.access) var has\(self.method.name)ResponsesRemaining: Bool {")
  480. self.withIndentation {
  481. self.println(
  482. "return self.fakeChannel.hasFakeResponseEnqueued(forPath: \(self.methodPathUsingClientMetadata))"
  483. )
  484. }
  485. self.println("}")
  486. }
  487. private func printTestClient() {
  488. self.println("@available(swift, deprecated: 5.6)")
  489. self.println("extension \(self.testClientClassName): @unchecked Sendable {}")
  490. self.println()
  491. self.println(
  492. "@available(swift, deprecated: 5.6, message: \"Test clients are not Sendable "
  493. + "but the 'GRPCClient' API requires clients to be Sendable. Using a localhost client and "
  494. + "server is the recommended alternative.\")"
  495. )
  496. self.println(
  497. "\(self.access) final class \(self.testClientClassName): \(self.clientProtocolName) {"
  498. )
  499. self.withIndentation {
  500. self.println("private let fakeChannel: FakeChannel")
  501. self.println("\(access) var defaultCallOptions: CallOptions")
  502. self.println("\(access) var interceptors: \(clientInterceptorProtocolName)?")
  503. self.println()
  504. self.println("\(self.access) var channel: GRPCChannel {")
  505. self.withIndentation {
  506. self.println("return self.fakeChannel")
  507. }
  508. self.println("}")
  509. self.println()
  510. self.println("\(self.access) init(")
  511. self.withIndentation {
  512. self.println("fakeChannel: FakeChannel = FakeChannel(),")
  513. self.println("defaultCallOptions callOptions: CallOptions = CallOptions(),")
  514. self.println("interceptors: \(clientInterceptorProtocolName)? = nil")
  515. }
  516. self.println(") {")
  517. self.withIndentation {
  518. self.println("self.fakeChannel = fakeChannel")
  519. self.println("self.defaultCallOptions = callOptions")
  520. self.println("self.interceptors = interceptors")
  521. }
  522. self.println("}")
  523. self.printFakeResponseStreams()
  524. }
  525. self.println("}") // end class
  526. }
  527. }
  528. extension Generator {
  529. private var streamType: StreamingType {
  530. return streamingType(self.method)
  531. }
  532. }
  533. extension Generator {
  534. private var methodArguments: [String] {
  535. switch self.streamType {
  536. case .unary:
  537. return [
  538. "_ request: \(self.methodInputName)",
  539. "callOptions: CallOptions? = nil",
  540. ]
  541. case .serverStreaming:
  542. return [
  543. "_ request: \(self.methodInputName)",
  544. "callOptions: CallOptions? = nil",
  545. "handler: @escaping (\(methodOutputName)) -> Void",
  546. ]
  547. case .clientStreaming:
  548. return ["callOptions: CallOptions? = nil"]
  549. case .bidirectionalStreaming:
  550. return [
  551. "callOptions: CallOptions? = nil",
  552. "handler: @escaping (\(methodOutputName)) -> Void",
  553. ]
  554. }
  555. }
  556. private var methodArgumentsWithoutDefaults: [String] {
  557. return self.methodArguments.map { arg in
  558. // Remove default arg from call options.
  559. if arg == "callOptions: CallOptions? = nil" {
  560. return "callOptions: CallOptions?"
  561. } else {
  562. return arg
  563. }
  564. }
  565. }
  566. private var methodArgumentsWithoutCallOptions: [String] {
  567. return self.methodArguments.filter {
  568. !$0.hasPrefix("callOptions: ")
  569. }
  570. }
  571. private var methodReturnType: String {
  572. switch self.streamType {
  573. case .unary:
  574. return "UnaryCall<\(self.methodInputName), \(self.methodOutputName)>"
  575. case .serverStreaming:
  576. return "ServerStreamingCall<\(self.methodInputName), \(self.methodOutputName)>"
  577. case .clientStreaming:
  578. return "ClientStreamingCall<\(self.methodInputName), \(self.methodOutputName)>"
  579. case .bidirectionalStreaming:
  580. return "BidirectionalStreamingCall<\(self.methodInputName), \(self.methodOutputName)>"
  581. }
  582. }
  583. }
  584. extension StreamingType {
  585. fileprivate var name: String {
  586. switch self {
  587. case .unary:
  588. return "Unary"
  589. case .clientStreaming:
  590. return "Client streaming"
  591. case .serverStreaming:
  592. return "Server streaming"
  593. case .bidirectionalStreaming:
  594. return "Bidirectional streaming"
  595. }
  596. }
  597. }
  598. extension MethodDescriptor {
  599. var documentation: String? {
  600. let comments = self.protoSourceComments(commentPrefix: "")
  601. return comments.isEmpty ? nil : comments
  602. }
  603. fileprivate func documentation(streamingType: StreamingType) -> String {
  604. let sourceComments = self.protoSourceComments()
  605. if sourceComments.isEmpty {
  606. return "/// \(streamingType.name) call to \(self.name)\n" // comments end with "\n" already.
  607. } else {
  608. return sourceComments // already prefixed with "///"
  609. }
  610. }
  611. }
  612. extension Array {
  613. /// Like `forEach` except that the `body` closure operates on all elements except for the last,
  614. /// and the `last` closure only operates on the last element.
  615. fileprivate func forEach(beforeLast body: (Element) -> Void, onLast last: (Element) -> Void) {
  616. for element in self.dropLast() {
  617. body(element)
  618. }
  619. if let lastElement = self.last {
  620. last(lastElement)
  621. }
  622. }
  623. }