summaryrefslogtreecommitdiffhomepage
path: root/ios/Operations/OperationCompletion.swift
blob: 49a34e0884a536cafd225eb61d2ddf8441b53aff (plain)
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
//
//  OperationCompletion.swift
//  Operations
//
//  Created by pronebird on 24/01/2022.
//  Copyright © 2022 Mullvad VPN AB. All rights reserved.
//

import Foundation

public enum OperationCompletion<Success, Failure: Error> {
    case cancelled
    case success(Success)
    case failure(Failure)

    public var isSuccess: Bool {
        if case .success = self {
            return true
        } else {
            return false
        }
    }

    public var value: Success? {
        if case let .success(value) = self {
            return value
        } else {
            return nil
        }
    }

    public var error: Failure? {
        if case let .failure(error) = self {
            return error
        } else {
            return nil
        }
    }

    public var result: Result<Success, Failure>? {
        switch self {
        case let .success(value):
            return .success(value)
        case let .failure(error):
            return .failure(error)
        case .cancelled:
            return nil
        }
    }

    public init(result: Result<Success, Failure>) {
        switch result {
        case let .success(value):
            self = .success(value)
        case let .failure(error):
            self = .failure(error)
        }
    }

    public init(error: Failure?) where Success == Void {
        if let error = error {
            self = .failure(error)
        } else {
            self = .success(())
        }
    }

    public func get() throws -> Success {
        if let result = result {
            return try result.get()
        } else {
            throw OperationCancellationError()
        }
    }

    public func map<NewSuccess>(_ block: (Success) -> NewSuccess)
        -> OperationCompletion<NewSuccess, Failure>
    {
        switch self {
        case let .success(value):
            return .success(block(value))
        case let .failure(error):
            return .failure(error)
        case .cancelled:
            return .cancelled
        }
    }

    public func mapError<NewFailure: Error>(_ block: (Failure) -> NewFailure)
        -> OperationCompletion<Success, NewFailure>
    {
        switch self {
        case let .success(value):
            return .success(value)
        case let .failure(error):
            return .failure(block(error))
        case .cancelled:
            return .cancelled
        }
    }

    public func flatMap<NewSuccess>(_ block: (Success) -> OperationCompletion<NewSuccess, Failure>)
        -> OperationCompletion<NewSuccess, Failure>
    {
        switch self {
        case let .success(value):
            return block(value)
        case let .failure(error):
            return .failure(error)
        case .cancelled:
            return .cancelled
        }
    }

    public func flatMapError<NewFailure: Error>(
        _ block: (Failure)
            -> OperationCompletion<Success, NewFailure>
    ) -> OperationCompletion<Success, NewFailure> {
        switch self {
        case let .success(value):
            return .success(value)
        case let .failure(error):
            return block(error)
        case .cancelled:
            return .cancelled
        }
    }

    public func tryMap<NewSuccess>(_ block: (Success) throws -> NewSuccess)
        -> OperationCompletion<NewSuccess, Error>
    {
        switch self {
        case let .success(value):
            do {
                return .success(try block(value))
            } catch {
                return .failure(error)
            }
        case let .failure(error):
            return .failure(error)
        case .cancelled:
            return .cancelled
        }
    }

    public func ignoreOutput() -> OperationCompletion<Void, Failure> {
        return map { _ in () }
    }

    public func eraseFailureType() -> OperationCompletion<Success, Error> {
        return mapError { $0 }
    }
}

public struct OperationCancellationError: LocalizedError {
    public var errorDescription: String? {
        return "Operation was cancelled."
    }
}