-
Notifications
You must be signed in to change notification settings - Fork 169
/
Copy pathErrors.swift
189 lines (151 loc) · 5.07 KB
/
Errors.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
struct RPCError: Error {
let httpResponseCode: Int
let message: String
let status: RPCStatus
let details: [ErrorDetails]
private var errorInfo: ErrorDetails? {
return details.first { $0.isErrorInfo() }
}
init(httpResponseCode: Int, message: String, status: RPCStatus, details: [ErrorDetails]) {
self.httpResponseCode = httpResponseCode
self.message = message
self.status = status
self.details = details
}
func isInvalidAPIKeyError() -> Bool {
return errorInfo?.reason == "API_KEY_INVALID"
}
func isUnsupportedUserLocationError() -> Bool {
return message == RPCErrorMessage.unsupportedUserLocation.rawValue
}
}
extension RPCError: Decodable {
enum CodingKeys: CodingKey {
case error
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let status = try container.decode(ErrorStatus.self, forKey: .error)
if let code = status.code {
httpResponseCode = code
} else {
httpResponseCode = -1
}
if let message = status.message {
self.message = message
} else {
message = "Unknown error."
}
if let rpcStatus = status.status {
self.status = rpcStatus
} else {
self.status = .unknown
}
details = status.details
}
}
struct ErrorStatus {
let code: Int?
let message: String?
let status: RPCStatus?
let details: [ErrorDetails]
}
struct ErrorDetails {
static let errorInfoType = "type.googleapis.com/google.rpc.ErrorInfo"
let type: String
let reason: String?
let domain: String?
func isErrorInfo() -> Bool {
return type == ErrorDetails.errorInfoType
}
}
extension ErrorDetails: Decodable, Equatable {
enum CodingKeys: String, CodingKey {
case type = "@type"
case reason
case domain
}
}
extension ErrorStatus: Decodable {
enum CodingKeys: CodingKey {
case code
case message
case status
case details
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
code = try container.decodeIfPresent(Int.self, forKey: .code)
message = try container.decodeIfPresent(String.self, forKey: .message)
do {
status = try container.decodeIfPresent(RPCStatus.self, forKey: .status)
} catch {
status = .unknown
}
if container.contains(.details) {
details = try container.decode([ErrorDetails].self, forKey: .details)
} else {
details = []
}
}
}
enum RPCStatus: String, Decodable {
// Not an error; returned on success.
case ok = "OK"
// The operation was cancelled, typically by the caller.
case cancelled = "CANCELLED"
// Unknown error.
case unknown = "UNKNOWN"
// The client specified an invalid argument.
case invalidArgument = "INVALID_ARGUMENT"
// The deadline expired before the operation could complete.
case deadlineExceeded = "DEADLINE_EXCEEDED"
// Some requested entity (e.g., file or directory) was not found.
case notFound = "NOT_FOUND"
// The entity that a client attempted to create (e.g., file or directory) already exists.
case alreadyExists = "ALREADY_EXISTS"
// The caller does not have permission to execute the specified operation.
case permissionDenied = "PERMISSION_DENIED"
// The request does not have valid authentication credentials for the operation.
case unauthenticated = "UNAUTHENTICATED"
// Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system
// is out of space.
case resourceExhausted = "RESOURCE_EXHAUSTED"
// The operation was rejected because the system is not in a state required for the operation's
// execution.
case failedPrecondition = "FAILED_PRECONDITION"
// The operation was aborted, typically due to a concurrency issue such as a sequencer check
// failure or transaction abort.
case aborted = "ABORTED"
// The operation was attempted past the valid range.
case outOfRange = "OUT_OF_RANGE"
// The operation is not implemented or is not supported/enabled in this service.
case unimplemented = "UNIMPLEMENTED"
// Internal errors.
case internalError = "INTERNAL"
// The service is currently unavailable.
case unavailable = "UNAVAILABLE"
// Unrecoverable data loss or corruption.
case dataLoss = "DATA_LOSS"
}
enum RPCErrorMessage: String {
case unsupportedUserLocation = "User location is not supported for the API use."
}
enum InvalidCandidateError: Error {
case emptyContent(underlyingError: Error)
case malformedContent(underlyingError: Error)
}