/
Result.swift
159 lines (135 loc) · 5.1 KB
/
Result.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/// Result
///
/// Container for a successful value (T) or a failure with an NSError
///
import Foundation
/// A success `Result` returning `value`
/// This form is preferred to `Result.Success(Box(value))` because it
// does not require dealing with `Box()`
public func success<T,E>(value: T) -> Result<T,E> {
return .Success(Box(value))
}
/// A failure `Result` returning `error`
/// The default error is an empty one so that `failure()` is legal
/// To assign this to a variable, you must explicitly give a type.
/// Otherwise the compiler has no idea what `T` is. This form is preferred
/// to Result.Failure(error) because it provides a useful default.
/// For example:
/// let fail: Result<Int> = failure()
///
/// Dictionary keys for default errors
public let ErrorFileKey = "LMErrorFile"
public let ErrorLineKey = "LMErrorLine"
private func defaultError(userInfo: [NSObject: AnyObject]) -> NSError {
return NSError(domain: "", code: 0, userInfo: userInfo)
}
private func defaultError(message: String, file: String = __FILE__, line: Int = __LINE__) -> NSError {
return defaultError([NSLocalizedDescriptionKey: message, ErrorFileKey: file, ErrorLineKey: line])
}
private func defaultError(file: String = __FILE__, line: Int = __LINE__) -> NSError {
return defaultError([ErrorFileKey: file, ErrorLineKey: line])
}
public func failure<T>(message: String, file: String = __FILE__, line: Int = __LINE__) -> Result<T,NSError> {
let userInfo: [NSObject : AnyObject] = [NSLocalizedDescriptionKey: message, ErrorFileKey: file, ErrorLineKey: line]
return failure(defaultError(userInfo))
}
public func failure<T>(file: String = __FILE__, line: Int = __LINE__) -> Result<T,NSError> {
let userInfo: [NSObject : AnyObject] = [ErrorFileKey: file, ErrorLineKey: line]
return failure(defaultError(userInfo))
}
public func failure<T,E>(error: E) -> Result<T,E> {
return .Failure(Box(error))
}
/// Construct a `Result` using a block which receives an error parameter.
/// Expected to return non-nil for success.
public func try<T>(f: NSErrorPointer -> T?, file: String = __FILE__, line: Int = __LINE__) -> Result<T,NSError> {
var error: NSError?
return f(&error).map(success) ?? failure(error ?? defaultError(file: file, line: line))
}
public func try(f: NSErrorPointer -> Bool, file: String = __FILE__, line: Int = __LINE__) -> Result<(),NSError> {
var error: NSError?
return f(&error) ? success(()) : failure(error ?? defaultError(file: file, line: line))
}
/// Container for a successful value (T) or a failure with an E
public enum Result<T,E> {
case Success(Box<T>)
case Failure(Box<E>)
/// The successful value as an Optional
public var value: T? {
switch self {
case .Success(let box): return box.unbox
case .Failure: return nil
}
}
/// The failing error as an Optional
public var error: E? {
switch self {
case .Success: return nil
case .Failure(let err): return err.unbox
}
}
public var isSuccess: Bool {
switch self {
case .Success: return true
case .Failure: return false
}
}
/// Return a new result after applying a transformation to a successful value.
/// Mapping a failure returns a new failure without evaluating the transform
public func map<U>(transform: T -> U) -> Result<U,E> {
switch self {
case Success(let box):
return .Success(Box(transform(box.unbox)))
case Failure(let err):
return .Failure(err)
}
}
/// Return a new result after applying a transformation (that itself
/// returns a result) to a successful value.
/// Calling with a failure returns a new failure without evaluating the transform
public func flatMap<U>(transform:T -> Result<U,E>) -> Result<U,E> {
switch self {
case Success(let value): return transform(value.unbox)
case Failure(let error): return .Failure(error)
}
}
}
extension Result: Printable {
public var description: String {
switch self {
case .Success(let box):
return "Success: \(box.unbox)"
case .Failure(let error):
return "Failure: \(error.unbox)"
}
}
}
/// Failure coalescing
/// .Success(Box(42)) ?? 0 ==> 42
/// .Failure(NSError()) ?? 0 ==> 0
public func ??<T,E>(result: Result<T,E>, @autoclosure defaultValue: () -> T) -> T {
switch result {
case .Success(let value):
return value.unbox
case .Failure(let error):
return defaultValue()
}
}
/// Equatable
/// Equality for Result is defined by the equality of the contained types
public func ==<T, E where T: Equatable, E: Equatable>(lhs: Result<T, E>, rhs: Result<T, E>) -> Bool {
switch (lhs, rhs) {
case let (.Success(l), .Success(r)): return l.unbox == r.unbox
case let (.Failure(l), .Failure(r)): return l.unbox == r.unbox
default: return false
}
}
public func !=<T, E where T: Equatable, E: Equatable>(lhs: Result<T, E>, rhs: Result<T, E>) -> Bool {
return !(lhs == rhs)
}
/// Due to current swift limitations, we have to include this Box in Result.
/// Swift cannot handle an enum with multiple associated data (A, NSError) where one is of unknown size (A)
final public class Box<T> {
public let unbox: T
public init(_ value: T) { self.unbox = value }
}