From 58dbe934a90c1eda8e76cd218a413f01a4d9b11d Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:33:25 -0300 Subject: Carry the municipality of a missing CEP on not_found MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A CEP that fails is rarely a random number: it is someone who knows the city and guessed the digits, and the classic guess is the -000 that 957 municipalities never received. The API now answers that 404 with an optional faixa block naming where the CEP would have been, so a caller can turn "CEP does not exist" into something the user can act on. It hangs off the error, never off an address: the lookup still failed. Parsing gives up quietly at every step — no block, a block that is not an object, one without a municipality to name — because a broken piece of context must not turn a clean not-found into a parse failure. cep_ini and cep_fim are the CEPs that exist, not the range the Correios publish, and the interval between them is sparse; ceps is what keeps the pair from being read as a list of valid CEPs. --- cepis/client.lua | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'cepis/client.lua') diff --git a/cepis/client.lua b/cepis/client.lua index 2a8ae2c..d8a42c9 100644 --- a/cepis/client.lua +++ b/cepis/client.lua @@ -24,7 +24,7 @@ local csv = require("cepis.csv") local M = {} --- Library version, as it appears in the default `User-Agent`. -M.VERSION = "0.1.0" +M.VERSION = "0.2.0" --- Where the API lives unless `base_url` says otherwise. M.DEFAULT_URL = "https://cep.is" @@ -191,6 +191,22 @@ local function error_message(body, fallback) return fallback end +--- The `faixa` of a 404 body, when it carries one. +-- +-- Every step is allowed to give up and return `nil`: the body may not be JSON +-- at all (an edge answering instead of the API), may not carry the block (the +-- API omits it, never nulls it, for a CEP in a band no municipality owns), or +-- may carry something unusable. None of that may turn a clean not-found into a +-- parse failure — the answer to the caller is the same 404 either way, only +-- with less context on it. +local function faixa_from(body) + local ok, data = pcall(json.decode, body or "", 1, nil) + if not ok or type(data) ~= "table" then + return nil + end + return models.new_faixa(data.faixa) +end + local function transport(url) if url:lower():find("^https://") then local ok, https = pcall(require, "ssl.https") @@ -430,6 +446,15 @@ end -- the format already passed local validation, so the server knows something -- this client does not. -- +-- A `not_found` may carry `err.faixa`, a `Faixa` naming the municipality the +-- CEP would have belonged to. It is context for the failure and nothing more — +-- the lookup still failed, and it is `nil` whenever the API omits the block: +-- +-- local address, err = client:lookup("25845000") +-- if not address and err.kind == cepis.NOT_FOUND and err.faixa then +-- print("did you mean a CEP in " .. err.faixa:label() .. "?") +-- end +-- -- @tparam string cep masked or unmasked -- @treturn[1] Address -- @treturn[2] nil @@ -448,12 +473,17 @@ function Client:lookup(cep) end if status == 404 then + local faixa = faixa_from(body) + local message = "CEP " .. number .. " does not exist in the DNE database" + if faixa then + message = message .. ", which would place it in " .. faixa:label() + end return nil, - errors.new( - errors.NOT_FOUND, - "CEP " .. number .. " does not exist in the DNE database", - { cep = number, status = 404 } - ) + errors.new(errors.NOT_FOUND, message, { + cep = number, + status = 404, + faixa = faixa, + }) end if status == 400 then return nil, -- cgit v1.3