Skip to content

Commit

Permalink
Allow abuse errors to be overridden to prevent preconditionFailure
Browse files Browse the repository at this point in the history
  • Loading branch information
EricRabil committed Sep 23, 2021
1 parent 17d38c6 commit 30cb795
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 17 deletions.
6 changes: 3 additions & 3 deletions Sources/Pwomise/PendingPromise.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import Foundation

internal enum PendingPromise<Output, Failure: Error>: Equatable, CustomDebugStringConvertible {
internal static func == (lhs: PendingPromise<Output, Failure>, rhs: PendingPromise<Output, Failure>) -> Bool {
public enum PendingPromise<Output, Failure: Error>: Equatable, CustomDebugStringConvertible {
public static func == (lhs: PendingPromise<Output, Failure>, rhs: PendingPromise<Output, Failure>) -> Bool {
switch lhs {
case .pending:
switch rhs {
Expand All @@ -30,7 +30,7 @@ internal enum PendingPromise<Output, Failure: Error>: Equatable, CustomDebugStri
case pending
case resolved(Result<Output, Failure>)

internal var debugDescription: String {
public var debugDescription: String {
switch self {
case .pending:
return "pending"
Expand Down
29 changes: 15 additions & 14 deletions Sources/Pwomise/Pwomise.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ public enum PromiseInconsistencyError: Error {
case opaqueMismatch
}

public protocol PromiseAbuseDelegate {
func promise<T>(_ promise: Promise<T>, doubleResolvedWithResult result: PendingPromise<T, Error>, resolutionStackTrace: [String], abusingStackTrace: [String])
}

public var SharedPromiseAbuseDelegate: PromiseAbuseDelegate?

public class Promise<Output>: CustomDebugStringConvertible {
internal typealias Pending = PendingPromise<Output, Error>
public typealias Completion = Result<Output, Error>
Expand Down Expand Up @@ -89,9 +95,16 @@ public class Promise<Output>: 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
}
}
Expand All @@ -109,26 +122,14 @@ public class Promise<Output>: 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))
})
}
Expand Down

0 comments on commit 30cb795

Please sign in to comment.