A helper for easily unwrapping Optional values in an EventLoopFuture (#1656)
Motivation: Unwrapping an `Optional` value from an `EventLoopFuture` is a fairly common requirement that currently involves the client writing boilerplate code, for example: ``` extension EventLoopFuture { func unwrapOptional<T>(orError error: Swift.Error) -> EventLoopFuture<T> where Value == T? { self.flatMapThrowing { value in guard let value = value else { throw error } return value } } } ``` As this is a fairly common requirement adding an extension of `EventLoopFuture` to unwrap `Optional` values would remove this burden from clients. Modifications: Added Extension to `EventLoopFuture` containing the following functions: - `unwrap<NewValue>(orError: Error)`: Unwraps a future returning a new `EventLoopFuture` with the same value as the resolved future when its value is Optional.some(...)`, otherwise the `Error` passed in the `orError` parameter is thrown - func unwrap<NewValue>(orReplace: NewValue)`: Unwraps a future returning a new `EventLoopFuture` with either: the value passed in the `orReplace` parameter when the future resolved with value `Optional.none`, or the same value otherwise. - func unwrap<NewValue>(orElse: @escaping ()- > NewValue): Unwraps a future returning a new `EventLoopFuture` with either: the value returned by the closure passed in the `orElse` parameter when the future resolved with value `Optional.none`, or the same value otherwise. Added new unit tests for each new `unwrap(orXXX:)` function. Result: Client's no longer have to write their own boilerplate code.
This commit is contained in:
parent
934de6a284
commit
7c42e5a45d
|
@ -1325,3 +1325,80 @@ extension EventLoopFuture {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: unwrap
|
||||||
|
|
||||||
|
extension EventLoopFuture {
|
||||||
|
/// Unwrap an `EventLoopFuture` where its type parameter is an `Optional`.
|
||||||
|
///
|
||||||
|
/// Unwrap a future returning a new `EventLoopFuture`. When the resolved future's value is `Optional.some(...)`
|
||||||
|
/// the new future is created with the identical value. Otherwise the `Error` passed in the `orError` parameter
|
||||||
|
/// is thrown. For example:
|
||||||
|
/// ```
|
||||||
|
/// do {
|
||||||
|
/// try promise.futureResult.unwrap(orError: ErrorToThrow).wait()
|
||||||
|
/// } catch ErrorToThrow {
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// - parameters:
|
||||||
|
/// - orError: the `Error` that is thrown when then resolved future's value is `Optional.none`.
|
||||||
|
/// - returns: an new `EventLoopFuture` with new type parameter `NewValue` and the same value as the resolved
|
||||||
|
/// future.
|
||||||
|
/// - throws: the `Error` passed in the `orError` parameter when the resolved future's value is `Optional.none`.
|
||||||
|
@inlinable
|
||||||
|
public func unwrap<NewValue>(orError error: Error) -> EventLoopFuture<NewValue> where Value == Optional<NewValue> {
|
||||||
|
return self.flatMapThrowing { (value) throws -> NewValue in
|
||||||
|
guard let value = value else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwrap an `EventLoopFuture` where its type parameter is an `Optional`.
|
||||||
|
///
|
||||||
|
/// Unwraps a future returning a new `EventLoopFuture` with either: the value passed in the `orReplace`
|
||||||
|
/// parameter when the future resolved with value Optional.none, or the same value otherwise. For example:
|
||||||
|
/// ```
|
||||||
|
/// promise.futureResult.unwrap(orReplace: 42).wait()
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// - parameters:
|
||||||
|
/// - orReplace: the value of the returned `EventLoopFuture` when then resolved future's value is `Optional.some()`.
|
||||||
|
/// - returns: an new `EventLoopFuture` with new type parameter `NewValue` and the value passed in the `orReplace` parameter.
|
||||||
|
@inlinable
|
||||||
|
public func unwrap<NewValue>(orReplace replacement: NewValue) -> EventLoopFuture<NewValue> where Value == Optional<NewValue> {
|
||||||
|
return self.map { (value) -> NewValue in
|
||||||
|
guard let value = value else {
|
||||||
|
return replacement
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwrap an `EventLoopFuture` where its type parameter is an `Optional`.
|
||||||
|
///
|
||||||
|
/// Unwraps a future returning a new `EventLoopFuture` with either: the value returned by the closure passed in
|
||||||
|
/// the `orElse` parameter when the future resolved with value Optional.none, or the same value otherwise. For example:
|
||||||
|
/// ```
|
||||||
|
/// var x = 2
|
||||||
|
/// promise.futureResult.unwrap(orElse: { x * 2 }).wait()
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// - parameters:
|
||||||
|
/// - orElse: a closure that returns the value of the returned `EventLoopFuture` when then resolved future's value
|
||||||
|
/// is `Optional.some()`.
|
||||||
|
/// - returns: an new `EventLoopFuture` with new type parameter `NewValue` and with the value returned by the closure
|
||||||
|
/// passed in the `orElse` parameter.
|
||||||
|
@inlinable
|
||||||
|
public func unwrap<NewValue>(orElse callback: @escaping () -> NewValue) -> EventLoopFuture<NewValue> where Value == Optional<NewValue> {
|
||||||
|
return self.map { (value) -> NewValue in
|
||||||
|
guard let value = value else {
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -84,6 +84,12 @@ extension EventLoopFutureTest {
|
||||||
("testAndAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures", testAndAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures),
|
("testAndAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures", testAndAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures),
|
||||||
("testWhenAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures", testWhenAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures),
|
("testWhenAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures", testWhenAllCompleteWithMixOfPreSuccededAndNotYetCompletedFutures),
|
||||||
("testRepeatedTaskOffEventLoopGroupFuture", testRepeatedTaskOffEventLoopGroupFuture),
|
("testRepeatedTaskOffEventLoopGroupFuture", testRepeatedTaskOffEventLoopGroupFuture),
|
||||||
|
("testEventLoopFutureOrErrorNoThrow", testEventLoopFutureOrErrorNoThrow),
|
||||||
|
("testEventLoopFutureOrThrows", testEventLoopFutureOrThrows),
|
||||||
|
("testEventLoopFutureOrNoReplacement", testEventLoopFutureOrNoReplacement),
|
||||||
|
("testEventLoopFutureOrReplacement", testEventLoopFutureOrReplacement),
|
||||||
|
("testEventLoopFutureOrNoElse", testEventLoopFutureOrNoElse),
|
||||||
|
("testEventLoopFutureOrElse", testEventLoopFutureOrElse),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1207,5 +1207,62 @@ class EventLoopFutureTest : XCTestCase {
|
||||||
|
|
||||||
try exitPromise.futureResult.wait()
|
try exitPromise.futureResult.wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testEventLoopFutureOrErrorNoThrow() {
|
||||||
|
let eventLoop = EmbeddedEventLoop()
|
||||||
|
let promise = eventLoop.makePromise(of: Int?.self)
|
||||||
|
let result: Result<Int?, Error> = .success(42)
|
||||||
|
promise.completeWith(result)
|
||||||
|
|
||||||
|
XCTAssertEqual(try promise.futureResult.unwrap(orError: EventLoopFutureTestError.example).wait(), 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEventLoopFutureOrThrows() {
|
||||||
|
let eventLoop = EmbeddedEventLoop()
|
||||||
|
let promise = eventLoop.makePromise(of: Int?.self)
|
||||||
|
let result: Result<Int?, Error> = .success(nil)
|
||||||
|
promise.completeWith(result)
|
||||||
|
|
||||||
|
XCTAssertThrowsError(try _ = promise.futureResult.unwrap(orError: EventLoopFutureTestError.example).wait()) { (error) -> Void in
|
||||||
|
XCTAssertEqual(error as! EventLoopFutureTestError, EventLoopFutureTestError.example)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEventLoopFutureOrNoReplacement() {
|
||||||
|
let eventLoop = EmbeddedEventLoop()
|
||||||
|
let promise = eventLoop.makePromise(of: Int?.self)
|
||||||
|
let result: Result<Int?, Error> = .success(42)
|
||||||
|
promise.completeWith(result)
|
||||||
|
|
||||||
|
XCTAssertEqual(try! promise.futureResult.unwrap(orReplace: 41).wait(), 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEventLoopFutureOrReplacement() {
|
||||||
|
let eventLoop = EmbeddedEventLoop()
|
||||||
|
let promise = eventLoop.makePromise(of: Int?.self)
|
||||||
|
let result: Result<Int?, Error> = .success(nil)
|
||||||
|
promise.completeWith(result)
|
||||||
|
|
||||||
|
XCTAssertEqual(try! promise.futureResult.unwrap(orReplace: 42).wait(), 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEventLoopFutureOrNoElse() {
|
||||||
|
let eventLoop = EmbeddedEventLoop()
|
||||||
|
let promise = eventLoop.makePromise(of: Int?.self)
|
||||||
|
let result: Result<Int?, Error> = .success(42)
|
||||||
|
promise.completeWith(result)
|
||||||
|
|
||||||
|
XCTAssertEqual(try! promise.futureResult.unwrap(orElse: { 41 } ).wait(), 42)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEventLoopFutureOrElse() {
|
||||||
|
let eventLoop = EmbeddedEventLoop()
|
||||||
|
let promise = eventLoop.makePromise(of: Int?.self)
|
||||||
|
let result: Result<Int?, Error> = .success(4)
|
||||||
|
promise.completeWith(result)
|
||||||
|
|
||||||
|
let x = 2
|
||||||
|
XCTAssertEqual(try! promise.futureResult.unwrap(orElse: { x * 2 } ).wait(), 4)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue