diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-27 20:35:12 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-27 20:35:12 -0300 |
| commit | 2ed1190f0f021bc4de17b47d8d4ed7241611d6f1 (patch) | |
| tree | 221a8ba542c35ec66b4c79805067749e78c5e03f /src/client.lisp | |
| parent | ae9ad8a4c61b793f6859c84dac80b1c1b04b7b68 (diff) | |
| download | lisp-0.2.0.tar.gz | |
A CEP that fails is rarely random: it is someone who knows the city and
guessed the number, most often the -000 that 957 municipalities never
received. The API now answers that 404 with the municipality owning the
band the CEP falls in, and without it every caller can say no more than
"does not exist" to a person who is one digit away from the right CEP.
It lands on the condition rather than in a return value because it is
context for the failure and nothing else: the answer is still a 404, and
a faixa must never read as an address that resolved. Parsing it is
deliberately defensive for the same reason — a missing, malformed or
empty block yields NIL, so a field that only adds context cannot turn a
clean not-found into a parse failure, and an older API stays supported.
The ends of the band are the CEPs that actually exist, not the bounds
the Correios publish, and the band is sparse; the docstrings and the
README say so, because presenting the pair as a range of valid CEPs is
the mistake this data invites.
models is compiled before errors so the report function can name the
faixa's municipality.
Diffstat (limited to 'src/client.lisp')
| -rw-r--r-- | src/client.lisp | 21 |
1 files changed, 20 insertions, 1 deletions
diff --git a/src/client.lisp b/src/client.lisp index 2965c86..9dd5338 100644 --- a/src/client.lisp +++ b/src/client.lisp @@ -1,6 +1,6 @@ (in-package #:cepis) -(defparameter +version+ "0.1.0" +(defparameter +version+ "0.2.0" "Version of this library, as it appears in the default User-Agent.") (defparameter +default-url+ "https://cep.is" @@ -306,6 +306,19 @@ between you and the API is answering instead of it: ~s" fallback)) (error () fallback))) +(defun error-faixa (body) + "The \"faixa\" block of an error body as a `faixa', or NIL when there is none. + +Every failure to read it is NIL: a body that is not JSON, a `faixa' that is not +an object, an API too old to send the field. A not-found is a definitive answer +about the request, and a field that only says which municipality the CEP would +have belonged to must never be able to turn it into an `invalid-response'." + (handler-case + (let ((data (com.inuoe.jzon:parse (body-text body)))) + (when (hash-table-p data) + (faixa-from-json (gethash "faixa" data)))) + (error () nil))) + (defun lookup (cep &key (client *client*)) "Look up a single CEP and return an `address'. @@ -315,6 +328,11 @@ A 400 from the API is surfaced as `invalid-cep' carrying the API's own message: the format already passed local validation, so the server knows something this client does not. +A `cep-not-found' may carry a `faixa' in `cep-not-found-faixa': the +municipality that owns the band the CEP falls in, when the API knows one. It is +context for the error and nothing more — the answer is still a 404, and the +band is sparse, so it says which city to ask about, never which CEPs are valid. + Signals `invalid-cep' when the CEP is not 8 digits, without making a request; `cep-not-found' when it is well-formed but not in the database; `rate-limited' on 429 and `server-error' on 5xx after every attempt; `network-error' when the @@ -325,6 +343,7 @@ request never completed; `invalid-response' when the body cannot be read." (case status (404 (error 'cep-not-found :name number + :faixa (error-faixa body) :message (format nil "CEP ~a does not exist in the DNE database" number))) (400 (error 'invalid-cep |