import class Foundation.Thread import Dispatch /** A `Guarantee` is a functional abstraction around an asynchronous operation that cannot error. - See: `Thenable` */ public final class Guarantee: Thenable { let box: PromiseKit.Box fileprivate init(box: SealedBox) { self.box = box } /// Returns a `Guarantee` sealed with the provided value. public static func value(_ value: T) -> Guarantee { return .init(box: SealedBox(value: value)) } /// Returns a pending `Guarantee` that can be resolved with the provided closure’s parameter. public init(resolver body: (@escaping(T) -> Void) -> Void) { box = Box() body(box.seal) } /// - See: `Thenable.pipe` public func pipe(to: @escaping(Result) -> Void) { pipe{ to(.fulfilled($0)) } } func pipe(to: @escaping(T) -> Void) { switch box.inspect() { case .pending: box.inspect { switch $0 { case .pending(let handlers): handlers.append(to) case .resolved(let value): to(value) } } case .resolved(let value): to(value) } } /// - See: `Thenable.result` public var result: Result? { switch box.inspect() { case .pending: return nil case .resolved(let value): return .fulfilled(value) } } final private class Box: EmptyBox { deinit { switch inspect() { case .pending: PromiseKit.conf.logHandler(.pendingGuaranteeDeallocated) case .resolved: break } } } init(_: PMKUnambiguousInitializer) { box = Box() } /// Returns a tuple of a pending `Guarantee` and a function that resolves it. public class func pending() -> (guarantee: Guarantee, resolve: (T) -> Void) { return { ($0, $0.box.seal) }(Guarantee(.pending)) } } public extension Guarantee { @discardableResult func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee { let rg = Guarantee(.pending) pipe { (value: T) in on.async(flags: flags) { body(value) rg.box.seal(()) } } return rg } func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee { return map(on: on, flags: flags) { body($0) return $0 } } func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee { let rg = Guarantee(.pending) pipe { value in on.async(flags: flags) { rg.box.seal(body(value)) } } return rg } #if swift(>=4) && !swift(>=5.2) func map(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee { let rg = Guarantee(.pending) pipe { value in on.async(flags: flags) { rg.box.seal(value[keyPath: keyPath]) } } return rg } #endif @discardableResult func then(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee) -> Guarantee { let rg = Guarantee(.pending) pipe { value in on.async(flags: flags) { body(value).pipe(to: rg.box.seal) } } return rg } func asVoid() -> Guarantee { return map(on: nil) { _ in } } /** Blocks this thread, so you know, don’t call this on a serial thread that any part of your chain may use. Like the main thread for example. */ func wait() -> T { if Thread.isMainThread { conf.logHandler(.waitOnMainThread) } var result = value if result == nil { let group = DispatchGroup() group.enter() pipe { (foo: T) in result = foo; group.leave() } group.wait() } return result! } } public extension Guarantee where T: Sequence { /** `Guarantee<[T]>` => `T` -> `U` => `Guarantee<[U]>` Guarantee.value([1,2,3]) .mapValues { integer in integer * 2 } .done { // $0 => [2,4,6] } */ func mapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U]> { return map(on: on, flags: flags) { $0.map(transform) } } #if swift(>=4) && !swift(>=5.2) /** `Guarantee<[T]>` => `KeyPath` => `Guarantee<[U]>` Guarantee.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")]) .mapValues(\.name) .done { // $0 => ["Max", "Roman", "John"] } */ func mapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee<[U]> { return map(on: on, flags: flags) { $0.map { $0[keyPath: keyPath] } } } #endif /** `Guarantee<[T]>` => `T` -> `[U]` => `Guarantee<[U]>` Guarantee.value([1,2,3]) .flatMapValues { integer in [integer, integer] } .done { // $0 => [1,1,2,2,3,3] } */ func flatMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U.Iterator.Element]> { return map(on: on, flags: flags) { (foo: T) in foo.flatMap { transform($0) } } } /** `Guarantee<[T]>` => `T` -> `U?` => `Guarantee<[U]>` Guarantee.value(["1","2","a","3"]) .compactMapValues { Int($0) } .done { // $0 => [1,2,3] } */ func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U?) -> Guarantee<[U]> { return map(on: on, flags: flags) { foo -> [U] in #if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1)) return foo.flatMap(transform) #else return foo.compactMap(transform) #endif } } #if swift(>=4) && !swift(>=5.2) /** `Guarantee<[T]>` => `KeyPath` => `Guarantee<[U]>` Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)]) .compactMapValues(\.age) .done { // $0 => [26, 23] } */ func compactMapValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee<[U]> { return map(on: on, flags: flags) { foo -> [U] in #if !swift(>=4.1) return foo.flatMap { $0[keyPath: keyPath] } #else return foo.compactMap { $0[keyPath: keyPath] } #endif } } #endif /** `Guarantee<[T]>` => `T` -> `Guarantee` => `Guaranetee<[U]>` Guarantee.value([1,2,3]) .thenMap { .value($0 * 2) } .done { // $0 => [2,4,6] } */ func thenMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee) -> Guarantee<[U]> { return then(on: on, flags: flags) { when(fulfilled: $0.map(transform)) }.recover { // if happens then is bug inside PromiseKit fatalError(String(describing: $0)) } } /** `Guarantee<[T]>` => `T` -> `Guarantee<[U]>` => `Guarantee<[U]>` Guarantee.value([1,2,3]) .thenFlatMap { integer in .value([integer, integer]) } .done { // $0 => [1,1,2,2,3,3] } */ func thenFlatMap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U.T.Iterator.Element]> where U.T: Sequence { return then(on: on, flags: flags) { when(fulfilled: $0.map(transform)) }.map(on: nil) { $0.flatMap { $0 } }.recover { // if happens then is bug inside PromiseKit fatalError(String(describing: $0)) } } /** `Guarantee<[T]>` => `T` -> Bool => `Guarantee<[T]>` Guarantee.value([1,2,3]) .filterValues { $0 > 1 } .done { // $0 => [2,3] } */ func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping(T.Iterator.Element) -> Bool) -> Guarantee<[T.Iterator.Element]> { return map(on: on, flags: flags) { $0.filter(isIncluded) } } #if swift(>=4) && !swift(>=5.2) /** `Guarantee<[T]>` => `KeyPath` => `Guarantee<[T]>` Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)]) .filterValues(\.isStudent) .done { // $0 => [Person(name: "John", age: 23, isStudent: true)] } */ func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath) -> Guarantee<[T.Iterator.Element]> { return map(on: on, flags: flags) { $0.filter { $0[keyPath: keyPath] } } } #endif /** `Guarantee<[T]>` => (`T`, `T`) -> Bool => `Guarantee<[T]>` Guarantee.value([5,2,3,4,1]) .sortedValues { $0 > $1 } .done { // $0 => [5,4,3,2,1] } */ func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ areInIncreasingOrder: @escaping(T.Iterator.Element, T.Iterator.Element) -> Bool) -> Guarantee<[T.Iterator.Element]> { return map(on: on, flags: flags) { $0.sorted(by: areInIncreasingOrder) } } } public extension Guarantee where T: Sequence, T.Iterator.Element: Comparable { /** `Guarantee<[T]>` => `Guarantee<[T]>` Guarantee.value([5,2,3,4,1]) .sortedValues() .done { // $0 => [1,2,3,4,5] } */ func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> Guarantee<[T.Iterator.Element]> { return map(on: on, flags: flags) { $0.sorted() } } } #if swift(>=3.1) public extension Guarantee where T == Void { convenience init() { self.init(box: SealedBox(value: Void())) } static var value: Guarantee { return .value(Void()) } } #endif public extension DispatchQueue { /** Asynchronously executes the provided closure on a dispatch queue. DispatchQueue.global().async(.promise) { md5(input) }.done { md5 in //… } - Parameter body: The closure that resolves this promise. - Returns: A new `Guarantee` resolved by the result of the provided closure. - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues. */ @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *) final func async(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () -> T) -> Guarantee { let rg = Guarantee(.pending) async(group: group, qos: qos, flags: flags) { rg.box.seal(body()) } return rg } } #if os(Linux) import func CoreFoundation._CFIsMainThread extension Thread { // `isMainThread` is not implemented yet in swift-corelibs-foundation. static var isMainThread: Bool { return _CFIsMainThread() } } #endif