summaryrefslogtreecommitdiffstats
path: root/lib/src/client.dart
diff options
context:
space:
mode:
authorMurilo Ijanc <[email protected]>2026-07-27 20:25:04 -0300
committerMurilo Ijanc <[email protected]>2026-07-27 20:53:07 -0300
commit82d92ed4291663bae94014b4363064dbb26c6a51 (patch)
tree69d91a59c955f1e6ec6fc0007324b5d73b3f0d01 /lib/src/client.dart
parent8e719fc715eb7ae9aa1c842e633f15b5c6cd3688 (diff)
downloaddart-3f63360ff982ef2a932eca814fdd74d4cb4b1de9.tar.gz
Name the municipality behind a CEP that does not existHEADv0.2.0main
A CEP that fails a lookup is rarely a random number: it is usually someone who knows the city and guessed the digits, and the classic guess is the -000 that 957 municipalities never received. The API now attaches a faixa block to the 404 naming the municipality that band belongs to, so the caller can answer "no, but you were looking at Areal/RJ" instead of just "no". It stays an error. Faixa hangs off CepNotFoundException, never off Address, and cepIni/cepFim are the extremes that actually exist in the municipality rather than the range the Correios publish — echoing back the published start would return the very CEP that just failed. The interval is sparse, which is what ceps is for; it is not a list of alternatives. Parsing is deliberately forgiving: a missing, empty or malformed block is null. Losing a clean not-found to a parse error over the hint attached to it would be a bad trade.
Diffstat (limited to 'lib/src/client.dart')
-rw-r--r--lib/src/client.dart25
1 files changed, 25 insertions, 0 deletions
diff --git a/lib/src/client.dart b/lib/src/client.dart
index 9ddc165..dc0ddb8 100644
--- a/lib/src/client.dart
+++ b/lib/src/client.dart
@@ -186,6 +186,10 @@ class Client {
/// own message: the format already passed local validation, so the server
/// knows something this client does not.
///
+ /// A 404 may carry a [Faixa] on [CepNotFoundException.faixa], naming the
+ /// municipality the CEP would have belonged to. It is context for the error,
+ /// not an address, and it is `null` whenever the API did not send one.
+ ///
/// Throws [InvalidCepException] when the CEP is not 8 digits — no request is
/// made; [CepNotFoundException] when it is well-formed but absent from the
/// database; [RateLimitedException] on 429 after every attempt;
@@ -200,6 +204,7 @@ class Client {
throw CepNotFoundException(
'CEP $number does not exist in the DNE database',
cep: number,
+ faixa: _faixa(reply.body),
);
}
if (reply.status == 400) {
@@ -458,6 +463,26 @@ String _errorMessage(String body, String fallback) {
return fallback;
}
+/// Reads the `faixa` block out of a 404 body, or returns `null`.
+///
+/// Every failure — a body that is not JSON, no `faixa` key, a `faixa` that is
+/// not an object, one that is empty — is the same `null`. The 404 is the
+/// answer the caller asked for; it must not be lost to a parse error over the
+/// hint hanging off it.
+Faixa? _faixa(String body) {
+ final Object? data;
+ try {
+ data = jsonDecode(body);
+ } on FormatException {
+ return null;
+ }
+ if (data is! Map<String, dynamic>) {
+ return null;
+ }
+ final block = data['faixa'];
+ return block is Map<String, dynamic> ? Faixa.fromJson(block) : null;
+}
+
Duration? _retryAfter(String? value) {
if (value == null) {
return null;