diff options
Diffstat (limited to 'src/models.lisp')
| -rw-r--r-- | src/models.lisp | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/src/models.lisp b/src/models.lisp index 8905ae1..9ca0eaf 100644 --- a/src/models.lisp +++ b/src/models.lisp @@ -30,6 +30,36 @@ Shorthand for (string= (geo-precisao geo) \"logradouro\"), which is the check to make before using the point to measure distance." (string= (geo-precisao geo) "logradouro")) +(defstruct (faixa (:copier nil)) + "The municipality a CEP that does not exist would have belonged to. + +Context for an error, never an address. It arrives on the 404 of a CEP the DNE +does not have, and names the municipality that owns the band the CEP falls in. +A CEP that fails is rarely random: it is usually someone who knows the city and +guessed the number — the classic guess being the `-000' that 957 municipalities +never received. + +`faixa-cep-ini' and `faixa-cep-fim' are the smallest and the largest CEP that +*actually exist* in that municipality, not the bounds the Correios publish. For +Areal/RJ the DNE's own start is 25845000 — exactly the CEP that just failed — +while `faixa-cep-ini' is 25845001. + +They are not the ends of a list, either: the range is sparse. Areal's 118 CEPs +are scattered over the ~4,900 numbers between its two ends, which is what +`faixa-ceps' counts. Offer the municipality to whoever typed the CEP; never +present the pair as a range of valid CEPs. + +Both ends are strings, like every CEP in this library, because a leading zero +is part of the number." + (localidade "" :type string :read-only t) + (uf "" :type string :read-only t) + (estado "" :type string :read-only t) + (regiao "" :type string :read-only t) + (ibge "" :type string :read-only t) + (cep-ini "" :type string :read-only t) + (cep-fim "" :type string :read-only t) + (ceps 0 :type integer :read-only t)) + (defstruct (address (:copier nil)) "A CEP and everything the database knows about it. @@ -117,6 +147,30 @@ useful." :raio-m (round raio-m) :precisao precisao))))) +(defun faixa-from-json (value) + "Build a `faixa' from the parsed \"faixa\" block, or NIL when it is absent or +malformed. + +The API omits the field entirely — it never sends null — when the CEP falls in +a band no municipality owns, so an absent block is the ordinary case and not an +anomaly. + +Anything that is not a block naming a municipality also yields NIL. That is the +whole point of parsing it here and not at the call site: the response is a +definitive not-found, and a field that only adds context must never be able to +turn it into a parse failure." + (when (hash-table-p value) + (let ((localidade (json-string value "localidade"))) + (when (plusp (length localidade)) + (make-faixa :localidade localidade + :uf (json-string value "uf") + :estado (json-string value "estado") + :regiao (json-string value "regiao") + :ibge (json-string value "ibge") + :cep-ini (json-string value "cep_ini") + :cep-fim (json-string value "cep_fim") + :ceps (json-integer value "ceps")))))) + (defun address-from-json (data) "Build an `address' from the hash table the JSON parser produced. |