diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-27 20:33:37 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-27 20:33:37 -0300 |
| commit | 487b4c5628c02130580c675925a1d64b09a5f6cb (patch) | |
| tree | 240338af0c2c246bb2c689464f63d70b1f27f240 /src/error.rs | |
| parent | 4a8c3b675df4b1a6b29904fc2af60605002a7f9e (diff) | |
| download | rust-487b4c5628c02130580c675925a1d64b09a5f6cb.tar.gz | |
A 404 from the API now brings a "faixa" naming the municipality the CEP
would have belonged to, and NotFound carries it. A CEP that fails is
rarely a random number: it is usually someone who knows the city and
guessed the digits, so naming the city turns a dead end into an answer
the caller can show.
It stays an error. The range it carries is the CEPs that actually exist
there, not the bounds the Correios publish — those start at the very CEP
that just failed — and it is sparse, which is what "ceps" measures. The
field is decoration on a failure, so parsing it is entirely defensive:
absent, null or malformed all yield None rather than costing a clean
not-found.
Breaking: NotFound gained a field, hence 0.2.0.
Diffstat (limited to 'src/error.rs')
| -rw-r--r-- | src/error.rs | 49 |
1 files changed, 48 insertions, 1 deletions
diff --git a/src/error.rs b/src/error.rs index 40a3049..b771ded 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,11 +15,17 @@ //! That split is why `NotFound` is not "the HTTP 404 variant": the client also //! returns `InvalidCep` without making any request at all, when the CEP is //! malformed before it ever leaves the machine. +//! +//! [`Error::NotFound`] may carry a [`Faixa`] — the municipality the CEP would +//! have belonged to. That is context on the failure and changes nothing about +//! it: the answer is still 404 and the CEP still does not exist. use std::error::Error as StdError; use std::fmt; use std::time::Duration; +use crate::model::Faixa; + /// The result of every fallible operation in this crate. pub type Result<T> = std::result::Result<T, Error>; @@ -45,6 +51,17 @@ pub enum Error { NotFound { /// The CEP that was looked up, unmasked. cep: String, + /// The municipality this CEP *would* belong to, when the API knows it. + /// + /// Still an error, and still a 404 — see [`Faixa`] before showing any + /// of it to a user. `None` when the CEP falls in a band no municipality + /// owns, and also whenever the field cannot be read: an unparseable + /// `faixa` never costs the not-found. + /// + /// Boxed to keep [`Error`] small: it travels in the `Err` of every + /// [`Result`] in this crate, including the ones that never carry a + /// `faixa`. [`Error::faixa`] hides the box at the call site. + faixa: Option<Box<Faixa>>, }, /// The API (or the edge in front of it) answered 429 on every attempt. @@ -102,6 +119,28 @@ impl Error { ) } + /// The municipality of a [`Error::NotFound`], when the API named one. + /// + /// `None` for every other variant, and for a not-found the API could not + /// place. It is context for the failure, not a result: the CEP asked for + /// still does not exist. + /// + /// ```no_run + /// # let cep_from_form = "25845000"; + /// if let Err(err) = cepis::lookup(cep_from_form) { + /// match err.faixa() { + /// Some(faixa) => println!("no such CEP in {}", faixa.municipality()), + /// None => println!("no such CEP"), + /// } + /// } + /// ``` + pub fn faixa(&self) -> Option<&Faixa> { + match self { + Error::NotFound { faixa, .. } => faixa.as_deref(), + _ => None, + } + } + pub(crate) fn invalid_cep(message: impl Into<String>) -> Self { Error::InvalidCep { message: message.into(), @@ -129,9 +168,17 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::InvalidCep { message } => f.write_str(message), - Error::NotFound { cep } => { + Error::NotFound { cep, faixa: None } => { write!(f, "CEP {cep} does not exist in the DNE database") } + Error::NotFound { + cep, + faixa: Some(faixa), + } => write!( + f, + "CEP {cep} does not exist in the DNE database (it would be in {})", + faixa.municipality() + ), Error::RateLimited { message, .. } => f.write_str(message), Error::Server { message, .. } => f.write_str(message), Error::Network { message, .. } => f.write_str(message), |