Created
March 14, 2020 12:13
-
-
Save tonyarnold/0fe81541cd780f9d34e83028583e487c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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