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.

239 lines
8.3 KiB

  1. //
  2. // Result.swift
  3. // Kingfisher
  4. //
  5. // Created by onevcat on 2018/09/22.
  6. //
  7. // Copyright (c) 2019 Wei Wang <onevcat@gmail.com>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. import Foundation
  27. #if swift(>=4.3)
  28. /// Result type already built-in
  29. #else
  30. /// A value that represents either a success or failure, capturing associated
  31. /// values in both cases.
  32. public enum Result<Success, Failure> {
  33. /// A success, storing a `Value`.
  34. case success(Success)
  35. /// A failure, storing an `Error`.
  36. case failure(Failure)
  37. /// Evaluates the given transform closure when this `Result` instance is
  38. /// `.success`, passing the value as a parameter.
  39. ///
  40. /// Use the `map` method with a closure that returns a non-`Result` value.
  41. ///
  42. /// - Parameter transform: A closure that takes the successful value of the
  43. /// instance.
  44. /// - Returns: A new `Result` instance with the result of the transform, if
  45. /// it was applied.
  46. public func map<NewSuccess>(
  47. _ transform: (Success) -> NewSuccess
  48. ) -> Result<NewSuccess, Failure> {
  49. switch self {
  50. case let .success(success):
  51. return .success(transform(success))
  52. case let .failure(failure):
  53. return .failure(failure)
  54. }
  55. }
  56. /// Evaluates the given transform closure when this `Result` instance is
  57. /// `.failure`, passing the error as a parameter.
  58. ///
  59. /// Use the `mapError` method with a closure that returns a non-`Result`
  60. /// value.
  61. ///
  62. /// - Parameter transform: A closure that takes the failure value of the
  63. /// instance.
  64. /// - Returns: A new `Result` instance with the result of the transform, if
  65. /// it was applied.
  66. public func mapError<NewFailure>(
  67. _ transform: (Failure) -> NewFailure
  68. ) -> Result<Success, NewFailure> {
  69. switch self {
  70. case let .success(success):
  71. return .success(success)
  72. case let .failure(failure):
  73. return .failure(transform(failure))
  74. }
  75. }
  76. /// Evaluates the given transform closure when this `Result` instance is
  77. /// `.success`, passing the value as a parameter and flattening the result.
  78. ///
  79. /// - Parameter transform: A closure that takes the successful value of the
  80. /// instance.
  81. /// - Returns: A new `Result` instance, either from the transform or from
  82. /// the previous error value.
  83. public func flatMap<NewSuccess>(
  84. _ transform: (Success) -> Result<NewSuccess, Failure>
  85. ) -> Result<NewSuccess, Failure> {
  86. switch self {
  87. case let .success(success):
  88. return transform(success)
  89. case let .failure(failure):
  90. return .failure(failure)
  91. }
  92. }
  93. /// Evaluates the given transform closure when this `Result` instance is
  94. /// `.failure`, passing the error as a parameter and flattening the result.
  95. ///
  96. /// - Parameter transform: A closure that takes the error value of the
  97. /// instance.
  98. /// - Returns: A new `Result` instance, either from the transform or from
  99. /// the previous success value.
  100. public func flatMapError<NewFailure>(
  101. _ transform: (Failure) -> Result<Success, NewFailure>
  102. ) -> Result<Success, NewFailure> {
  103. switch self {
  104. case let .success(success):
  105. return .success(success)
  106. case let .failure(failure):
  107. return transform(failure)
  108. }
  109. }
  110. }
  111. extension Result where Failure: Error {
  112. /// Returns the success value as a throwing expression.
  113. ///
  114. /// Use this method to retrieve the value of this result if it represents a
  115. /// success, or to catch the value if it represents a failure.
  116. ///
  117. /// let integerResult: Result<Int, Error> = .success(5)
  118. /// do {
  119. /// let value = try integerResult.get()
  120. /// print("The value is \(value).")
  121. /// } catch error {
  122. /// print("Error retrieving the value: \(error)")
  123. /// }
  124. /// // Prints "The value is 5."
  125. ///
  126. /// - Returns: The success value, if the instance represents a success.
  127. /// - Throws: The failure value, if the instance represents a failure.
  128. public func get() throws -> Success {
  129. switch self {
  130. case let .success(success):
  131. return success
  132. case let .failure(failure):
  133. throw failure
  134. }
  135. }
  136. /// Unwraps the `Result` into a throwing expression.
  137. ///
  138. /// - Returns: The success value, if the instance is a success.
  139. /// - Throws: The error value, if the instance is a failure.
  140. @available(*, deprecated, message: "This method will be removed soon. Use `get() throws -> Success` instead.")
  141. public func unwrapped() throws -> Success {
  142. switch self {
  143. case let .success(value):
  144. return value
  145. case let .failure(error):
  146. throw error
  147. }
  148. }
  149. }
  150. extension Result where Failure == Swift.Error {
  151. /// Creates a new result by evaluating a throwing closure, capturing the
  152. /// returned value as a success, or any thrown error as a failure.
  153. ///
  154. /// - Parameter body: A throwing closure to evaluate.
  155. @_transparent
  156. public init(catching body: () throws -> Success) {
  157. do {
  158. self = .success(try body())
  159. } catch {
  160. self = .failure(error)
  161. }
  162. }
  163. }
  164. extension Result : Equatable where Success : Equatable, Failure: Equatable { }
  165. extension Result : Hashable where Success : Hashable, Failure : Hashable { }
  166. extension Result : CustomDebugStringConvertible {
  167. public var debugDescription: String {
  168. var output = "Result."
  169. switch self {
  170. case let .success(value):
  171. output += "success("
  172. debugPrint(value, terminator: "", to: &output)
  173. case let .failure(error):
  174. output += "failure("
  175. debugPrint(error, terminator: "", to: &output)
  176. }
  177. output += ")"
  178. return output
  179. }
  180. }
  181. #endif
  182. // These helper methods are not public since we do not want them to be exposed or cause any conflicting.
  183. // However, they are just wrapper of `ResultUtil` static methods.
  184. extension Result where Failure: Error {
  185. /// Evaluates the given transform closures to create a single output value.
  186. ///
  187. /// - Parameters:
  188. /// - onSuccess: A closure that transforms the success value.
  189. /// - onFailure: A closure that transforms the error value.
  190. /// - Returns: A single `Output` value.
  191. func match<Output>(
  192. onSuccess: (Success) -> Output,
  193. onFailure: (Failure) -> Output) -> Output
  194. {
  195. switch self {
  196. case let .success(value):
  197. return onSuccess(value)
  198. case let .failure(error):
  199. return onFailure(error)
  200. }
  201. }
  202. func matchSuccess<Output>(with folder: (Success?) -> Output) -> Output {
  203. return match(
  204. onSuccess: { value in return folder(value) },
  205. onFailure: { _ in return folder(nil) }
  206. )
  207. }
  208. func matchFailure<Output>(with folder: (Error?) -> Output) -> Output {
  209. return match(
  210. onSuccess: { _ in return folder(nil) },
  211. onFailure: { error in return folder(error) }
  212. )
  213. }
  214. func match<Output>(with folder: (Success?, Error?) -> Output) -> Output {
  215. return match(
  216. onSuccess: { return folder($0, nil) },
  217. onFailure: { return folder(nil, $0) }
  218. )
  219. }
  220. }