diff options
Diffstat (limited to 'src/client.rs')
| -rw-r--r-- | src/client.rs | 27 |
1 files changed, 24 insertions, 3 deletions
diff --git a/src/client.rs b/src/client.rs index 8d73f06..4e6afcd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -12,7 +12,7 @@ use serde_json::{Map, Value}; use crate::csv; use crate::error::{Error, Result}; -use crate::model::{Address, Health}; +use crate::model::{Address, Faixa, Health}; use crate::retry::{jitter, parse_retry_after}; /// Where the public API lives. @@ -270,7 +270,9 @@ impl Client { /// # Errors /// /// - [`Error::InvalidCep`] — not 8 digits (no request is made). - /// - [`Error::NotFound`] — well-formed, but not in the database. + /// - [`Error::NotFound`] — well-formed, but not in the database. It carries + /// a [`Faixa`] naming the municipality the CEP would belong to, whenever + /// the API can place it in one. /// - [`Error::RateLimited`] — 429 after every attempt. /// - [`Error::Server`] — 5xx after every attempt. /// - [`Error::Network`] — the request never completed. @@ -280,7 +282,10 @@ impl Client { let (status, body) = self.request(&format!("/{number}"), None, &[200])?; if status == 404 { - return Err(Error::NotFound { cep: number }); + return Err(Error::NotFound { + cep: number, + faixa: faixa(&body), + }); } if status == 400 { return Err(Error::invalid_cep(error_message( @@ -548,6 +553,22 @@ fn error_message(body: &[u8], fallback: &str) -> String { .unwrap_or_else(|| fallback.to_owned()) } +/// Pull the `faixa` block out of a 404 body, if there is a readable one. +/// +/// Every step is allowed to fail into `None`: a body that is not JSON, a +/// missing or null `faixa`, an object that names no municipality. The caller is +/// on its way to returning [`Error::NotFound`] either way, and an unparseable +/// extra field must not turn a correct not-found into an +/// [`Error::InvalidResponse`]. +fn faixa(body: &[u8]) -> Option<Box<Faixa>> { + serde_json::from_slice::<Value>(body) + .ok() + .as_ref() + .and_then(|v| v.get("faixa")) + .and_then(Faixa::from_json) + .map(Box::new) +} + fn preview(body: &[u8]) -> String { let text = String::from_utf8_lossy(body); let cut = text |