|
12 | 12 | //
|
13 | 13 | //===----------------------------------------------------------------------===//
|
14 | 14 |
|
| 15 | +import protocol Foundation.LocalizedError |
15 | 16 | import struct Logging.Logger
|
16 | 17 | import NIO
|
17 | 18 |
|
@@ -60,3 +61,61 @@ extension RedisClient {
|
60 | 61 | return self
|
61 | 62 | }
|
62 | 63 | }
|
| 64 | + |
| 65 | +/// When working with `RedisClient`, runtime errors can be thrown to indicate problems with connection state, decoding assertions, or otherwise. |
| 66 | +public struct RedisClientError: LocalizedError, Equatable, Hashable { |
| 67 | + /// The connection is closed, but was used to try and send a command to Redis. |
| 68 | + public static let connectionClosed = RedisClientError(.connectionClosed) |
| 69 | + |
| 70 | + /// Conversion from `RESPValue` to the specified type failed. |
| 71 | + /// |
| 72 | + /// If this is ever triggered, please capture the original `RESPValue` string sent from Redis for bug reports. |
| 73 | + public static func failedRESPConversion(to type: Any.Type) -> RedisClientError { |
| 74 | + return .init(.failedRESPConversion(to: type)) |
| 75 | + } |
| 76 | + |
| 77 | + /// Expectations of message structures were not met. |
| 78 | + /// |
| 79 | + /// If this is ever triggered, please capture the original `RESPValue` string sent from Redis along with the command and arguments sent to Redis for bug reports. |
| 80 | + public static func assertionFailure(message: String) -> RedisClientError { |
| 81 | + return .init(.assertionFailure(message: message)) |
| 82 | + } |
| 83 | + |
| 84 | + public var errorDescription: String? { |
| 85 | + let message: String |
| 86 | + switch self.baseError { |
| 87 | + case .connectionClosed: message = "Connection was closed while trying to send command." |
| 88 | + case let .failedRESPConversion(type): message = "Failed to convert RESP to \(type)" |
| 89 | + case let .assertionFailure(text): message = text |
| 90 | + } |
| 91 | + return "(RediStack) \(message)" |
| 92 | + } |
| 93 | + |
| 94 | + public var recoverySuggestion: String? { |
| 95 | + switch self.baseError { |
| 96 | + case .connectionClosed: return "Check that the connection is not closed before invoking commands. With RedisConnection, this can be done with the 'isConnected' property." |
| 97 | + case .failedRESPConversion: return "Ensure that the data type being requested is actually what's being returned. If you see this error and are not sure why, capture the original RESPValue string sent from Redis to add to your bug report." |
| 98 | + case .assertionFailure: return "This error should in theory never happen. If you trigger this error, capture the original RESPValue string sent from Redis along with the command and arguments that you sent to Redis to add to your bug report." |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + private var baseError: BaseError |
| 103 | + |
| 104 | + private init(_ baseError: BaseError) { self.baseError = baseError } |
| 105 | + |
| 106 | + /* Protocol Conformances and Private Type implementation */ |
| 107 | + |
| 108 | + public static func ==(lhs: RedisClientError, rhs: RedisClientError) -> Bool { |
| 109 | + return lhs.localizedDescription == rhs.localizedDescription |
| 110 | + } |
| 111 | + |
| 112 | + public func hash(into hasher: inout Hasher) { |
| 113 | + hasher.combine(self.localizedDescription) |
| 114 | + } |
| 115 | + |
| 116 | + fileprivate enum BaseError { |
| 117 | + case connectionClosed |
| 118 | + case failedRESPConversion(to: Any.Type) |
| 119 | + case assertionFailure(message: String) |
| 120 | + } |
| 121 | +} |
0 commit comments