You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
11 KiB
300 lines
11 KiB
// |
|
// Result.swift |
|
// |
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/) |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy |
|
// of this software and associated documentation files (the "Software"), to deal |
|
// in the Software without restriction, including without limitation the rights |
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
// copies of the Software, and to permit persons to whom the Software is |
|
// furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in |
|
// all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
// THE SOFTWARE. |
|
// |
|
|
|
import Foundation |
|
|
|
/// Used to represent whether a request was successful or encountered an error. |
|
/// |
|
/// - success: The request and all post processing operations were successful resulting in the serialization of the |
|
/// provided associated value. |
|
/// |
|
/// - failure: The request encountered an error resulting in a failure. The associated values are the original data |
|
/// provided by the server as well as the error that caused the failure. |
|
public enum Result<Value> { |
|
case success(Value) |
|
case failure(Error) |
|
|
|
/// Returns `true` if the result is a success, `false` otherwise. |
|
public var isSuccess: Bool { |
|
switch self { |
|
case .success: |
|
return true |
|
case .failure: |
|
return false |
|
} |
|
} |
|
|
|
/// Returns `true` if the result is a failure, `false` otherwise. |
|
public var isFailure: Bool { |
|
return !isSuccess |
|
} |
|
|
|
/// Returns the associated value if the result is a success, `nil` otherwise. |
|
public var value: Value? { |
|
switch self { |
|
case .success(let value): |
|
return value |
|
case .failure: |
|
return nil |
|
} |
|
} |
|
|
|
/// Returns the associated error value if the result is a failure, `nil` otherwise. |
|
public var error: Error? { |
|
switch self { |
|
case .success: |
|
return nil |
|
case .failure(let error): |
|
return error |
|
} |
|
} |
|
} |
|
|
|
// MARK: - CustomStringConvertible |
|
|
|
extension Result: CustomStringConvertible { |
|
/// The textual representation used when written to an output stream, which includes whether the result was a |
|
/// success or failure. |
|
public var description: String { |
|
switch self { |
|
case .success: |
|
return "SUCCESS" |
|
case .failure: |
|
return "FAILURE" |
|
} |
|
} |
|
} |
|
|
|
// MARK: - CustomDebugStringConvertible |
|
|
|
extension Result: CustomDebugStringConvertible { |
|
/// The debug textual representation used when written to an output stream, which includes whether the result was a |
|
/// success or failure in addition to the value or error. |
|
public var debugDescription: String { |
|
switch self { |
|
case .success(let value): |
|
return "SUCCESS: \(value)" |
|
case .failure(let error): |
|
return "FAILURE: \(error)" |
|
} |
|
} |
|
} |
|
|
|
// MARK: - Functional APIs |
|
|
|
extension Result { |
|
/// Creates a `Result` instance from the result of a closure. |
|
/// |
|
/// A failure result is created when the closure throws, and a success result is created when the closure |
|
/// succeeds without throwing an error. |
|
/// |
|
/// func someString() throws -> String { ... } |
|
/// |
|
/// let result = Result(value: { |
|
/// return try someString() |
|
/// }) |
|
/// |
|
/// // The type of result is Result<String> |
|
/// |
|
/// The trailing closure syntax is also supported: |
|
/// |
|
/// let result = Result { try someString() } |
|
/// |
|
/// - parameter value: The closure to execute and create the result for. |
|
public init(value: () throws -> Value) { |
|
do { |
|
self = try .success(value()) |
|
} catch { |
|
self = .failure(error) |
|
} |
|
} |
|
|
|
/// Returns the success value, or throws the failure error. |
|
/// |
|
/// let possibleString: Result<String> = .success("success") |
|
/// try print(possibleString.unwrap()) |
|
/// // Prints "success" |
|
/// |
|
/// let noString: Result<String> = .failure(error) |
|
/// try print(noString.unwrap()) |
|
/// // Throws error |
|
public func unwrap() throws -> Value { |
|
switch self { |
|
case .success(let value): |
|
return value |
|
case .failure(let error): |
|
throw error |
|
} |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. |
|
/// |
|
/// Use the `map` method with a closure that does not throw. For example: |
|
/// |
|
/// let possibleData: Result<Data> = .success(Data()) |
|
/// let possibleInt = possibleData.map { $0.count } |
|
/// try print(possibleInt.unwrap()) |
|
/// // Prints "0" |
|
/// |
|
/// let noData: Result<Data> = .failure(error) |
|
/// let noInt = noData.map { $0.count } |
|
/// try print(noInt.unwrap()) |
|
/// // Throws error |
|
/// |
|
/// - parameter transform: A closure that takes the success value of the `Result` instance. |
|
/// |
|
/// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the |
|
/// same failure. |
|
public func map<T>(_ transform: (Value) -> T) -> Result<T> { |
|
switch self { |
|
case .success(let value): |
|
return .success(transform(value)) |
|
case .failure(let error): |
|
return .failure(error) |
|
} |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. |
|
/// |
|
/// Use the `flatMap` method with a closure that may throw an error. For example: |
|
/// |
|
/// let possibleData: Result<Data> = .success(Data(...)) |
|
/// let possibleObject = possibleData.flatMap { |
|
/// try JSONSerialization.jsonObject(with: $0) |
|
/// } |
|
/// |
|
/// - parameter transform: A closure that takes the success value of the instance. |
|
/// |
|
/// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the |
|
/// same failure. |
|
public func flatMap<T>(_ transform: (Value) throws -> T) -> Result<T> { |
|
switch self { |
|
case .success(let value): |
|
do { |
|
return try .success(transform(value)) |
|
} catch { |
|
return .failure(error) |
|
} |
|
case .failure(let error): |
|
return .failure(error) |
|
} |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. |
|
/// |
|
/// Use the `mapError` function with a closure that does not throw. For example: |
|
/// |
|
/// let possibleData: Result<Data> = .failure(someError) |
|
/// let withMyError: Result<Data> = possibleData.mapError { MyError.error($0) } |
|
/// |
|
/// - Parameter transform: A closure that takes the error of the instance. |
|
/// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns |
|
/// the same instance. |
|
public func mapError<T: Error>(_ transform: (Error) -> T) -> Result { |
|
switch self { |
|
case .failure(let error): |
|
return .failure(transform(error)) |
|
case .success: |
|
return self |
|
} |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. |
|
/// |
|
/// Use the `flatMapError` function with a closure that may throw an error. For example: |
|
/// |
|
/// let possibleData: Result<Data> = .success(Data(...)) |
|
/// let possibleObject = possibleData.flatMapError { |
|
/// try someFailableFunction(taking: $0) |
|
/// } |
|
/// |
|
/// - Parameter transform: A throwing closure that takes the error of the instance. |
|
/// |
|
/// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns |
|
/// the same instance. |
|
public func flatMapError<T: Error>(_ transform: (Error) throws -> T) -> Result { |
|
switch self { |
|
case .failure(let error): |
|
do { |
|
return try .failure(transform(error)) |
|
} catch { |
|
return .failure(error) |
|
} |
|
case .success: |
|
return self |
|
} |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. |
|
/// |
|
/// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance. |
|
/// |
|
/// - Parameter closure: A closure that takes the success value of this instance. |
|
/// - Returns: This `Result` instance, unmodified. |
|
@discardableResult |
|
public func withValue(_ closure: (Value) throws -> Void) rethrows -> Result { |
|
if case let .success(value) = self { try closure(value) } |
|
|
|
return self |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter. |
|
/// |
|
/// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance. |
|
/// |
|
/// - Parameter closure: A closure that takes the success value of this instance. |
|
/// - Returns: This `Result` instance, unmodified. |
|
@discardableResult |
|
public func withError(_ closure: (Error) throws -> Void) rethrows -> Result { |
|
if case let .failure(error) = self { try closure(error) } |
|
|
|
return self |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a success. |
|
/// |
|
/// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance. |
|
/// |
|
/// - Parameter closure: A `Void` closure. |
|
/// - Returns: This `Result` instance, unmodified. |
|
@discardableResult |
|
public func ifSuccess(_ closure: () throws -> Void) rethrows -> Result { |
|
if isSuccess { try closure() } |
|
|
|
return self |
|
} |
|
|
|
/// Evaluates the specified closure when the `Result` is a failure. |
|
/// |
|
/// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance. |
|
/// |
|
/// - Parameter closure: A `Void` closure. |
|
/// - Returns: This `Result` instance, unmodified. |
|
@discardableResult |
|
public func ifFailure(_ closure: () throws -> Void) rethrows -> Result { |
|
if isFailure { try closure() } |
|
|
|
return self |
|
} |
|
}
|
|
|