Explorar o código

Make result type compatible to both Swift 4 and 5

onevcat %!s(int64=7) %!d(string=hai) anos
pai
achega
4e307debfe

+ 8 - 2
Sources/Extensions/ImageView+Kingfisher.swift

@@ -118,8 +118,14 @@ extension KingfisherWrapper where Base: ImageView {
                 CallbackQueue.mainCurrentOrAsync.execute {
                     maybeIndicator?.stopAnimatingView()
                     guard issuedIdentifier == self.taskIdentifier else {
-                        let error = KingfisherError.imageSettingError(
-                            reason: .notCurrentSourceTask(result: result.value, error: result.error, source: source))
+                        let reason: KingfisherError.ImageSettingErrorReason
+                        do {
+                            let value = try result.get()
+                            reason = .notCurrentSourceTask(result: value, error: nil, source: source)
+                        } catch {
+                            reason = .notCurrentSourceTask(result: nil, error: error, source: source)
+                        }
+                        let error = KingfisherError.imageSettingError(reason: reason)
                         completionHandler?(.failure(error))
                         return
                     }

+ 16 - 4
Sources/Extensions/UIButton+Kingfisher.swift

@@ -80,8 +80,14 @@ extension KingfisherWrapper where Base: UIButton {
             completionHandler: { result in
                 CallbackQueue.mainCurrentOrAsync.execute {
                     guard issuedTaskIdentifier == self.taskIdentifier(for: state) else {
-                        let error = KingfisherError.imageSettingError(
-                            reason: .notCurrentSourceTask(result: result.value, error: result.error, source: source))
+                        let reason: KingfisherError.ImageSettingErrorReason
+                        do {
+                            let value = try result.get()
+                            reason = .notCurrentSourceTask(result: value, error: nil, source: source)
+                        } catch {
+                            reason = .notCurrentSourceTask(result: nil, error: error, source: source)
+                        }
+                        let error = KingfisherError.imageSettingError(reason: reason)
                         completionHandler?(.failure(error))
                         return
                     }
@@ -205,8 +211,14 @@ extension KingfisherWrapper where Base: UIButton {
             completionHandler: { result in
                 CallbackQueue.mainCurrentOrAsync.execute {
                     guard issuedTaskIdentifier == self.backgroundTaskIdentifier(for: state) else {
-                        let error = KingfisherError.imageSettingError(
-                            reason: .notCurrentSourceTask(result: result.value, error: result.error, source: source))
+                        let reason: KingfisherError.ImageSettingErrorReason
+                        do {
+                            let value = try result.get()
+                            reason = .notCurrentSourceTask(result: value, error: nil, source: source)
+                        } catch {
+                            reason = .notCurrentSourceTask(result: nil, error: error, source: source)
+                        }
+                        let error = KingfisherError.imageSettingError(reason: reason)
                         completionHandler?(.failure(error))
                         return
                     }

+ 1 - 1
Sources/General/ImageSource/ImageDataProvider.swift

@@ -80,7 +80,7 @@ public struct LocalFileImageDataProvider: ImageDataProvider {
     public var cacheKey: String
 
     public func data(handler: (Result<Data, Error>) -> Void) {
-        handler( Result { try Data(contentsOf: fileURL) } )
+        handler(Result(catching: { try Data(contentsOf: fileURL) }))
     }
 }
 

+ 52 - 36
Sources/General/KingfisherManager.swift

@@ -323,14 +323,22 @@ public class KingfisherManager {
             targetCache.retrieveImage(forKey: key, options: options) { result in
                 guard let completionHandler = completionHandler else { return }
                 options.callbackQueue.execute {
-                    if let image = result.value?.image {
-                        let value = result.map {
-                            RetrieveImageResult(image: image, cacheType: $0.cacheType, source: source)
+                    result.match(
+                        onSuccess: { cacheResult in
+                            let value: Result<RetrieveImageResult, KingfisherError>
+                            if let image = cacheResult.image {
+                                value = result.map {
+                                    RetrieveImageResult(image: image, cacheType: $0.cacheType, source: source)
+                                }
+                            } else {
+                                value = .failure(KingfisherError.cacheError(reason: .imageNotExisting(key: key)))
+                            }
+                            completionHandler(value)
+                        },
+                        onFailure: { _ in
+                            completionHandler(.failure(KingfisherError.cacheError(reason: .imageNotExisting(key: key))))
                         }
-                        completionHandler(value)
-                    } else {
-                        completionHandler(.failure(KingfisherError.cacheError(reason: .imageNotExisting(key: key))))
-                    }
+                    )
                 }
             }
             return true
@@ -352,44 +360,52 @@ public class KingfisherManager {
             var optionsWithoutProcessor = options
             optionsWithoutProcessor.processor = DefaultImageProcessor.default
             originalCache.retrieveImage(forKey: key, options: optionsWithoutProcessor) { result in
-                if let image = result.value?.image {
-                    let processor = options.processor
-                    (options.processingQueue ?? self.processingQueue).execute {
-                        let item = ImageProcessItem.image(image)
-                        guard let processedImage = processor.process(item: item, options: options) else {
-                            let error = KingfisherError.processorError(
-                                            reason: .processingFailed(processor: processor, item: item))
-                            options.callbackQueue.execute { completionHandler?(.failure(error)) }
+
+                result.match(
+                    onSuccess: { cacheResult in
+                        guard let image = cacheResult.image else {
                             return
                         }
 
-                        var cacheOptions = options
-                        cacheOptions.callbackQueue = .untouch
-                        targetCache.store(
-                            processedImage,
-                            forKey: key,
-                            options: cacheOptions,
-                            toDisk: !options.cacheMemoryOnly)
-                        {
-                            _ in
-                            if options.waitForCache {
+                        let processor = options.processor
+                        (options.processingQueue ?? self.processingQueue).execute {
+                            let item = ImageProcessItem.image(image)
+                            guard let processedImage = processor.process(item: item, options: options) else {
+                                let error = KingfisherError.processorError(
+                                    reason: .processingFailed(processor: processor, item: item))
+                                options.callbackQueue.execute { completionHandler?(.failure(error)) }
+                                return
+                            }
+
+                            var cacheOptions = options
+                            cacheOptions.callbackQueue = .untouch
+                            targetCache.store(
+                                processedImage,
+                                forKey: key,
+                                options: cacheOptions,
+                                toDisk: !options.cacheMemoryOnly)
+                            {
+                                _ in
+                                if options.waitForCache {
+                                    let value = RetrieveImageResult(image: processedImage, cacheType: .none, source: source)
+                                    options.callbackQueue.execute { completionHandler?(.success(value)) }
+                                }
+                            }
+
+                            if !options.waitForCache {
                                 let value = RetrieveImageResult(image: processedImage, cacheType: .none, source: source)
                                 options.callbackQueue.execute { completionHandler?(.success(value)) }
                             }
                         }
-
-                        if !options.waitForCache {
-                            let value = RetrieveImageResult(image: processedImage, cacheType: .none, source: source)
-                            options.callbackQueue.execute { completionHandler?(.success(value)) }
+                    },
+                    onFailure: { _ in
+                        // This should not happen actually, since we already confirmed `originalImageCached` is `true`.
+                        // Just in case...
+                        options.callbackQueue.execute {
+                            completionHandler?(.failure(KingfisherError.cacheError(reason: .imageNotExisting(key: key))))
                         }
                     }
-                } else {
-                    // This should not happen actually, since we already confirmed `originalImageCached` is `true`.
-                    // Just in case...
-                    options.callbackQueue.execute {
-                        completionHandler?(.failure(KingfisherError.cacheError(reason: .imageNotExisting(key: key))))
-                    }
-                }
+                )
             }
             return true
         }

+ 21 - 8
Sources/Networking/ImageDownloader.swift

@@ -157,8 +157,12 @@ open class ImageDownloader {
         }
         sessionDelegate.onDownloadingFinished.delegate(on: self) { (self, value) in
             let (url, result) = value
-            self.delegate?.imageDownloader(
-                self, didFinishDownloadingImageForURL: url, with: result.value, error: result.error)
+            do {
+                let value = try result.get()
+                self.delegate?.imageDownloader(self, didFinishDownloadingImageForURL: url, with: value, error: nil)
+            } catch {
+                self.delegate?.imageDownloader(self, didFinishDownloadingImageForURL: url, with: nil, error: error)
+            }
         }
         sessionDelegate.onDidDownloadData.delegate(on: self) { (self, task) in
             guard let url = task.task.originalRequest?.url else {
@@ -244,11 +248,20 @@ open class ImageDownloader {
                 let (result, callbacks) = done
 
                 // Before processing the downloaded data.
-                self.delegate?.imageDownloader(
-                    self,
-                    didFinishDownloadingImageForURL: url,
-                    with: result.value?.1,
-                    error: result.error)
+                do {
+                    let value = try result.get()
+                    self.delegate?.imageDownloader(
+                        self,
+                        didFinishDownloadingImageForURL: url,
+                        with: value.1,
+                        error: nil)
+                } catch {
+                    self.delegate?.imageDownloader(
+                        self,
+                        didFinishDownloadingImageForURL: url,
+                        with: nil,
+                        error: error)
+                }
 
                 switch result {
                 // Download finished. Now process the data to an image.
@@ -261,7 +274,7 @@ open class ImageDownloader {
                         // result: Result<Image>, callback: SessionDataTask.TaskCallback
                         let (result, callback) = result
 
-                        if let image = result.value {
+                        if let image = try? result.get() {
                             self.delegate?.imageDownloader(self, didDownload: image, for: url, with: response)
                         }
 

+ 4 - 3
Sources/Networking/ImagePrefetcher.swift

@@ -190,10 +190,11 @@ public class ImagePrefetcher {
 
         let downloadTaskCompletionHandler: ((Result<RetrieveImageResult, KingfisherError>) -> Void) = { result in
             self.tasks.removeValue(forKey: resource.downloadURL)
-            if let _ = result.error {
-                self.failedResources.append(resource)
-            } else {
+            do {
+                let _ = try result.get()
                 self.completedResources.append(resource)
+            } catch {
+                self.failedResources.append(resource)
             }
             
             self.reportProgress()

+ 150 - 136
Sources/Utility/Result.swift

@@ -26,50 +26,15 @@
 
 import Foundation
 
-// This is a copy from https://github.com/apple/swift/pull/19982
-// If this PR is merged to stblib later, we may need to remove these content by a Swift version flag.
-
 /// A value that represents either a success or failure, capturing associated
 /// values in both cases.
-public enum Result<Value, Error> {
+public enum Result<Success, Failure> {
     /// A success, storing a `Value`.
-    case success(Value)
-    
+    case success(Success)
+
     /// A failure, storing an `Error`.
-    case failure(Error)
-    
-    /// The stored value of a successful `Result`. `nil` if the `Result` was a
-    /// failure.
-    public var value: Value? {
-        switch self {
-        case let .success(value):
-            return value
-        case .failure:
-            return nil
-        }
-    }
-    
-    /// The stored value of a failure `Result`. `nil` if the `Result` was a
-    /// success.
-    public var error: Error? {
-        switch self {
-        case let .failure(error):
-            return error
-        case .success:
-            return nil
-        }
-    }
-    
-    /// A Boolean value indicating whether the `Result` as a success.
-    public var isSuccess: Bool {
-        switch self {
-        case .success:
-            return true
-        case .failure:
-            return false
-        }
-    }
-    
+    case failure(Failure)
+
     /// Evaluates the given transform closure when this `Result` instance is
     /// `.success`, passing the value as a parameter.
     ///
@@ -79,17 +44,17 @@ public enum Result<Value, Error> {
     ///   instance.
     /// - Returns: A new `Result` instance with the result of the transform, if
     ///   it was applied.
-    public func map<NewValue>(
-        _ transform: (Value) -> NewValue
-        ) -> Result<NewValue, Error> {
+    public func map<NewSuccess>(
+        _ transform: (Success) -> NewSuccess
+        ) -> Result<NewSuccess, Failure> {
         switch self {
-        case let .success(value):
-            return .success(transform(value))
-        case let .failure(error):
-            return .failure(error)
+        case let .success(success):
+            return .success(transform(success))
+        case let .failure(failure):
+            return .failure(failure)
         }
     }
-    
+
     /// Evaluates the given transform closure when this `Result` instance is
     /// `.failure`, passing the error as a parameter.
     ///
@@ -100,17 +65,17 @@ public enum Result<Value, Error> {
     ///   instance.
     /// - Returns: A new `Result` instance with the result of the transform, if
     ///   it was applied.
-    public func mapError<NewError>(
-        _ transform: (Error) -> NewError
-        ) -> Result<Value, NewError> {
+    public func mapError<NewFailure>(
+        _ transform: (Failure) -> NewFailure
+        ) -> Result<Success, NewFailure> {
         switch self {
-        case let .success(value):
-            return .success(value)
-        case let .failure(error):
-            return .failure(transform(error))
+        case let .success(success):
+            return .success(success)
+        case let .failure(failure):
+            return .failure(transform(failure))
         }
     }
-    
+
     /// Evaluates the given transform closure when this `Result` instance is
     /// `.success`, passing the value as a parameter and flattening the result.
     ///
@@ -118,17 +83,17 @@ public enum Result<Value, Error> {
     ///   instance.
     /// - Returns: A new `Result` instance, either from the transform or from
     ///   the previous error value.
-    public func flatMap<NewValue>(
-        _ transform: (Value) -> Result<NewValue, Error>
-        ) -> Result<NewValue, Error> {
+    public func flatMap<NewSuccess>(
+        _ transform: (Success) -> Result<NewSuccess, Failure>
+        ) -> Result<NewSuccess, Failure> {
         switch self {
-        case let .success(value):
-            return transform(value)
-        case let .failure(error):
-            return .failure(error)
+        case let .success(success):
+            return transform(success)
+        case let .failure(failure):
+            return .failure(failure)
         }
     }
-    
+
     /// Evaluates the given transform closure when this `Result` instance is
     /// `.failure`, passing the error as a parameter and flattening the result.
     ///
@@ -136,42 +101,50 @@ public enum Result<Value, Error> {
     ///   instance.
     /// - Returns: A new `Result` instance, either from the transform or from
     ///   the previous success value.
-    public func flatMapError<NewError>(
-        _ transform: (Error) -> Result<Value, NewError>
-        ) -> Result<Value, NewError> {
+    public func flatMapError<NewFailure>(
+        _ transform: (Failure) -> Result<Success, NewFailure>
+        ) -> Result<Success, NewFailure> {
         switch self {
-        case let .success(value):
-            return .success(value)
-        case let .failure(error):
-            return transform(error)
+        case let .success(success):
+            return .success(success)
+        case let .failure(failure):
+            return transform(failure)
         }
     }
-    
-    /// Evaluates the given transform closures to create a single output value.
+}
+
+extension Result where Failure: Error {
+    /// Returns the success value as a throwing expression.
     ///
-    /// - Parameters:
-    ///   - onSuccess: A closure that transforms the success value.
-    ///   - onFailure: A closure that transforms the error value.
-    /// - Returns: A single `Output` value.
-    public func fold<Output>(
-        onSuccess: (Value) -> Output,
-        onFailure: (Error) -> Output
-        ) -> Output {
+    /// Use this method to retrieve the value of this result if it represents a
+    /// success, or to catch the value if it represents a failure.
+    ///
+    ///     let integerResult: Result<Int, Error> = .success(5)
+    ///     do {
+    ///         let value = try integerResult.get()
+    ///         print("The value is \(value).")
+    ///     } catch error {
+    ///         print("Error retrieving the value: \(error)")
+    ///     }
+    ///     // Prints "The value is 5."
+    ///
+    /// - Returns: The success value, if the instance represents a success.
+    /// - Throws: The failure value, if the instance represents a failure.
+    public func get() throws -> Success {
         switch self {
-        case let .success(value):
-            return onSuccess(value)
-        case let .failure(error):
-            return onFailure(error)
+        case let .success(success):
+            return success
+        case let .failure(failure):
+            throw failure
         }
     }
-}
 
-extension Result where Error : Swift.Error {
     /// Unwraps the `Result` into a throwing expression.
     ///
     /// - Returns: The success value, if the instance is a success.
     /// - Throws:  The error value, if the instance is a failure.
-    public func unwrapped() throws -> Value {
+    @available(*, deprecated, message: "This method will be removed soon. Use `get() throws -> Success` instead.")
+    public func unwrapped() throws -> Success {
         switch self {
         case let .success(value):
             return value
@@ -181,78 +154,119 @@ extension Result where Error : Swift.Error {
     }
 }
 
-extension Result where Error == Swift.Error {
-    /// Create an instance by capturing the output of a throwing closure.
+extension Result where Failure == Swift.Error {
+    /// Creates a new result by evaluating a throwing closure, capturing the
+    /// returned value as a success, or any thrown error as a failure.
     ///
-    /// - Parameter throwing: A throwing closure to evaluate.
+    /// - Parameter body: A throwing closure to evaluate.
     @_transparent
-    public init(_ throwing: () throws -> Value) {
+    public init(catching body: () throws -> Success) {
         do {
-            let value = try throwing()
-            self = .success(value)
+            self = .success(try body())
         } catch {
             self = .failure(error)
         }
     }
-    
-    /// Unwraps the `Result` into a throwing expression.
-    ///
-    /// - Returns: The success value, if the instance is a success.
-    /// - Throws:  The error value, if the instance is a failure.
-    public func unwrapped() throws -> Value {
+}
+
+extension Result : Equatable where Success : Equatable, Failure: Equatable { }
+
+extension Result : Hashable where Success : Hashable, Failure : Hashable { }
+
+extension Result : CustomDebugStringConvertible {
+    public var debugDescription: String {
+        var output = "Result."
         switch self {
         case let .success(value):
-            return value
+            output += "success("
+            debugPrint(value, terminator: "", to: &output)
         case let .failure(error):
-            throw error
+            output += "failure("
+            debugPrint(error, terminator: "", to: &output)
         }
+        output += ")"
+
+        return output
     }
-    
-    /// Evaluates the given transform closure when this `Result` instance is
-    /// `.success`, passing the value as a parameter and flattening the result.
-    ///
-    /// - Parameter transform: A closure that takes the successful value of the
-    ///   instance.
-    /// - Returns: A new `Result` instance, either from the transform or from
-    ///   the previous error value.
-    public func flatMap<NewValue>(
-        _ transform: (Value) throws -> NewValue
-        ) -> Result<NewValue, Error> {
+}
+
+// Deprecated
+extension Result {
+
+    /// The stored value of a successful `Result`. `nil` if the `Result` was a failure.
+    @available(*, deprecated, message: "This method will be removed soon. Use `get() throws -> Success` instead.")
+    public var value: Success? {
         switch self {
         case let .success(value):
-            do {
-                return .success(try transform(value))
-            } catch {
-                return .failure(error)
-            }
-        case let .failure(error):
-            return .failure(error)
+            return value
+        case .failure:
+            return nil
         }
     }
-}
 
-extension Result : Equatable where Value : Equatable, Error : Equatable { }
+    /// The stored value of a failure `Result`. `nil` if the `Result` was a success.
+    @available(*, deprecated, message: "This method will be removed soon. Use `get() throws -> Success` instead.")
+    public var error: Failure? {
+        switch self {
+        case let .failure(error):
+            return error
+        case .success:
+            return nil
+        }
+    }
 
-extension Result : Hashable where Value : Hashable, Error : Hashable {
-    public func hash(into hasher: inout Hasher) {
-        hasher.combine(value)
-        hasher.combine(error)
+    /// A Boolean value indicating whether the `Result` as a success.
+    @available(*, deprecated, message: "This method will be removed soon. Use methods defined in `Swift.Result`.")
+    public var isSuccess: Bool {
+        switch self {
+        case .success:
+            return true
+        case .failure:
+            return false
+        }
     }
 }
 
-extension Result : CustomDebugStringConvertible {
-    public var debugDescription: String {
-        var output = "Result."
+// These helper methods are not public since we do not want them to be exposed or cause any conflicting.
+// However, they are just wrapper of `ResultUtil` static methods.
+extension Result where Failure: Error {
+
+    /// Evaluates the given transform closures to create a single output value.
+    ///
+    /// - Parameters:
+    ///   - onSuccess: A closure that transforms the success value.
+    ///   - onFailure: A closure that transforms the error value.
+    /// - Returns: A single `Output` value.
+    func match<Output>(
+        onSuccess: (Success) -> Output,
+        onFailure: (Failure) -> Output) -> Output
+    {
         switch self {
         case let .success(value):
-            output += "success("
-            debugPrint(value, terminator: "", to: &output)
+            return onSuccess(value)
         case let .failure(error):
-            output += "failure("
-            debugPrint(error, terminator: "", to: &output)
+            return onFailure(error)
         }
-        output += ")"
-        
-        return output
+    }
+
+    func matchSuccess<Output>(with folder: (Success?) -> Output) -> Output {
+        return match(
+            onSuccess: { value in return folder(value) },
+            onFailure: { _ in return folder(nil) }
+        )
+    }
+
+    func matchFailure<Output>(with folder: (Error?) -> Output) -> Output {
+        return match(
+            onSuccess: { _ in return folder(nil) },
+            onFailure: { error in return folder(error) }
+        )
+    }
+
+    func match<Output>(with folder: (Success?, Error?) -> Output) -> Output {
+        return match(
+            onSuccess: { return folder($0, nil) },
+            onFailure: { return folder(nil, $0) }
+        )
     }
 }

+ 6 - 0
Sources/Utility/String+MD5.swift

@@ -34,9 +34,15 @@ extension KingfisherWrapper where Base == String {
             return base
         }
         var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
+        #if swift(>=5.0)
+        _ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
+            return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest)
+        }
+        #else
         _ = data.withUnsafeBytes { bytes in
             return CC_MD5(bytes, CC_LONG(data.count), &digest)
         }
+        #endif
         
         return digest.map { String(format: "%02x", $0) }.joined()
     }

+ 12 - 10
Tests/KingfisherTests/ImageExtensionTests.swift

@@ -42,8 +42,11 @@ class ImageExtensionTests: XCTestCase {
         XCTAssertEqual(format, .GIF)
         
         let raw: [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8]
-        
+        #if swift(>=5.0)
+        format = Data(raw).kf.imageFormat
+        #else
         format = Data(bytes: raw).kf.imageFormat
+        #endif
         XCTAssertEqual(format, .unknown)
     }
     
@@ -58,23 +61,23 @@ class ImageExtensionTests: XCTestCase {
         let options = ImageCreatingOptions()
         let image = KingfisherWrapper<Image>.animatedImage(data: testImageGIFData, options: options)
         XCTAssertNotNil(image)
-#if os(iOS) || os(tvOS)
+        #if os(iOS) || os(tvOS)
         let count = CGImageSourceGetCount(image!.kf.imageSource!)
         XCTAssertEqual(count, 8)
-#else
+        #else
         XCTAssertEqual(image!.kf.images!.count, 8)
         XCTAssertEqual(image!.kf.duration, 0.8, accuracy: 0.001)
-#endif
+        #endif
     }
 
-#if os(iOS) || os(tvOS)
+    #if os(iOS) || os(tvOS)
     func testScaleForGIFImage() {
         let options = ImageCreatingOptions(scale: 2.0, duration: 0.0, preloadAll: false, onlyFirstFrame: false)
         let image = KingfisherWrapper<Image>.animatedImage(data: testImageGIFData, options: options)
         XCTAssertNotNil(image)
         XCTAssertEqual(image!.scale, 2.0)
     }
-#endif
+    #endif
 
     func testGIFRepresentation() {
         let options = ImageCreatingOptions()
@@ -95,14 +98,13 @@ class ImageExtensionTests: XCTestCase {
         let options = ImageCreatingOptions()
         let image = KingfisherWrapper<Image>.animatedImage(data: testImageSingleFrameGIFData, options: options)
         XCTAssertNotNil(image)
-#if os(iOS) || os(tvOS)
+        #if os(iOS) || os(tvOS)
         let count = CGImageSourceGetCount(image!.kf.imageSource!)
         XCTAssertEqual(count, 1)
-#else
+        #else
         XCTAssertEqual(image!.kf.images!.count, 1)
-        
         XCTAssertEqual(image!.kf.duration, Double.infinity)
-#endif
+        #endif
     }
     
     func testGenerateFromNonImage() {

+ 17 - 0
Tests/KingfisherTests/KingfisherTestHelper.swift

@@ -214,3 +214,20 @@ extension Data {
 extension Bundle {
     static let test: Bundle = Bundle(for: ImageExtensionTests.self)
 }
+
+// Make tests happier with old Result type
+extension Kingfisher.Result {
+    var value: Success? {
+        switch self {
+        case .success(let success): return success
+        case .failure: return nil
+        }
+    }
+
+    var error: Failure? {
+        switch self {
+        case .success: return nil
+        case .failure(let failure): return failure
+        }
+    }
+}