diff --git a/Result/Result.swift b/Result/Result.swift index a6416c1..4d5e8a0 100644 --- a/Result/Result.swift +++ b/Result/Result.swift @@ -108,10 +108,24 @@ public enum Result: ResultProtocol, CustomStringConvertib // MARK: - Derive result from failable closure +public func materialize(_ f: () throws -> T) -> Result { + return materialize(try f()) +} + +public func materialize(_ f: @autoclosure () throws -> T) -> Result { + do { + return .success(try f()) + } catch { + return .failure(AnyError(error)) + } +} + +@available(*, deprecated, message: "Use the overload which returns `Result` instead") public func materialize(_ f: () throws -> T) -> Result { return materialize(try f()) } +@available(*, deprecated, message: "Use the overload which returns `Result` instead") public func materialize(_ f: @autoclosure () throws -> T) -> Result { do { return .success(try f()) @@ -160,7 +174,7 @@ extension NSError: ErrorProtocolConvertible { } } -// MARK: - +// MARK: - Errors /// An “error” that is impossible to construct. /// @@ -169,6 +183,33 @@ extension NSError: ErrorProtocolConvertible { /// contains an `Int`eger and is guaranteed never to be a `failure`. public enum NoError: Swift.Error { } +/// A type-erased error which wraps an arbitrary error instance. This should be +/// useful for generic contexts. +public struct AnyError: Swift.Error { + /// The underlying error. + public let error: Swift.Error + + public init(_ error: Swift.Error) { + if let anyError = error as? AnyError { + self = anyError + } else { + self.error = error + } + } +} + +extension AnyError: ErrorProtocolConvertible { + public static func error(from error: Error) -> AnyError { + return AnyError(error) + } +} + +extension AnyError: CustomStringConvertible { + public var description: String { + return String(describing: error) + } +} + // MARK: - migration support extension Result { @available(*, unavailable, renamed: "success") diff --git a/Tests/ResultTests/ResultTests.swift b/Tests/ResultTests/ResultTests.swift index e0285e7..b5a7dbe 100644 --- a/Tests/ResultTests/ResultTests.swift +++ b/Tests/ResultTests/ResultTests.swift @@ -38,71 +38,54 @@ final class ResultTests: XCTestCase { // MARK: Try - Catch func testTryCatchProducesSuccesses() { - let result: Result = Result(try tryIsSuccess("success")) + let result: Result = Result(try tryIsSuccess("success")) XCTAssert(result == success) } func testTryCatchProducesFailures() { - #if os(Linux) - /// FIXME: skipped on Linux because of crash with swift-3.0-PREVIEW-4. - print("Test Case `\(#function)` skipped on Linux because of crash with swift-3.0-PREVIEW-4.") - #else - let result: Result = Result(try tryIsSuccess(nil)) - XCTAssert(result.error == error) - #endif + let result: Result = Result(try tryIsSuccess(nil)) + XCTAssert(result.error == error) } func testTryCatchWithFunctionProducesSuccesses() { let function = { try tryIsSuccess("success") } - let result: Result = Result(attempt: function) + let result: Result = Result(attempt: function) XCTAssert(result == success) } func testTryCatchWithFunctionCatchProducesFailures() { - #if os(Linux) - /// FIXME: skipped on Linux because of crash with swift-3.0-PREVIEW-4. - print("Test Case `\(#function)` skipped on Linux because of crash with swift-3.0-PREVIEW-4.") - #else - let function = { try tryIsSuccess(nil) } + let function = { try tryIsSuccess(nil) } - let result: Result = Result(attempt: function) - XCTAssert(result.error == error) - #endif + let result: Result = Result(attempt: function) + XCTAssert(result.error == error) } func testMaterializeProducesSuccesses() { - let result1 = materialize(try tryIsSuccess("success")) + let result1: Result = materialize(try tryIsSuccess("success")) XCTAssert(result1 == success) - let result2: Result = materialize { try tryIsSuccess("success") } + let result2: Result = materialize { try tryIsSuccess("success") } XCTAssert(result2 == success) } func testMaterializeProducesFailures() { - #if os(Linux) - /// FIXME: skipped on Linux because of crash with swift-3.0-PREVIEW-4. - print("Test Case `\(#function)` skipped on Linux because of crash with swift-3.0-PREVIEW-4.") - #else - let result1 = materialize(try tryIsSuccess(nil)) - XCTAssert(result1.error == error) + let result1: Result = materialize(try tryIsSuccess(nil)) + XCTAssert(result1.error == error) - let result2: Result = materialize { try tryIsSuccess(nil) } - XCTAssert(result2.error == error) - #endif + let result2: Result = materialize { try tryIsSuccess(nil) } + XCTAssert(result2.error == error) } // MARK: Recover func testRecoverProducesLeftForLeftSuccess() { - let left = Result.success("left") + let left = Result.success("left") XCTAssertEqual(left.recover("right"), "left") } func testRecoverProducesRightForLeftFailure() { - struct Error: Swift.Error {} - - let left = Result.failure(Error()) + let left = Result.failure(Error.a) XCTAssertEqual(left.recover("right"), "right") } @@ -169,13 +152,8 @@ final class ResultTests: XCTestCase { } func testTryMapProducesFailure() { - #if os(Linux) - /// FIXME: skipped on Linux because of crash with swift-3.0-PREVIEW-4. - print("Test Case `\(#function)` skipped on Linux because of crash with swift-3.0-PREVIEW-4.") - #else - let result = Result.success("fail").tryMap(tryIsSuccess) - XCTAssert(result == failure) - #endif + let result = Result.success("fail").tryMap(tryIsSuccess) + XCTAssert(result == failure) } // MARK: Operators @@ -202,15 +180,26 @@ final class ResultTests: XCTestCase { // MARK: - Fixtures -let success = Result.success("success") -let error = NSError(domain: "com.antitypical.Result", code: 1, userInfo: nil) -let error2 = NSError(domain: "com.antitypical.Result", code: 2, userInfo: nil) -let failure = Result.failure(error) -let failure2 = Result.failure(error2) +private enum Error: Swift.Error { + case a, b +} + +let success = Result.success("success") +let error = AnyError(Error.a) +let error2 = AnyError(Error.b) +let failure = Result.failure(error) +let failure2 = Result.failure(error2) // MARK: - Helpers +extension AnyError: Equatable { + public static func ==(lhs: AnyError, rhs: AnyError) -> Bool { + return lhs.error._code == rhs.error._code + && lhs.error._domain == rhs.error._domain + } +} + #if !os(Linux) func attempt(_ value: T, succeed: Bool, error: NSErrorPointer) -> T? {