From 516254878b0b8f92a8ef4543bf5d0601aac3466d Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:22:17 -0300 Subject: Carry the municipality of a nonexistent CEP on the 404 A CEP that fails is almost never a random number: it is someone who knows the city and guessed the digits, most often the -000 that 957 municipalities never received. The API now names the municipality that number would belong to, so the client surfaces it as CepNotFoundException.faixa instead of throwing away the one piece of information that makes the failure useful. It stays an exception. The block is context for a 404, not an address, and cep_ini/cep_fim are the smallest and largest CEP that actually exist there rather than the range the Correios publish -- the published start for Areal is the very CEP that just failed. The range is sparse (118 CEPs across some 9,000 numbers), which is what ceps is for. Parsing is deliberately total: absent, null, empty or malformed all yield null. Losing a clean not-found to a parse error over an optional field would be a bad trade. --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) (limited to 'README.md') diff --git a/README.md b/README.md index 4278f8a..b8da7ca 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ repositories { } dependencies { - implementation("org.cepis:cepis:0.1.0") + implementation("org.cepis:cepis:0.2.0") } ``` @@ -145,7 +145,7 @@ val client = backoffBase = Duration.ofMillis(500), // base of the exponential wait backoffMax = Duration.ofSeconds(8), // cap on each wait totalTimeout = null, // cap on the whole call, attempts included - userAgent = null, // defaults to cepis-kotlin/0.1.0 + userAgent = null, // defaults to cepis-kotlin/0.2.0 ) val address = client.lookup("01001000") @@ -176,7 +176,7 @@ is by **the decision you have to make**, not by the HTTP status that caused it: | error | when | does retrying help? | | --- | --- | --- | | `InvalidCepException` | not 8 digits | no | -| `CepNotFoundException` | well-formed, not in the database | no | +| `CepNotFoundException` | well-formed, not in the database; may carry `faixa` | no | | `RateLimitedException` | 429; carries `retryAfter` | yes, later | | `ServerErrorException` | 5xx; carries `status` | yes, later | | `NetworkException` | DNS, connection, timeout, TLS | yes, later | @@ -192,6 +192,49 @@ try { } ``` +### When the CEP does not exist + +A CEP that fails is rarely a random number — it is usually someone who knows +the city and guessed the digits. The classic guess is the `-000`, which 957 +municipalities never received. So `CepNotFoundException` carries a `faixa` +naming the municipality the number would have belonged to: + +```kotlin +try { + lookup("25845-000") +} catch (e: CepNotFoundException) { + val f = e.faixa + if (f != null) { + println("${f.localidade}/${f.uf}") // Areal/RJ + println(f.ibge) // "3300225" + println("${f.cepIni}..${f.cepFim}") // 25845001..25849899 + println(f.ceps) // 118 + } +} +``` + +Three things to keep straight: + +- **It is still a 404.** `faixa` is context for the failure, not an address. + The CEP does not exist; do not let the block turn a not-found into a + success. +- **`cepIni` and `cepFim` are the smallest and the largest CEP that actually + exist** in that municipality, not the bounds the Correios publish. The DNE's + own start for Areal is `25845000` — precisely the CEP that just failed, so + echoing it back would help nobody. +- **The range is sparse.** Areal's 118 CEPs are spread over some 4,900 + numbers; that is what `ceps` is for. Never present the pair as a list of + valid CEPs — "try something between 25845001 and 25849899" is wrong advice + 99% of the time. + +`faixa` is **omitted, never null**, when the number falls in a band no +municipality owns, so `null` is a normal outcome and not an error. A missing, +malformed or empty block simply yields `null`: it can never cost you the +`CepNotFoundException` itself. + +`lookupMany` is unaffected — the CSV endpoint behind it has no such column, +and a CEP that does not exist is still `null` at its position. + A nonsensical `Client` configuration throws a plain `IllegalArgumentException` instead: that is a programming error, not a lookup failure. -- cgit v1.3