diff options
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), |