diff options
Diffstat (limited to 'Sources/Cepis/Errors.swift')
| -rw-r--r-- | Sources/Cepis/Errors.swift | 44 |
1 files changed, 36 insertions, 8 deletions
diff --git a/Sources/Cepis/Errors.swift b/Sources/Cepis/Errors.swift index 1472f81..089121d 100644 --- a/Sources/Cepis/Errors.swift +++ b/Sources/Cepis/Errors.swift @@ -6,15 +6,15 @@ import Foundation /// wrong. The cases split by **the decision the caller has to make**, not by /// the HTTP status that caused them: /// -/// - ``invalidCEP(_:)`` and ``cepNotFound(_:)`` are problems with the -/// *request*. Retrying is pointless — the same CEP will fail again. +/// - ``invalidCEP(_:)`` and ``cepNotFound(message:faixa:)`` are problems with +/// the *request*. Retrying is pointless — the same CEP will fail again. /// - ``networkError(message:)``, ``serverError(message:status:)`` and /// ``rateLimited(message:retryAfter:)`` are problems with *transport*. /// Retrying may help, and the client already retried on its own before /// throwing any of them. /// -/// That split is why ``cepNotFound(_:)`` is not "the HTTP 404 case": nothing -/// about it is tied to a status line. +/// That split is why ``cepNotFound(message:faixa:)`` is not "the HTTP 404 +/// case": nothing about it is tied to a status line. public enum CepisError: Error, Sendable { /// The CEP is not 8 digits. /// @@ -25,7 +25,13 @@ public enum CepisError: Error, Sendable { case invalidCEP(String) /// The CEP is well-formed but does not exist in the DNE database. - case cepNotFound(String) + /// + /// `faixa` names the municipality the CEP *would* have belonged to, when + /// the API can say — see ``Faixa``. It is `nil` whenever the API omits it, + /// which happens for a CEP in a band no municipality owns, and whenever + /// what came back could not be understood. It never changes the meaning of + /// the case: the CEP does not exist either way. + case cepNotFound(message: String, faixa: Faixa?) /// The API (or the edge in front of it) answered 429 on every attempt. /// @@ -53,7 +59,7 @@ public enum CepisError: Error, Sendable { public var message: String { switch self { case .invalidCEP(let message): return message - case .cepNotFound(let message): return message + case .cepNotFound(let message, _): return message case .rateLimited(let message, _): return message case .serverError(let message, _): return message case .networkError(let message): return message @@ -61,6 +67,15 @@ public enum CepisError: Error, Sendable { } } + /// The municipality the missing CEP would have belonged to, when known. + /// + /// `nil` for every case other than ``cepNotFound(message:faixa:)``, so a + /// caller can reach for it without pattern-matching the enum first. + public var faixa: Faixa? { + guard case .cepNotFound(_, let faixa) = self else { return nil } + return faixa + } + /// Whether making the same call again later stands a chance of succeeding. /// /// `true` for rate limiting, server errors and network failures; `false` @@ -74,9 +89,22 @@ public enum CepisError: Error, Sendable { } extension CepisError: CustomStringConvertible { - public var description: String { message } + /// ``message``, plus the municipality when a ``Faixa`` came with the + /// not-found. + /// + /// The addition is the whole point of the faixa: *"CEP 25845000 does not + /// exist in the DNE database (that range is Areal/RJ)"* tells the person at + /// the keyboard they typed the right city and the wrong number, which + /// *"does not exist"* on its own does not. + /// + /// ``message`` stays exactly what it was, for callers that log or match on + /// it. + public var description: String { + guard let faixa else { return message } + return "\(message) (that range is \(faixa.localidadeUF))" + } } extension CepisError: LocalizedError { - public var errorDescription: String? { message } + public var errorDescription: String? { description } } |