diff --git a/Sources/Pwomise/PendingPromise.swift b/Sources/Pwomise/PendingPromise.swift index f95e7e8..cb41e70 100644 --- a/Sources/Pwomise/PendingPromise.swift +++ b/Sources/Pwomise/PendingPromise.swift @@ -7,8 +7,8 @@ import Foundation -internal enum PendingPromise: Equatable, CustomDebugStringConvertible { - internal static func == (lhs: PendingPromise, rhs: PendingPromise) -> Bool { +public enum PendingPromise: Equatable, CustomDebugStringConvertible { + public static func == (lhs: PendingPromise, rhs: PendingPromise) -> Bool { switch lhs { case .pending: switch rhs { @@ -30,7 +30,7 @@ internal enum PendingPromise: Equatable, CustomDebugStri case pending case resolved(Result) - internal var debugDescription: String { + public var debugDescription: String { switch self { case .pending: return "pending" diff --git a/Sources/Pwomise/Pwomise.swift b/Sources/Pwomise/Pwomise.swift index 7b0be57..9a20978 100644 --- a/Sources/Pwomise/Pwomise.swift +++ b/Sources/Pwomise/Pwomise.swift @@ -12,6 +12,12 @@ public enum PromiseInconsistencyError: Error { case opaqueMismatch } +public protocol PromiseAbuseDelegate { + func promise(_ promise: Promise, doubleResolvedWithResult result: PendingPromise, resolutionStackTrace: [String], abusingStackTrace: [String]) +} + +public var SharedPromiseAbuseDelegate: PromiseAbuseDelegate? + public class Promise: CustomDebugStringConvertible { internal typealias Pending = PendingPromise public typealias Completion = Result @@ -89,9 +95,16 @@ public class Promise: CustomDebugStringConvertible { willSet { guard result == .pending, newValue != .pending else { /// Result can only be set once – its a promise of a result, not a publisher - print(resolutionStackTrace.joined(separator: "\n")) - preconditionFailure("result is omnidirectional, from pending to resolved.") + if let abuseDelegate = SharedPromiseAbuseDelegate { + abuseDelegate.promise(self, doubleResolvedWithResult: newValue, resolutionStackTrace: resolutionStackTrace, abusingStackTrace: Thread.callStackSymbols) + } else { + print(resolutionStackTrace.joined(separator: "\n")) + preconditionFailure("result is omnidirectional, from pending to resolved.") + } + + return } + resolutionStackTrace = Thread.callStackSymbols } } @@ -109,26 +122,14 @@ public class Promise: CustomDebugStringConvertible { public init(_ cb: (@escaping Resolve, @escaping Reject) -> ()) { cb({ output in - guard self.pending else { - preconditionFailure("cannot overwrite promise state") - } - self.result = .resolved(.success(output)) }, { error in - guard self.pending else { - preconditionFailure("cannot overwrite promise state") - } - self.result = .resolved(.failure(error)) }) } public init(_ cb: (@escaping Resolve) -> ()) { cb({ output in - guard self.pending else { - preconditionFailure("cannot overwrite promise state") - } - self.result = .resolved(.success(output)) }) }