mirror of
https://github.com/apple/swift-foundation.git
synced 2025-05-22 13:39:25 +08:00
* Consolidate/standardize file-based error reporting * Fix build failures * Fix test failures
288 lines
9.4 KiB
Swift
288 lines
9.4 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2022 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if FOUNDATION_FRAMEWORK
|
|
/// Describes errors within the Cocoa error domain, including errors that Foundation throws.
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public struct CocoaError : _BridgedStoredNSError {
|
|
// On Darwin, CocoaError is backed by `NSError`.
|
|
public let _nsError: NSError
|
|
|
|
public init(_nsError error: NSError) {
|
|
precondition(error.domain == NSCocoaErrorDomain)
|
|
self._nsError = error
|
|
}
|
|
|
|
public static var errorDomain: String { return NSCocoaErrorDomain }
|
|
|
|
public var hashValue: Int {
|
|
return _nsError.hashValue
|
|
}
|
|
}
|
|
#else
|
|
|
|
public protocol _StoredError {
|
|
associatedtype Code: _ErrorCodeProtocol, RawRepresentable where Code.RawValue: FixedWidthInteger
|
|
|
|
var code: Code { get }
|
|
}
|
|
|
|
/// Describes errors within the Cocoa error domain, including errors that Foundation throws.
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public struct CocoaError : CustomNSError, _StoredError, Hashable {
|
|
// On not-Darwin, CocoaError is backed by a simple code.
|
|
public let code: Code
|
|
public let userInfo: [String: AnyHashable]
|
|
|
|
public init(_ code: Code, userInfo: [String: AnyHashable] = [:]) {
|
|
self.code = code
|
|
self.userInfo = userInfo
|
|
}
|
|
|
|
public static var errorDomain: String { "NSCocoaErrorDomain" }
|
|
|
|
public func hash(into hasher: inout Hasher) {
|
|
hasher.combine(code)
|
|
}
|
|
|
|
public static func ==(lhs: Self, rhs: Self) -> Bool {
|
|
return lhs.code == rhs.code && lhs.userInfo == rhs.userInfo
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// Describes the code of an error.
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public protocol _ErrorCodeProtocol : Equatable {
|
|
#if FOUNDATION_FRAMEWORK
|
|
/// The corresponding error code.
|
|
associatedtype _ErrorType: _BridgedStoredNSError where _ErrorType.Code == Self
|
|
#else
|
|
associatedtype _ErrorType: _StoredError where _ErrorType.Code == Self
|
|
#endif
|
|
}
|
|
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
extension _ErrorCodeProtocol {
|
|
/// Allow one to match an error code against an arbitrary error.
|
|
public static func ~=(match: Self, error: Error) -> Bool {
|
|
guard let specificError = error as? Self._ErrorType else { return false }
|
|
|
|
return match == specificError.code
|
|
}
|
|
}
|
|
|
|
extension CocoaError {
|
|
/// The error code itself.
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public struct Code : RawRepresentable, Hashable, _ErrorCodeProtocol, Sendable {
|
|
public typealias _ErrorType = CocoaError
|
|
|
|
public let rawValue: Int
|
|
|
|
public init(rawValue: Int) {
|
|
self.rawValue = rawValue
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public extension CocoaError {
|
|
#if FOUNDATION_FRAMEWORK
|
|
private var _nsUserInfo: [AnyHashable : Any] {
|
|
return (self as NSError).userInfo
|
|
}
|
|
#endif
|
|
|
|
/// The file path associated with the error, if any.
|
|
var filePath: String? {
|
|
#if FOUNDATION_FRAMEWORK
|
|
return _nsUserInfo[NSFilePathErrorKey as NSString] as? String
|
|
#else
|
|
return userInfo[NSFilePathErrorKey] as? String
|
|
#endif
|
|
}
|
|
|
|
/// The string encoding associated with this error, if any.
|
|
var stringEncoding: String.Encoding? {
|
|
#if FOUNDATION_FRAMEWORK
|
|
return (_nsUserInfo[NSStringEncodingErrorKey as NSString] as? NSNumber)
|
|
.map { String.Encoding(rawValue: $0.uintValue) }
|
|
#else
|
|
return (userInfo["NSStringEncodingErrorKey"] as? Int)
|
|
.map { String.Encoding(rawValue: UInt($0)) }
|
|
#endif
|
|
}
|
|
|
|
/// The underlying error behind this error, if any.
|
|
var underlying: Error? {
|
|
#if FOUNDATION_FRAMEWORK
|
|
return _nsUserInfo[NSUnderlyingErrorKey as NSString] as? Error
|
|
#else
|
|
return userInfo[NSUnderlyingErrorKey] as? Error
|
|
#endif
|
|
}
|
|
|
|
/// A list of underlying errors, if any. It includes the values of both NSUnderlyingErrorKey and NSMultipleUnderlyingErrorsKey. If there are no underlying errors, returns an empty array.
|
|
@available(macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5, *)
|
|
var underlyingErrors: [Error] {
|
|
var result : [Error] = []
|
|
|
|
#if FOUNDATION_FRAMEWORK
|
|
if let underlying = _nsUserInfo[NSUnderlyingErrorKey as NSString] as? Error {
|
|
result.append(underlying)
|
|
}
|
|
|
|
if let multipleUnderlying = _nsUserInfo["NSMultipleUnderlyingErrorsKey" as NSString] as? [Error] {
|
|
result += multipleUnderlying
|
|
}
|
|
|
|
if let coreDataUnderlying = _nsUserInfo["NSDetailedErrors" as NSString] as? [Error] {
|
|
result += coreDataUnderlying
|
|
}
|
|
#else
|
|
if let underlying = userInfo[NSUnderlyingErrorKey] as? Error {
|
|
result.append(underlying)
|
|
}
|
|
|
|
if let multipleUnderlying = userInfo["NSMultipleUnderlyingErrorsKey"] as? [Error] {
|
|
result += multipleUnderlying
|
|
}
|
|
#endif
|
|
|
|
return result
|
|
}
|
|
|
|
/// The URL associated with this error, if any.
|
|
var url: URL? {
|
|
#if FOUNDATION_FRAMEWORK
|
|
return _nsUserInfo[NSURLErrorKey as NSString] as? URL
|
|
#else
|
|
return userInfo[NSURLErrorKey] as? URL
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
extension CocoaError {
|
|
#if FOUNDATION_FRAMEWORK
|
|
public static func error(_ code: CocoaError.Code, userInfo: [AnyHashable : Any]? = nil, url: URL? = nil) -> Error {
|
|
var info: [String : Any] = userInfo as? [String : Any] ?? [:]
|
|
if let url = url {
|
|
info[NSURLErrorKey] = url
|
|
}
|
|
return NSError(domain: NSCocoaErrorDomain, code: code.rawValue, userInfo: info)
|
|
}
|
|
#else
|
|
public static func error(_ code: CocoaError.Code, userInfo: [String : AnyHashable]? = nil, url: URL? = nil) -> Error {
|
|
var info: [String : AnyHashable] = userInfo ?? [:]
|
|
if let url = url {
|
|
info["NSURLErrorKey"] = url
|
|
}
|
|
return CocoaError(code, userInfo: info)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// Describes an error that provides localized messages describing why
|
|
/// an error occurred and provides more information about the error.
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public protocol LocalizedError : Error {
|
|
/// A localized message describing what error occurred.
|
|
var errorDescription: String? { get }
|
|
|
|
/// A localized message describing the reason for the failure.
|
|
var failureReason: String? { get }
|
|
|
|
/// A localized message describing how one might recover from the failure.
|
|
var recoverySuggestion: String? { get }
|
|
|
|
/// A localized message providing "help" text if the user requests help.
|
|
var helpAnchor: String? { get }
|
|
}
|
|
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public extension LocalizedError {
|
|
var errorDescription: String? { return nil }
|
|
var failureReason: String? { return nil }
|
|
var recoverySuggestion: String? { return nil }
|
|
var helpAnchor: String? { return nil }
|
|
}
|
|
|
|
/// Describes an error type that specifically provides a domain, code,
|
|
/// and user-info dictionary.
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public protocol CustomNSError : Error {
|
|
/// The domain of the error.
|
|
static var errorDomain: String { get }
|
|
|
|
/// The error code within the given domain.
|
|
var errorCode: Int { get }
|
|
|
|
/// The user-info dictionary.
|
|
var errorUserInfo: [String : Any] { get }
|
|
}
|
|
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public extension CustomNSError {
|
|
/// Default domain of the error.
|
|
static var errorDomain: String {
|
|
return String(reflecting: self)
|
|
}
|
|
|
|
/// The error code within the given domain.
|
|
var errorCode: Int {
|
|
return _getDefaultErrorCode(self)
|
|
}
|
|
|
|
/// The default user-info dictionary.
|
|
var errorUserInfo: [String : Any] {
|
|
return [:]
|
|
}
|
|
}
|
|
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public extension Error where Self : CustomNSError {
|
|
/// Default implementation for customized NSErrors.
|
|
var _domain: String { return Self.errorDomain }
|
|
|
|
/// Default implementation for customized NSErrors.
|
|
var _code: Int { return self.errorCode }
|
|
}
|
|
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public extension Error where Self: CustomNSError, Self: RawRepresentable, Self.RawValue: FixedWidthInteger {
|
|
/// Default implementation for customized NSErrors.
|
|
var _code: Int { return self.errorCode }
|
|
}
|
|
|
|
#if FOUNDATION_FRAMEWORK
|
|
@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)
|
|
public extension Error {
|
|
/// Retrieve the localized description for this error.
|
|
var localizedDescription: String {
|
|
return (self as NSError).localizedDescription
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#if FOUNDATION_FRAMEWORK
|
|
#else
|
|
// These are loosely typed, as a typedef for String called NSErrorUserInfoKey
|
|
internal let NSUnderlyingErrorKey = "NSUnderlyingError"
|
|
internal let NSUserStringVariantErrorKey = "NSUserStringVariant"
|
|
internal let NSFilePathErrorKey = "NSFilePath"
|
|
internal let NSURLErrorKey = "NSURL"
|
|
internal let NSSourceFilePathErrorKey = "NSSourceFilePathErrorKey"
|
|
internal let NSDestinationFilePathErrorKey = "NSDestinationFilePath"
|
|
#endif
|