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

6 years ago
5 years ago
6 years ago
5 years ago
6 years ago
5 years ago
6 years ago
5 years ago
6 years ago
5 years ago
6 years ago
  1. //
  2. // Result.swift
  3. //
  4. // Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. import Foundation
  25. /// Used to represent whether a request was successful or encountered an error.
  26. ///
  27. /// - success: The request and all post processing operations were successful resulting in the serialization of the
  28. /// provided associated value.
  29. ///
  30. /// - failure: The request encountered an error resulting in a failure. The associated values are the original data
  31. /// provided by the server as well as the error that caused the failure.
  32. public enum Result<Value> {
  33. case success(Value)
  34. case failure(Error)
  35. /// Returns `true` if the result is a success, `false` otherwise.
  36. public var isSuccess: Bool {
  37. switch self {
  38. case .success:
  39. return true
  40. case .failure:
  41. return false
  42. }
  43. }
  44. /// Returns `true` if the result is a failure, `false` otherwise.
  45. public var isFailure: Bool {
  46. return !isSuccess
  47. }
  48. /// Returns the associated value if the result is a success, `nil` otherwise.
  49. public var value: Value? {
  50. switch self {
  51. case .success(let value):
  52. return value
  53. case .failure:
  54. return nil
  55. }
  56. }
  57. /// Returns the associated error value if the result is a failure, `nil` otherwise.
  58. public var error: Error? {
  59. switch self {
  60. case .success:
  61. return nil
  62. case .failure(let error):
  63. return error
  64. }
  65. }
  66. }
  67. // MARK: - CustomStringConvertible
  68. extension Result: CustomStringConvertible {
  69. /// The textual representation used when written to an output stream, which includes whether the result was a
  70. /// success or failure.
  71. public var description: String {
  72. switch self {
  73. case .success:
  74. return "SUCCESS"
  75. case .failure:
  76. return "FAILURE"
  77. }
  78. }
  79. }
  80. // MARK: - CustomDebugStringConvertible
  81. extension Result: CustomDebugStringConvertible {
  82. /// The debug textual representation used when written to an output stream, which includes whether the result was a
  83. /// success or failure in addition to the value or error.
  84. public var debugDescription: String {
  85. switch self {
  86. case .success(let value):
  87. return "SUCCESS: \(value)"
  88. case .failure(let error):
  89. return "FAILURE: \(error)"
  90. }
  91. }
  92. }
  93. // MARK: - Functional APIs
  94. extension Result {
  95. /// Creates a `Result` instance from the result of a closure.
  96. ///
  97. /// A failure result is created when the closure throws, and a success result is created when the closure
  98. /// succeeds without throwing an error.
  99. ///
  100. /// func someString() throws -> String { ... }
  101. ///
  102. /// let result = Result(value: {
  103. /// return try someString()
  104. /// })
  105. ///
  106. /// // The type of result is Result<String>
  107. ///
  108. /// The trailing closure syntax is also supported:
  109. ///
  110. /// let result = Result { try someString() }
  111. ///
  112. /// - parameter value: The closure to execute and create the result for.
  113. public init(value: () throws -> Value) {
  114. do {
  115. self = try .success(value())
  116. } catch {
  117. self = .failure(error)
  118. }
  119. }
  120. /// Returns the success value, or throws the failure error.
  121. ///
  122. /// let possibleString: Result<String> = .success("success")
  123. /// try print(possibleString.unwrap())
  124. /// // Prints "success"
  125. ///
  126. /// let noString: Result<String> = .failure(error)
  127. /// try print(noString.unwrap())
  128. /// // Throws error
  129. public func unwrap() throws -> Value {
  130. switch self {
  131. case .success(let value):
  132. return value
  133. case .failure(let error):
  134. throw error
  135. }
  136. }
  137. /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
  138. ///
  139. /// Use the `map` method with a closure that does not throw. For example:
  140. ///
  141. /// let possibleData: Result<Data> = .success(Data())
  142. /// let possibleInt = possibleData.map { $0.count }
  143. /// try print(possibleInt.unwrap())
  144. /// // Prints "0"
  145. ///
  146. /// let noData: Result<Data> = .failure(error)
  147. /// let noInt = noData.map { $0.count }
  148. /// try print(noInt.unwrap())
  149. /// // Throws error
  150. ///
  151. /// - parameter transform: A closure that takes the success value of the `Result` instance.
  152. ///
  153. /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the
  154. /// same failure.
  155. public func map<T>(_ transform: (Value) -> T) -> Result<T> {
  156. switch self {
  157. case .success(let value):
  158. return .success(transform(value))
  159. case .failure(let error):
  160. return .failure(error)
  161. }
  162. }
  163. /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
  164. ///
  165. /// Use the `flatMap` method with a closure that may throw an error. For example:
  166. ///
  167. /// let possibleData: Result<Data> = .success(Data(...))
  168. /// let possibleObject = possibleData.flatMap {
  169. /// try JSONSerialization.jsonObject(with: $0)
  170. /// }
  171. ///
  172. /// - parameter transform: A closure that takes the success value of the instance.
  173. ///
  174. /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the
  175. /// same failure.
  176. public func flatMap<T>(_ transform: (Value) throws -> T) -> Result<T> {
  177. switch self {
  178. case .success(let value):
  179. do {
  180. return try .success(transform(value))
  181. } catch {
  182. return .failure(error)
  183. }
  184. case .failure(let error):
  185. return .failure(error)
  186. }
  187. }
  188. /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
  189. ///
  190. /// Use the `mapError` function with a closure that does not throw. For example:
  191. ///
  192. /// let possibleData: Result<Data> = .failure(someError)
  193. /// let withMyError: Result<Data> = possibleData.mapError { MyError.error($0) }
  194. ///
  195. /// - Parameter transform: A closure that takes the error of the instance.
  196. /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns
  197. /// the same instance.
  198. public func mapError<T: Error>(_ transform: (Error) -> T) -> Result {
  199. switch self {
  200. case .failure(let error):
  201. return .failure(transform(error))
  202. case .success:
  203. return self
  204. }
  205. }
  206. /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
  207. ///
  208. /// Use the `flatMapError` function with a closure that may throw an error. For example:
  209. ///
  210. /// let possibleData: Result<Data> = .success(Data(...))
  211. /// let possibleObject = possibleData.flatMapError {
  212. /// try someFailableFunction(taking: $0)
  213. /// }
  214. ///
  215. /// - Parameter transform: A throwing closure that takes the error of the instance.
  216. ///
  217. /// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns
  218. /// the same instance.
  219. public func flatMapError<T: Error>(_ transform: (Error) throws -> T) -> Result {
  220. switch self {
  221. case .failure(let error):
  222. do {
  223. return try .failure(transform(error))
  224. } catch {
  225. return .failure(error)
  226. }
  227. case .success:
  228. return self
  229. }
  230. }
  231. /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
  232. ///
  233. /// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance.
  234. ///
  235. /// - Parameter closure: A closure that takes the success value of this instance.
  236. /// - Returns: This `Result` instance, unmodified.
  237. @discardableResult
  238. public func withValue(_ closure: (Value) throws -> Void) rethrows -> Result {
  239. if case let .success(value) = self { try closure(value) }
  240. return self
  241. }
  242. /// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
  243. ///
  244. /// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance.
  245. ///
  246. /// - Parameter closure: A closure that takes the success value of this instance.
  247. /// - Returns: This `Result` instance, unmodified.
  248. @discardableResult
  249. public func withError(_ closure: (Error) throws -> Void) rethrows -> Result {
  250. if case let .failure(error) = self { try closure(error) }
  251. return self
  252. }
  253. /// Evaluates the specified closure when the `Result` is a success.
  254. ///
  255. /// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance.
  256. ///
  257. /// - Parameter closure: A `Void` closure.
  258. /// - Returns: This `Result` instance, unmodified.
  259. @discardableResult
  260. public func ifSuccess(_ closure: () throws -> Void) rethrows -> Result {
  261. if isSuccess { try closure() }
  262. return self
  263. }
  264. /// Evaluates the specified closure when the `Result` is a failure.
  265. ///
  266. /// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance.
  267. ///
  268. /// - Parameter closure: A `Void` closure.
  269. /// - Returns: This `Result` instance, unmodified.
  270. @discardableResult
  271. public func ifFailure(_ closure: () throws -> Void) rethrows -> Result {
  272. if isFailure { try closure() }
  273. return self
  274. }
  275. }