From 487b4c5628c02130580c675925a1d64b09a5f6cb Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:33:37 -0300 Subject: Carry the municipality of a CEP that does not exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'README.md') diff --git a/README.md b/README.md index 8481710..b6a5f69 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ make**, not by the HTTP status that caused it: | variant | when | does retrying help? | | --- | --- | --- | | `InvalidCep` | not 8 digits | no | -| `NotFound` | well-formed, not in the database | no | +| `NotFound` | well-formed, not in the database; may carry a `faixa` | no | | `RateLimited` | 429; carries `retry_after` | yes, later | | `Server` | 5xx; carries `status` | yes, later | | `Network` | DNS, connection, timeout, TLS | yes, later | @@ -183,6 +183,53 @@ match cepis::lookup(&cep_from_form) { answering instead of it — a captive Wi-Fi portal, a corporate proxy, an HTML error page. Useful as a diagnosis: the problem is not in your code. +### A CEP that does not exist, and where it would be + +A failing CEP is rarely a random number: it is usually someone who knows the +city and guessed the digits, and the classic guess is the `-000` that 957 +municipalities never received. So `NotFound` carries the municipality that CEP +*would* belong to, whenever the API can place it: + +```rust +if let Err(err) = cepis::lookup("25845000") { + match err.faixa() { + Some(f) => println!("no such CEP in {}", f.municipality()), // "Areal/RJ" + None => println!("no such CEP"), + } +} +``` + +```rust +if let Some(f) = err.faixa() { + f.localidade; // "Areal" + f.uf; // "RJ" + f.estado; // "Rio de Janeiro" + f.regiao; // "Sudeste" + f.ibge; // "3300225" + f.cep_ini; // "25845001" + f.cep_fim; // "25849899" + f.ceps; // 118 +} +``` + +Three things not to get wrong: + +- **It is still an error.** The response is still a 404 and the CEP still does + not exist. This is context for the failure, never an address. +- **`cep_ini`/`cep_fim` are not the Correios range.** They are the smallest and + largest CEP that *actually exist* in the municipality. The DNE's own start + for Areal is `25845000` — exactly the CEP that just failed. Show the official + bounds and you suggest the CEP is valid. +- **The range is sparse.** Areal's 118 CEPs are scattered over the ~4,900 + numbers between the two ends; almost none of that interval exists either. + That is what `ceps` is for — the pair is not a list, or a count, of valid + CEPs. + +`faixa` is **omitted, never null**, when the CEP falls in a band no +municipality owns, so `err.faixa()` is `None` there. It is also `None` if the +field cannot be read: an unparseable extra never turns a clean not-found into a +parse error. + ## Development Everything runs through `make`, which shells out to `cargo`: -- cgit v1.3