Skip to content

Instantly share code, notes, and snippets.

@tonyarnold
Created March 14, 2020 12:13
Show Gist options
  • Save tonyarnold/0fe81541cd780f9d34e83028583e487c to your computer and use it in GitHub Desktop.
Save tonyarnold/0fe81541cd780f9d34e83028583e487c to your computer and use it in GitHub Desktop.
import Vapor
extension EventLoopFuture {
/// When the current `EventLoopFuture<Value>` is fulfilled, run the provided callback,
/// which will provide a new `EventLoopFuture`.
///
/// This allows you to dynamically dispatch new asynchronous tasks as phases in a
/// longer series of processing steps. Note that you can use the results of the
/// current `EventLoopFuture<Value>` when determining how to dispatch the next operation.
///
/// This works well when you have APIs that already know how to return `EventLoopFuture`s.
/// You can do something with the result of one and just return the next future:
///
/// ```
/// let d1 = networkRequest(args).future()
/// let d2 = d1.flatMap { t -> EventLoopFuture<NewValue> in
/// . . . something with t . . .
/// return netWorkRequest(args)
/// }
/// d2.whenSuccess { u in
/// NSLog("Result of second request: \(u)")
/// }
/// ```
///
/// Note: In a sense, the `EventLoopFuture<NewValue>` is returned before it's created.
///
/// - parameters:
/// - callback: Function that will receive the value of this `EventLoopFuture` and return
/// a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value.
@inlinable
public func then<NewValue>(
file: StaticString = #file,
line: UInt = #line, _
callback: @escaping (Value) -> EventLoopFuture<NewValue>
) -> EventLoopFuture<NewValue> {
return flatMap(file: file, line: line, callback)
}
/// When the current `EventLoopFuture<Value>` is fulfilled, run the provided callback, which
/// performs a synchronous computation and returns a new value of type `NewValue`. The provided
/// callback may optionally `throw`.
///
/// Operations performed in `then` should not block, or they will block the entire
/// event loop. `then` is intended for use when you have a data-driven function that
/// performs a simple data transformation that can potentially error.
///
/// If your callback function throws, the returned `EventLoopFuture` will error.
///
/// - parameters:
/// - callback: Function that will receive the value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value.
@inlinable
public func then<NewValue>(
file: StaticString = #file,
line: UInt = #line,
_ callback: @escaping (Value) throws -> NewValue
) -> EventLoopFuture<NewValue> {
return self.flatMapThrowing(file: file, line: line, callback)
}
/// When the current `EventLoopFuture<Value>` is in an error state, run the provided callback, which
/// may recover from the error and returns a new value of type `Value`. The provided callback may optionally `throw`,
/// in which case the `EventLoopFuture` will be in a failed state with the new thrown error.
///
/// Operations performed in `then` should not block, or they will block the entire
/// event loop. `then` is intended for use when you have the ability to synchronously
/// recover from errors.
///
/// If your callback function throws, the returned `EventLoopFuture` will error.
///
/// - parameters:
/// - callback: Function that will receive the error value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value or a rethrown error.
@inlinable
public func then(
file: StaticString = #file,
line: UInt = #line,
_ callback: @escaping (Error) throws -> Value
) -> EventLoopFuture<Value> {
return self.flatMapErrorThrowing(file: file, line: line, callback)
}
/// When the current `EventLoopFuture<Value>` is fulfilled, run the provided callback, which
/// performs a synchronous computation and returns a new value of type `NewValue`.
///
/// Operations performed in `then` should not block, or they will block the entire event
/// loop. `then` is intended for use when you have a data-driven function that performs
/// a simple data transformation that cannot error.
///
/// If you have a data-driven function that can throw, you should use `then`
/// instead.
///
/// ```
/// let future1 = eventually()
/// let future2 = future1.then { T -> U in
/// ... stuff ...
/// return u
/// }
/// let future3 = future2.then { U -> V in
/// ... stuff ...
/// return v
/// }
/// ```
///
/// - parameters:
/// - callback: Function that will receive the value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value.
@inlinable
public func then<NewValue>(
file: StaticString = #file,
line: UInt = #line,
_ callback: @escaping (Value) -> (NewValue)
) -> EventLoopFuture<NewValue> {
return map(file: file, line: line, callback)
}
/// When the current `EventLoopFuture<Value>` is in an error state, run the provided callback, which
/// may recover from the error by returning an `EventLoopFuture<NewValue>`. The callback is intended to potentially
/// recover from the error by returning a new `EventLoopFuture` that will eventually contain the recovered
/// result.
///
/// If the callback cannot recover it should return a failed `EventLoopFuture`.
///
/// - parameters:
/// - callback: Function that will receive the error value of this `EventLoopFuture` and return
/// a new value lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the recovered value.
@inlinable
public func then(
file: StaticString = #file,
line: UInt = #line,
_ callback: @escaping (Error) -> EventLoopFuture<Value>
) -> EventLoopFuture<Value> {
return flatMapError(file: file, line: line, callback)
}
/// When the current `EventLoopFuture<Value>` is fulfilled, run the provided callback, which
/// performs a synchronous computation and returns either a new value (of type `NewValue`) or
/// an error depending on the `Result` returned by the closure.
///
/// Operations performed in `flatMapResult` should not block, or they will block the entire
/// event loop. `flatMapResult` is intended for use when you have a data-driven function that
/// performs a simple data transformation that can potentially error.
///
///
/// - parameters:
/// - body: Function that will receive the value of this `EventLoopFuture` and return
/// a new value or error lifted into a new `EventLoopFuture`.
/// - returns: A future that will receive the eventual value.
@inlinable
public func then<NewValue, SomeError: Error>(
file: StaticString = #file,
line: UInt = #line,
_ body: @escaping (Value) -> Result<NewValue, SomeError>
) -> EventLoopFuture<NewValue> {
return self.then(file: file, line: line) { value in
switch body(value) {
case let .success(value):
return self.eventLoop.makeSucceededFuture(value, file: file, line: line)
case let .failure(error):
return self.eventLoop.makeFailedFuture(error, file: file, line: line)
}
}
}
/// Calls the supplied closure if the chained Future resolves to a specific Error type.
///
/// The closure gives you a chance to rectify the error (returning the desired expectation) or to re-throw or throw a different error.
///
/// The callback expects a non-Future return (if not throwing instead). See `then` for a Future return.
///
/// - Parameter errorType: The `Error` type to be promoted.
/// - Returns: A `EventLoopFuture` that yields either a successful value or an error.
public func then<E: Error>(
_ errorType: E.Type,
file: StaticString = #file,
line: UInt = #line,
transform: @escaping (E) throws -> Value
) -> EventLoopFuture<Value> {
return then(file: file, line: line) { error -> Value in
guard let caughtError = error as? E else {
throw error
}
return try transform(caughtError)
}
}
/// Calls the supplied closure if the chained Future resolves to a specific Error type.
///
/// The closure gives you a chance to rectify the error (returning the desired expectation) or to re-throw or throw a different error.
///
/// The callback expects a Future return (if not throwing instead). See `catchMap` for a non-Future return.
///
/// - Parameter errorType: The `Error` type to be promoted.
/// - Returns: A `EventLoopFuture` that yields either a successful value or an error.
public func then<E: Error>(
_ errorType: E.Type,
file: StaticString = #file,
line: UInt = #line,
transform: @escaping (E) throws -> EventLoopFuture<Value>
) -> EventLoopFuture<Value> where Value: Error {
return then(file: file, line: line) { error -> EventLoopFuture<Value> in
guard let caughtError = error as? E else {
return self.eventLoop.makeFailedFuture(error)
}
do {
return try transform(caughtError)
} catch let innerError {
return self.eventLoop.makeFailedFuture(innerError)
}
}
}
func then<NewValue>(
file: StaticString = #file,
line: UInt = #line,
_ callback: @escaping (Value) throws -> EventLoopFuture<NewValue>
) -> EventLoopFuture<NewValue> {
return then(file: file, line: line) { result in
do {
return try callback(result)
} catch {
return self.eventLoop.makeFailedFuture(error, file: file, line: line)
}
}
}
}
extension EventLoopFuture {
func and<A>(
_ f1: EventLoopFuture<A>,
file: StaticString = #file,
line: UInt = #line
) -> EventLoopFuture<(Value, A)> {
self.and(f1, file: file, line: line).map { t0 in
let (r1, r2) = t0
return (r1, r2)
}
}
func and<A, B>(
_ f1: EventLoopFuture<A>,
_ f2: EventLoopFuture<B>,
file: StaticString = #file,
line: UInt = #line
) -> EventLoopFuture<(Value, A, B)> {
self.and(f1, file: file, line: line)
.and(f2, file: file, line: line)
.map { t0 in
let (t1, r3) = t0
let (r1, r2) = t1
return (r1, r2, r3)
}
}
func and<A, B, C>(
_ f1: EventLoopFuture<A>,
_ f2: EventLoopFuture<B>,
_ f3: EventLoopFuture<C>,
file: StaticString = #file,
line: UInt = #line
) -> EventLoopFuture<(Value, A, B, C)> {
self.and(f1, file: file, line: line)
.and(f2, file: file, line: line)
.and(f3, file: file, line: line)
.map { t0 in
let (t1, r4) = t0
let (t2, r3) = t1
let (r1, r2) = t2
return (r1, r2, r3, r4)
}
}
func and<A, B, C, D>(
_ f1: EventLoopFuture<A>,
_ f2: EventLoopFuture<B>,
_ f3: EventLoopFuture<C>,
_ f4: EventLoopFuture<D>,
file: StaticString = #file,
line: UInt = #line
) -> EventLoopFuture<(Value, A, B, C, D)> {
self.and(f1, file: file, line: line)
.and(f2, file: file, line: line)
.and(f3, file: file, line: line)
.and(f4, file: file, line: line)
.map { t0 in
let (t1, r5) = t0
let (t2, r4) = t1
let (t3, r3) = t2
let (r1, r2) = t3
return (r1, r2, r3, r4, r5)
}
}
func and<A, B, C, D, E>(
_ f1: EventLoopFuture<A>,
_ f2: EventLoopFuture<B>,
_ f3: EventLoopFuture<C>,
_ f4: EventLoopFuture<D>,
_ f5: EventLoopFuture<E>,
file: StaticString = #file,
line: UInt = #line
) -> EventLoopFuture<(Value, A, B, C, D, E)> {
self.and(f1, file: file, line: line)
.and(f2, file: file, line: line)
.and(f3, file: file, line: line)
.and(f4, file: file, line: line)
.and(f5, file: file, line: line)
.map { t0 in
let (t1, r6) = t0
let (t2, r5) = t1
let (t3, r4) = t2
let (t4, r3) = t3
let (r1, r2) = t4
return (r1, r2, r3, r4, r5, r6)
}
}
func and<A, B, C, D, E, F>(
_ f1: EventLoopFuture<A>,
_ f2: EventLoopFuture<B>,
_ f3: EventLoopFuture<C>,
_ f4: EventLoopFuture<D>,
_ f5: EventLoopFuture<E>,
_ f6: EventLoopFuture<F>,
file: StaticString = #file,
line: UInt = #line
) -> EventLoopFuture<(Value, A, B, C, D, E, F)> {
self.and(f1, file: file, line: line)
.and(f2, file: file, line: line)
.and(f3, file: file, line: line)
.and(f4, file: file, line: line)
.and(f5, file: file, line: line)
.and(f6, file: file, line: line)
.map { t0 in
let (t1, r7) = t0
let (t2, r6) = t1
let (t3, r5) = t2
let (t4, r4) = t3
let (t5, r3) = t4
let (r1, r2) = t5
return (r1, r2, r3, r4, r5, r6, r7)
}
}
func and<A, B, C, D, E, F, G>(
_ f1: EventLoopFuture<A>,
_ f2: EventLoopFuture<B>,
_ f3: EventLoopFuture<C>,
_ f4: EventLoopFuture<D>,
_ f5: EventLoopFuture<E>,
_ f6: EventLoopFuture<F>,
_ f7: EventLoopFuture<G>,
file: StaticString = #file,
line: UInt = #line
) -> EventLoopFuture<(Value, A, B, C, D, E, F, G)> {
self.and(f1, file: file, line: line)
.and(f2, file: file, line: line)
.and(f3, file: file, line: line)
.and(f4, file: file, line: line)
.and(f5, file: file, line: line)
.and(f6, file: file, line: line)
.and(f7, file: file, line: line)
.map { t0 in
let (t1, r8) = t0
let (t2, r7) = t1
let (t3, r6) = t2
let (t4, r5) = t3
let (t5, r4) = t4
let (t6, r3) = t5
let (r1, r2) = t6
return (r1, r2, r3, r4, r5, r6, r7, r8)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment