PromiseKit/Sources/Error.swift
2018-06-04 19:23:16 -04:00

188 lines
5.3 KiB
Swift

import Foundation
public enum PMKError: Error {
/**
The ErrorType for a rejected `join`.
- Parameter 0: The promises passed to this `join` that did not *all* fulfill.
- Note: The array is untyped because Swift generics are fussy with enums.
*/
case join([AnyObject])
/**
The completionHandler with form (T?, ErrorType?) was called with (nil, nil)
This is invalid as per Cocoa/Apple calling conventions.
*/
case invalidCallingConvention
/**
A handler returned its own promise. 99% of the time, this is likely a
programming error. It is also invalid per Promises/A+.
*/
case returnedSelf
/** `when()` was called with a concurrency of <= 0 */
case whenConcurrentlyZero
/** AnyPromise.toPromise failed to cast as requested */
case castError(Any.Type)
}
extension PMKError: LocalizedError {
public var errorDescription: String? {
switch self {
case .invalidCallingConvention:
return "A closure was called with an invalid calling convention, probably (nil, nil)"
case .returnedSelf:
return "A promise handler returned itself"
case .whenConcurrentlyZero, .join:
return "Bad input was provided to a PromiseKit function"
case .castError(let type):
return "Promise chain sequence failed to cast an object to \(type)."
}
}
}
public enum PMKURLError: Error {
/**
The URLRequest succeeded but a valid UIImage could not be decoded from
the data that was received.
*/
case invalidImageData(URLRequest, Data)
/**
The HTTP request returned a non-200 status code.
*/
case badResponse(URLRequest, Data?, URLResponse?)
/**
The data could not be decoded using the encoding specified by the HTTP
response headers.
*/
case stringEncoding(URLRequest, Data, URLResponse)
/**
Usually the `URLResponse` is actually an `HTTPURLResponse`, if so you
can access it using this property. Since it is returned as an unwrapped
optional: be sure.
*/
public var NSHTTPURLResponse: Foundation.HTTPURLResponse! {
switch self {
case .invalidImageData:
return nil
case .badResponse(_, _, let rsp):
return rsp as? Foundation.HTTPURLResponse
case .stringEncoding(_, _, let rsp):
return rsp as? Foundation.HTTPURLResponse
}
}
}
extension PMKURLError: LocalizedError {
public var errorDescription: String? {
switch self {
case let .badResponse(rq, data, rsp):
if let data = data, let str = String(data: data, encoding: .utf8), let rsp = rsp {
return "PromiseKit: badResponse: \(rq): \(rsp)\n\(str)"
} else {
fallthrough
}
default:
return "\(self)"
}
}
}
public enum JSONError: Error {
/// The JSON response was different to that requested
case unexpectedRootNode(Any)
}
//////////////////////////////////////////////////////////// Cancellation
public protocol CancellableError: Error {
var isCancelled: Bool { get }
}
#if !SWIFT_PACKAGE
private struct ErrorPair: Hashable {
let domain: String
let code: Int
init(_ d: String, _ c: Int) {
domain = d; code = c
}
var hashValue: Int {
return "\(domain):\(code)".hashValue
}
}
private func ==(lhs: ErrorPair, rhs: ErrorPair) -> Bool {
return lhs.domain == rhs.domain && lhs.code == rhs.code
}
extension NSError {
@objc public class func cancelledError() -> NSError {
let info = [NSLocalizedDescriptionKey: "The operation was cancelled"]
return NSError(domain: PMKErrorDomain, code: PMKOperationCancelled, userInfo: info)
}
/**
- Warning: You must call this method before any promises in your application are rejected. Failure to ensure this may lead to concurrency crashes.
- Warning: You must call this method on the main thread. Failure to do this may lead to concurrency crashes.
*/
@objc public class func registerCancelledErrorDomain(_ domain: String, code: Int) {
cancelledErrorIdentifiers.insert(ErrorPair(domain, code))
}
/// - Returns: true if the error represents cancellation.
@objc public var isCancelled: Bool {
return (self as Error).isCancelledError
}
}
private var cancelledErrorIdentifiers = Set([
ErrorPair(PMKErrorDomain, PMKOperationCancelled),
ErrorPair(NSCocoaErrorDomain, NSUserCancelledError),
ErrorPair(NSURLErrorDomain, NSURLErrorCancelled),
])
#endif
extension Error {
public var isCancelledError: Bool {
if let ce = self as? CancellableError {
return ce.isCancelled
} else {
#if SWIFT_PACKAGE
return false
#else
let ne = self as NSError
return cancelledErrorIdentifiers.contains(ErrorPair(ne.domain, ne.code))
#endif
}
}
}
//////////////////////////////////////////////////////// Unhandled Errors
class ErrorConsumptionToken {
var consumed = false
let error: Error
init(_ error: Error) {
self.error = error
}
deinit {
if !consumed {
#if os(Linux) || os(Android)
PMKUnhandledErrorHandler(error)
#else
PMKUnhandledErrorHandler(error as NSError)
#endif
}
}
}