Skip to content

Commit 46a98e2

Browse files
authored
refactor "append" api (#3)
motivation: simplify registering items based on closures changes: * rename append to register * introduce a Handler object that is an enum of sorts for types of startup and shutdown handler signature we support * refactor tests to use the new code
1 parent ed61383 commit 46a98e2

File tree

6 files changed

+353
-226
lines changed

6 files changed

+353
-226
lines changed

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ let package = Package(
1515
],
1616
targets: [
1717
.target(name: "Lifecycle", dependencies: ["Logging", "Metrics", "Backtrace"]),
18-
.testTarget(name: "LifecycleTests", dependencies: ["Lifecycle", "NIO"]),
18+
.target(name: "LifecycleNIOCompat", dependencies: ["Lifecycle", "NIO"]),
19+
.testTarget(name: "LifecycleTests", dependencies: ["Lifecycle", "LifecycleNIOCompat"]),
1920
]
2021
)

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,17 @@ SwiftServiceLauncher is non-framework specific, designed to be integrated with a
1111
var lifecycle = Lifecycle()
1212

1313
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
14-
lifecycle.append(
14+
lifecycle.registerShutdown(
1515
name: "eventLoopGroup",
16-
shutdown: eventLoopGroup.syncShutdownGracefully
16+
eventLoopGroup.syncShutdownGracefully
1717
)
1818

1919
let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup))
20-
lifecycle.append(
20+
lifecycle.registerShutdown(
2121
name: "HTTPClient",
22-
shutdown: httpClient.shutdown
22+
httpClient.syncShutdown
2323
)
2424

25-
2625
lifecycle.start() { error in
2726
if let error = error {
2827
logger.error("failed starting \(self) ☠️: \(error)")

Sources/Lifecycle/Lifecycle.swift

Lines changed: 61 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -212,98 +212,16 @@ public class Lifecycle {
212212

213213
extension Lifecycle {
214214
internal struct Item: LifecycleItem {
215-
public let name: String
216-
private let _start: FunctionWithCallback?
217-
private let _shutdown: FunctionWithCallback
218-
219-
init(name: String, start: FunctionWithCallback?, shutdown: @escaping FunctionWithCallback) {
220-
self.name = name
221-
self._start = start
222-
self._shutdown = shutdown
223-
}
224-
225-
init(name: String, start: FunctionWithCallback?, shutdown: @escaping ThrowingFunction) {
226-
self.name = name
227-
self._start = start
228-
self._shutdown = Item.callback(shutdown)
229-
}
230-
231-
init<Future, Value>(name: String, start: FunctionWithCallback?, shutdown: @escaping FutureProvider<Future, Value>) {
232-
self.name = name
233-
self._start = start
234-
self._shutdown = Item.callback(shutdown)
235-
}
236-
237-
init(name: String, start: ThrowingFunction?, shutdown: @escaping FunctionWithCallback) {
238-
self.name = name
239-
self._start = start.map(Item.callback)
240-
self._shutdown = shutdown
241-
}
242-
243-
init(name: String, start: ThrowingFunction?, shutdown: @escaping ThrowingFunction) {
244-
self.name = name
245-
self._start = start.map(Item.callback)
246-
self._shutdown = Item.callback(shutdown)
247-
}
248-
249-
init<Future, Value>(name: String, start: ThrowingFunction?, shutdown: @escaping FutureProvider<Future, Value>) {
250-
self.name = name
251-
self._start = start.map(Item.callback)
252-
self._shutdown = Item.callback(shutdown)
253-
}
254-
255-
init<Future, Value>(name: String, start: FutureProvider<Future, Value>?, shutdown: @escaping FunctionWithCallback) {
256-
self.name = name
257-
self._start = start.map(Item.callback)
258-
self._shutdown = shutdown
259-
}
260-
261-
init<Future, Value>(name: String, start: FutureProvider<Future, Value>?, shutdown: @escaping ThrowingFunction) {
262-
self.name = name
263-
self._start = start.map(Item.callback)
264-
self._shutdown = Item.callback(shutdown)
265-
}
266-
267-
init<Future, Value>(name: String, start: FutureProvider<Future, Value>?, shutdown: @escaping FutureProvider<Future, Value>) {
268-
self.name = name
269-
self._start = start.map(Item.callback)
270-
self._shutdown = Item.callback(shutdown)
271-
}
215+
let name: String
216+
let start: Handler
217+
let shutdown: Handler
272218

273219
func start(callback: @escaping (Error?) -> Void) {
274-
if let body = self._start {
275-
body(callback)
276-
} else {
277-
callback(nil)
278-
}
220+
self.start.run(callback)
279221
}
280222

281223
func shutdown(callback: @escaping (Error?) -> Void) {
282-
self._shutdown(callback)
283-
}
284-
285-
private static func callback(_ body: @escaping ThrowingFunction) -> FunctionWithCallback {
286-
return { callback in
287-
do {
288-
try body()
289-
callback(nil)
290-
} catch {
291-
callback(error)
292-
}
293-
}
294-
}
295-
296-
private static func callback<Future, Value>(_ future: @escaping FutureProvider<Future, Value>) -> FunctionWithCallback {
297-
return { callback in
298-
future().whenComplete { result in
299-
switch result {
300-
case .success:
301-
callback(nil)
302-
case .failure(let error):
303-
callback(error)
304-
}
305-
}
306-
}
224+
self.shutdown.run(callback)
307225
}
308226
}
309227
}
@@ -334,23 +252,23 @@ public extension Lifecycle {
334252
///
335253
/// - parameters:
336254
/// - items: one or more `LifecycleItem`.
337-
internal func append(_ items: LifecycleItem...) {
255+
func register(_ items: [LifecycleItem]) {
338256
self.stateSemaphore.lock {
339257
guard case .idle = self.state else {
340258
preconditionFailure("invalid state, \(self.state)")
341259
}
342260
}
343261
self.itemsSemaphore.lock {
344-
items.forEach { self.items.append($0) }
262+
self.items.append(contentsOf: items)
345263
}
346264
}
347265

348266
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
349267
///
350268
/// - parameters:
351269
/// - items: one or more `LifecycleItem`.
352-
func append(_ items: [LifecycleItem]) {
353-
items.forEach { self.append($0) }
270+
internal func register(_ items: LifecycleItem...) {
271+
self.register(items)
354272
}
355273

356274
/// Add a `LifecycleItem` to a `LifecycleItems` collection.
@@ -359,128 +277,86 @@ public extension Lifecycle {
359277
/// - name: name of the item, useful for debugging.
360278
/// - start: closure to perform the startup.
361279
/// - shutdown: closure to perform the shutdown.
362-
func append(name: String, start: @escaping FunctionWithCallback, shutdown: @escaping FunctionWithCallback) {
363-
self.append(Item(name: name, start: start, shutdown: shutdown))
364-
}
365-
366-
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
367-
///
368-
/// - parameters:
369-
/// - name: name of the item, useful for debugging.
370-
/// - shutdown: closure to perform the shutdown.
371-
func append(name: String, shutdown: @escaping FunctionWithCallback) {
372-
self.append(Item(name: name, start: nil as FunctionWithCallback?, shutdown: shutdown))
373-
}
374-
375-
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
376-
///
377-
/// - parameters:
378-
/// - name: name of the item, useful for debugging.
379-
/// - start: closure to perform the startup.
380-
/// - shutdown: closure to perform the shutdown.
381-
func append(name: String, start: @escaping FunctionWithCallback, shutdown: @escaping ThrowingFunction) {
382-
self.append(Item(name: name, start: start, shutdown: shutdown))
383-
}
384-
385-
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
386-
///
387-
/// - parameters:
388-
/// - name: name of the item, useful for debugging.
389-
/// - shutdown: closure to perform the shutdown.
390-
func append(name: String, shutdown: @escaping ThrowingFunction) {
391-
self.append(Item(name: name, start: nil as ThrowingFunction?, shutdown: shutdown))
280+
func register(name: String, start: Handler, shutdown: Handler) {
281+
self.register(Item(name: name, start: start, shutdown: shutdown))
392282
}
393283

394284
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
395285
///
396286
/// - parameters:
397287
/// - name: name of the item, useful for debugging.
398-
/// - start: closure to perform the startup.
399288
/// - shutdown: closure to perform the shutdown.
400-
func append<Future, Value>(name: String, start: @escaping FunctionWithCallback, shutdown: @escaping FutureProvider<Future, Value>) {
401-
self.append(Item(name: name, start: start, shutdown: shutdown))
289+
func registerShutdown(name: String, _ handler: Handler) {
290+
self.register(name: name, start: .none, shutdown: handler)
402291
}
403292

404293
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
405294
///
406295
/// - parameters:
407296
/// - name: name of the item, useful for debugging.
297+
/// - start: closure to perform the shutdown.
408298
/// - shutdown: closure to perform the shutdown.
409-
func append<Future, Value>(name: String, shutdown: @escaping FutureProvider<Future, Value>) {
410-
self.append(Item(name: name, start: nil as FutureProvider<Future, Value>?, shutdown: shutdown))
299+
func register(name: String, start: @escaping () throws -> Void, shutdown: @escaping () throws -> Void) {
300+
self.register(name: name, start: .sync(start), shutdown: .sync(shutdown))
411301
}
412302

413303
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
414304
///
415305
/// - parameters:
416306
/// - name: name of the item, useful for debugging.
417-
/// - start: closure to perform the startup.
418307
/// - shutdown: closure to perform the shutdown.
419-
func append(name: String, start: @escaping ThrowingFunction, shutdown: @escaping FunctionWithCallback) {
420-
self.append(Item(name: name, start: start, shutdown: shutdown))
421-
}
422-
423-
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
424-
///
425-
/// - parameters:
426-
/// - name: name of the item, useful for debugging.
427-
/// - shutdown: closure to perform the shutdown.
428-
func append(name: String, start: @escaping ThrowingFunction, shutdown: @escaping ThrowingFunction) {
429-
self.append(Item(name: name, start: start, shutdown: shutdown))
430-
}
431-
432-
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
433-
///
434-
/// - parameters:
435-
/// - name: name of the item, useful for debugging.
436-
/// - start: closure to perform the startup.
437-
/// - shutdown: closure to perform the shutdown.
438-
func append<Future, Value>(name: String, start: @escaping ThrowingFunction, shutdown: @escaping FutureProvider<Future, Value>) {
439-
self.append(Item(name: name, start: start, shutdown: shutdown))
440-
}
441-
442-
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
443-
///
444-
/// - parameters:
445-
/// - name: name of the item, useful for debugging.
446-
/// - shutdown: closure to perform the shutdown.
447-
func append<Future, Value>(name: String, start: @escaping FutureProvider<Future, Value>, shutdown: @escaping FunctionWithCallback) {
448-
self.append(Item(name: name, start: start, shutdown: shutdown))
449-
}
450-
451-
/// Add a `LifecycleItem` to a `LifecycleItems` collection.
452-
///
453-
/// - parameters:
454-
/// - name: name of the item, useful for debugging.
455-
/// - start: closure to perform the startup.
456-
/// - shutdown: closure to perform the shutdown.
457-
func append<Future, Value>(name: String, start: @escaping FutureProvider<Future, Value>, shutdown: @escaping ThrowingFunction) {
458-
self.append(Item(name: name, start: start, shutdown: shutdown))
459-
}
460-
461-
/// Adds a `LifecycleItem` to a `LifecycleItems` collection.
462-
///
463-
/// - parameters:
464-
/// - name: name of the item, useful for debugging.
465-
/// - start: closure to perform the startup.
466-
/// - shutdown: closure to perform the shutdown.
467-
func append<Future, Value>(name: String, start: @escaping FutureProvider<Future, Value>, shutdown: @escaping FutureProvider<Future, Value>) {
468-
self.append(Item(name: name, start: start, shutdown: shutdown))
308+
func registerShutdown(name: String, _ handler: @escaping () throws -> Void) {
309+
self.register(name: name, start: .none, shutdown: .sync(handler))
469310
}
470311
}
471312

472313
/// Supported startup and shutdown method styles
473-
474314
public extension Lifecycle {
475-
typealias FunctionWithCallback = (@escaping (Error?) -> Void) -> Void
476-
typealias ThrowingFunction = () throws -> Void
477-
typealias FutureProvider<Future: LifecycleFuture, Value> = () -> Future where Future.Value == Value
478-
}
315+
struct Handler {
316+
private let body: (@escaping (Error?) -> Void) -> Void
317+
318+
/// Initialize a `Lifecycle.Handler` based on a completion handler.
319+
///
320+
/// - parameters:
321+
/// - callback: the underlying completion handler
322+
public init(_ callback: @escaping (@escaping (Error?) -> Void) -> Void) {
323+
self.body = callback
324+
}
479325

480-
public protocol LifecycleFuture {
481-
associatedtype Value
326+
/// Asynchronous `Lifecycle.Handler` based on a completion handler.
327+
///
328+
/// - parameters:
329+
/// - callback: the underlying completion handler
330+
public static func async(_ callback: @escaping (@escaping (Error?) -> Void) -> Void) -> Handler {
331+
return Handler(callback)
332+
}
482333

483-
func whenComplete(_ callback: @escaping (Result<Value, Error>) -> Void)
334+
/// Asynchronous `Lifecycle.Handler` based on a blocking, throwing function.
335+
///
336+
/// - parameters:
337+
/// - body: the underlying function
338+
public static func sync(_ body: @escaping () throws -> Void) -> Handler {
339+
return Handler { completionHandler in
340+
do {
341+
try body()
342+
completionHandler(nil)
343+
} catch {
344+
completionHandler(error)
345+
}
346+
}
347+
}
348+
349+
/// Noop `Lifecycle.Handler`.
350+
public static var none: Handler {
351+
return Handler { callback in
352+
callback(nil)
353+
}
354+
}
355+
356+
internal func run(_ callback: @escaping (Error?) -> Void) {
357+
self.body(callback)
358+
}
359+
}
484360
}
485361

486362
/// Represents an item that can be started and shut down
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftServiceLauncher open source project
4+
//
5+
// Copyright (c) 2019-2020 Apple Inc. and the SwiftServiceLauncher project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftServiceLauncher project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Lifecycle
16+
import NIO
17+
18+
extension Lifecycle.Handler {
19+
/// Asynchronous `Lifecycle.Handler` based on an `EventLoopFuture`.
20+
///
21+
/// - parameters:
22+
/// - future: the underlying `EventLoopFuture`
23+
public static func async(_ future: @escaping () -> EventLoopFuture<Void>) -> Lifecycle.Handler {
24+
return Lifecycle.Handler { callback in
25+
future().whenComplete { result in
26+
switch result {
27+
case .success:
28+
callback(nil)
29+
case .failure(let error):
30+
callback(error)
31+
}
32+
}
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)