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
|
//
// RESTError.swift
// RESTError
//
// Created by pronebird on 27/07/2021.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import Foundation
import MullvadTypes
extension REST {
/// An error type returned by REST API classes.
public enum Error: LocalizedError, WrappingError, Sendable {
/// Failure to create URL request.
case createURLRequest(Swift.Error)
/// Network failure.
case network(URLError)
/// Failure to handle response.
case unhandledResponse(_ statusCode: Int, _ serverResponse: ServerErrorResponse?)
/// Failure to decode server response.
case decodeResponse(Swift.Error)
/// Failure to transit URL request via selected transport implementation.
case transport(Swift.Error)
public var errorDescription: String? {
switch self {
case .createURLRequest:
return "Failure to create URL request."
case let .network(error):
return "Network error due to \(error.localizedDescription)."
case let .unhandledResponse(statusCode, serverResponse):
var str = "Failure to handle server response: HTTP/\(statusCode)."
if let code = serverResponse?.code {
str += " Error code: \(code.rawValue)."
}
if let detail = serverResponse?.detail {
str += " Detail: \(detail)"
}
return str
case .decodeResponse:
return "Failure to decode response."
case .transport:
return "Transport error."
}
}
public var underlyingError: Swift.Error? {
switch self {
case let .network(error):
return error
case let .createURLRequest(error):
return error
case let .decodeResponse(error):
return error
case let .transport(error):
return error
case .unhandledResponse:
return nil
}
}
public func compareErrorCode(_ code: ServerResponseCode) -> Bool {
if case let .unhandledResponse(_, serverResponse) = self {
return serverResponse?.code == code
} else {
return false
}
}
}
public struct ServerErrorResponse: Decodable, Sendable {
public let code: ServerResponseCode
public let detail: String?
private enum CodingKeys: String, CodingKey {
case code, detail, error
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let rawValue = try container.decode(String.self, forKey: .code)
code = ServerResponseCode(rawValue: rawValue)
detail =
try container.decodeIfPresent(String.self, forKey: .detail)
?? container.decodeIfPresent(String.self, forKey: .error)
}
public init(code: REST.ServerResponseCode, detail: String? = nil) {
self.code = code
self.detail = detail
}
}
public struct ServerResponseCode: RawRepresentable, Equatable, Sendable {
public static let invalidAccount = ServerResponseCode(rawValue: "INVALID_ACCOUNT")
public static let keyLimitReached = ServerResponseCode(rawValue: "KEY_LIMIT_REACHED")
public static let publicKeyNotFound = ServerResponseCode(rawValue: "PUBKEY_NOT_FOUND")
public static let publicKeyInUse = ServerResponseCode(rawValue: "PUBKEY_IN_USE")
public static let maxDevicesReached = ServerResponseCode(rawValue: "MAX_DEVICES_REACHED")
public static let invalidAccessToken = ServerResponseCode(rawValue: "INVALID_ACCESS_TOKEN")
public static let deviceNotFound = ServerResponseCode(rawValue: "DEVICE_NOT_FOUND")
public static let serviceUnavailable = ServerResponseCode(rawValue: "SERVICE_UNAVAILABLE")
public static let tooManyRequests = ServerResponseCode(rawValue: "TOO_MANY_REQUESTS")
public static let invalidVoucher = ServerResponseCode(rawValue: "INVALID_VOUCHER")
public static let usedVoucher = ServerResponseCode(rawValue: "VOUCHER_USED")
public static let parsingError = ServerResponseCode(rawValue: "PARSING_ERROR")
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
}
public enum InternalTransportError: LocalizedError {
/// Programmer error. Indicates that REST framework is not configured with any transport.
case noTransport
/// Indicates that the transport returned something else but `HTTPURLResponse` upon success.
/// Likely a programmer error.
case invalidResponse(URLResponse?)
public var errorDescription: String? {
switch self {
case .noTransport:
return "Transport is not configured."
case let .invalidResponse(response):
return
"Received invalid response. Expected HTTPURLResponse, got \(response.map { "\($0.self)" } ?? "(nil)")."
}
}
}
}
|