summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/cepis.h105
1 files changed, 101 insertions, 4 deletions
diff --git a/include/cepis.h b/include/cepis.h
index 6b2dc9a..d639d62 100644
--- a/include/cepis.h
+++ b/include/cepis.h
@@ -39,6 +39,10 @@
* borrowed for the duration of the call only; the library copies whatever it
* needs to keep.
*
+ * A ::cepis_error_t is the exception in the other direction: it owns nothing,
+ * lives happily on the stack and is never freed. ::cepis_error_faixa therefore
+ * returns a pointer *into* it, borrowed for as long as the error itself lives.
+ *
* @section fieldnames Field names
*
* The API surface is English, but the **data fields keep the API's own
@@ -63,7 +67,7 @@ extern "C" {
#endif
/** Library version. */
-#define CEPIS_VERSION "0.1.0"
+#define CEPIS_VERSION "0.2.0"
/** Default API endpoint. */
#define CEPIS_DEFAULT_BASE_URL "https://cep.is"
@@ -77,6 +81,12 @@ extern "C" {
/** Size of ::cepis_error_t::message, terminator included. */
#define CEPIS_ERROR_MESSAGE_MAX 256
+/** Size of the longer ::cepis_faixa_t members, terminator included. */
+#define CEPIS_FAIXA_NAME_MAX 80
+
+/** Size of the short coded ::cepis_faixa_t members, terminator included. */
+#define CEPIS_FAIXA_CODE_MAX 16
+
/**
* @brief Result of every fallible call.
*
@@ -108,11 +118,58 @@ typedef enum cepis_status {
} cepis_status_t;
/**
+ * @brief The municipality a CEP that does not exist would have belonged to.
+ *
+ * Context for a ::CEPIS_ERR_NOT_FOUND, **not an address**. The CEP asked for
+ * still does not exist and the lookup still failed with a 404; this only names
+ * the band it fell inside. Never promote it to a delivery address, and never
+ * let it turn a not-found into a success.
+ *
+ * It exists because a CEP that fails is rarely a random number: it is usually
+ * someone who knows the city and guessed the last digits, and the classic guess
+ * is the `-000` that 957 municipalities never received.
+ *
+ * @section faixabounds What the bounds are, and are not
+ *
+ * ::cep_ini and ::cep_fim are the smallest and the largest CEP that **actually
+ * exist** in that municipality -- not the range the Correios publish for it.
+ * For Areal/RJ the DNE's own start is `25845000`, which is exactly the CEP that
+ * just failed; ::cep_ini is `25845001`, the first one really there.
+ *
+ * The range is **sparse**. Areal's ::ceps is 118, spread over the ~4900 numbers
+ * between `25845001` and `25849899`. Iterating the interval, or offering the
+ * pair as a list of valid CEPs, produces mostly CEPs that do not exist either.
+ * ::ceps is there precisely so that cannot pass unnoticed: use it to say "118
+ * CEPs in Areal", never "4899 CEPs".
+ */
+typedef struct cepis_faixa {
+ /** Municipality name. Never `NULL`, never empty when present. */
+ char localidade[CEPIS_FAIXA_NAME_MAX];
+ /** Two-letter state code, e.g. `"RJ"`. */
+ char uf[CEPIS_FAIXA_CODE_MAX];
+ char estado[CEPIS_FAIXA_NAME_MAX];
+ char regiao[CEPIS_FAIXA_CODE_MAX];
+ /** IBGE municipality code. */
+ char ibge[CEPIS_FAIXA_CODE_MAX];
+ /** Smallest CEP that exists there, unmasked. */
+ char cep_ini[CEPIS_CEP_LEN + 1];
+ /** Largest CEP that exists there, unmasked. */
+ char cep_fim[CEPIS_CEP_LEN + 1];
+ /**
+ * How many CEPs exist between ::cep_ini and ::cep_fim. Far smaller
+ * than the numeric distance between them. `0` when the API did not
+ * say.
+ */
+ long long ceps;
+} cepis_faixa_t;
+
+/**
* @brief Detail about a failure.
*
- * Owns no memory: the message lives in a fixed buffer inside the struct, so an
- * error can be declared on the stack and forgotten. Pass a pointer to one as
- * the last argument of any fallible call, or `NULL` to discard the detail.
+ * Owns no memory: the message and ::faixa live in fixed buffers inside the
+ * struct, so an error can be declared on the stack and forgotten. Pass a
+ * pointer to one as the last argument of any fallible call, or `NULL` to
+ * discard the detail.
*/
typedef struct cepis_error {
/** Same value the call returned. */
@@ -128,6 +185,19 @@ typedef struct cepis_error {
* attempts it made; it is exposed for rescheduling the work.
*/
double retry_after;
+ /**
+ * `true` when ::faixa was filled. Only ::CEPIS_ERR_NOT_FOUND from
+ * ::cepis_lookup ever sets it, and only when the CEP fell inside a
+ * band some municipality owns -- the API **omits** the block, rather
+ * than sending null, for a band nobody owns. Always test this before
+ * reading ::faixa, or use ::cepis_error_faixa.
+ */
+ bool has_faixa;
+ /**
+ * The municipality the missing CEP would have belonged to. Meaningful
+ * only while ::has_faixa is `true`; untouched otherwise.
+ */
+ cepis_faixa_t faixa;
} cepis_error_t;
/**
@@ -345,6 +415,11 @@ const char *cepis_client_user_agent(const cepis_client_t *client);
* own message: the format already passed local validation, so the server knows
* something this client does not.
*
+ * On ::CEPIS_ERR_NOT_FOUND the API often names the municipality the CEP would
+ * have belonged to; it arrives in ::cepis_error_t::faixa, guarded by
+ * ::cepis_error_t::has_faixa. It is context for the failure and never an
+ * address -- see ::cepis_faixa_t.
+ *
* This is the only call that carries ::cepis_address_t::geo.
*/
cepis_status_t cepis_lookup(cepis_client_t *client, const char *cep,
@@ -458,6 +533,28 @@ bool cepis_geo_is_precise(const cepis_geo_t *geo);
/** @brief `true` when the API reports itself healthy. `false` for `NULL`. */
bool cepis_health_ok(const cepis_health_t *health);
+/**
+ * @brief The municipality named by a not-found error, or `NULL`.
+ *
+ * @return A pointer borrowed from @p err, valid as long as @p err is, or
+ * `NULL` when the API named no municipality -- for `NULL`, for any
+ * status other than ::CEPIS_ERR_NOT_FOUND, and for a CEP that fell in
+ * a band nobody owns.
+ *
+ * Reads better than testing ::cepis_error_t::has_faixa by hand and gives the
+ * absent case the same shape as ::cepis_address_t::geo:
+ *
+ * @code
+ * const cepis_faixa_t *faixa = cepis_error_faixa(&err);
+ *
+ * if (faixa != NULL)
+ * printf("no such CEP; %s/%s has %lld, from %s to %s\n",
+ * faixa->localidade, faixa->uf, faixa->ceps, faixa->cep_ini,
+ * faixa->cep_fim);
+ * @endcode
+ */
+const cepis_faixa_t *cepis_error_faixa(const cepis_error_t *err);
+
/** @brief A short, stable, English name for a status code. */
const char *cepis_status_str(cepis_status_t status);