Skip to content

Instantly share code, notes, and snippets.

@JohnCoates
Created March 10, 2019 23:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JohnCoates/e70c28b611f8df3b6bf0e6bcbf324e32 to your computer and use it in GitHub Desktop.
Save JohnCoates/e70c28b611f8df3b6bf0e6bcbf324e32 to your computer and use it in GitHub Desktop.
Mapping on nil for Vapor 3
//
// Future+NilMap.swift
// Created on 3/10/19
//
import Async
public protocol OptionalConstrainable {
associatedtype Element
var asOptional: Optional<Element> { get }
}
extension Optional: OptionalConstrainable {
public var asOptional: Optional<Wrapped> { return self }
}
extension Future where Expectation: OptionalConstrainable {
/// Calls the supplied closure if the chained `Future`'s optional return value resolves to a nil.
///
/// The closure gives you a chance to return something non-nil.
///
/// The callback expects a non-`Future` return. See `nilFlatMap` for a Future return.
public func nilMap(_ callback: @escaping () throws -> Expectation.Element) -> Future<Expectation.Element> {
let promise = eventLoop.newPromise(Expectation.Element.self)
addAwaiter { result in
switch result {
case .error(let error):
promise.fail(error: error)
case .success(let e):
if let result = e.asOptional {
promise.succeed(result: result)
} else {
do {
try promise.succeed(result: callback())
} catch {
promise.fail(error: error)
}
}
}
}
return promise.futureResult
}
/// Calls the supplied closure if the chained `Future`'s optional return value resolves to a nil.
///
/// The closure gives you a chance to return something non-nil.
///
/// The callback expects a `Future` return. See `nilMap` for a non-`Future` return.
public func nilFlatMap(_ callback: @escaping () throws -> Future<Expectation.Element>) -> Future<Expectation.Element> {
let promise = eventLoop.newPromise(Expectation.Element.self)
addAwaiter { result in
switch result {
case .error(let error):
promise.fail(error: error)
case .success(let e):
if let result = e.asOptional {
promise.succeed(result: result)
} else {
do {
let mapped = try callback()
mapped.cascade(promise: promise)
} catch {
promise.fail(error: error)
}
}
}
}
return promise.futureResult
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment