summaryrefslogtreecommitdiffstats
path: root/Sources/Cepis/Errors.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/Cepis/Errors.swift')
-rw-r--r--Sources/Cepis/Errors.swift44
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 }
}