1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
#ifndef CEPIS_MODELS_HPP
#define CEPIS_MODELS_HPP
#include <cstdint>
#include <optional>
#include <string>
#include "cepis/json.hpp"
/// The data the API returns, as values.
///
/// **Field names are the API's own, in Portuguese** — `logradouro`, `bairro`,
/// `localidade`. Everything else in this library is English; the fields are the
/// one deliberate exception.
///
/// The reason is fidelity: what you read in the JSON at https://cep.is/01001000
/// is exactly what you access on the object. Translating to `street`/`city`
/// would mean maintaining a field-by-field mapping and forcing anyone reading
/// the API docs alongside this library to translate back in their head. The
/// values are Portuguese either way — they come from the Correios database.
namespace cepis {
/// The CEP's coordinate, when the database has one.
///
/// The point is the **median** of the coordinates of that CEP's addresses, not
/// the mean: one badly geocoded address 40 km away shifts a mean and does not
/// move a median.
///
/// Always check `precisao` before using `lat`/`lon` for distance:
///
/// - `"logradouro"`: the point is a stretch of street. `raio_m` lands around
/// 100–150 m. Good for maps and for distance.
/// - `"localidade"`: the CEP *is* the whole municipality (a city-wide general
/// CEP), and the point is its centroid. `raio_m` runs from 7 km to 40 km.
/// Computing shipping from this is wrong by tens of kilometres.
///
/// `raio_m` is the radius containing 90% of that CEP's addresses. It exists
/// precisely so that error cannot pass unnoticed.
struct Geo {
double lat = 0.0;
double lon = 0.0;
std::int64_t raio_m = 0;
std::string precisao;
/// `true` when the coordinate points at a street, not a municipality.
///
/// Shorthand for `precisao == "logradouro"`, which is the check to make
/// before using the point to measure distance.
[[nodiscard]] bool is_precise() const noexcept { return precisao == "logradouro"; }
};
/// A CEP and everything the database knows about it.
///
/// Fields the database does not have for that CEP come back as empty strings,
/// never absent — that is what the API returns, and changing it here would
/// create a difference between reading the JSON and reading this library.
///
/// `geo` is the exception: it is `std::nullopt` when there is no coordinate.
/// There the difference is real, because the API **omits** the whole field in
/// that case (about 43% of CEPs) rather than sending null.
struct Address {
std::string cep;
std::string logradouro;
std::string complemento;
std::string unidade;
std::string bairro;
std::string localidade;
std::string uf;
std::string estado;
std::string regiao;
std::string ibge;
std::string gia;
std::string ddd;
std::string siafi;
std::optional<Geo> geo;
/// The response exactly as it arrived, uninterpreted, so a field the API
/// grows before this library is updated is not lost.
Json raw;
/// The CEP with its mask: `01001-000`.
///
/// The native contract always returns it unmasked; formatting is
/// presentation, and it lives here so it does not become a repeated
/// concatenation in every project that uses this.
[[nodiscard]] std::string formatted_cep() const;
/// Builds from the object the API returned.
///
/// Unknown fields are ignored during construction but preserved in `raw`. A
/// new field in the API therefore cannot break this library.
///
/// A malformed `geo` block yields an empty `geo` rather than propagating the
/// error: it is not worth losing the whole address over, since the rest of
/// the response is still useful.
static Address from_json(const Json& data);
};
/// The municipality a CEP that does **not** exist would have belonged to.
///
/// It arrives on a `404`, attached to `CepNotFound`. It is context for an
/// error, never an address: the CEP still does not exist, and nothing in this
/// block makes it deliverable. Treat it as a hint for the message you show —
/// "did you mean somewhere in Areal/RJ?" — not as a result.
///
/// It exists because a CEP that fails is rarely random. It is usually someone
/// who knows the city and guessed the number, and the classic guess is the
/// `-000` that 957 municipalities never received.
///
/// `cep_ini` and `cep_fim` are the smallest and the largest CEP that
/// **actually exist** in that municipality — not the bounds the Correios
/// publish. For Areal the DNE's own start is `25845000`, which is precisely
/// the CEP that just failed; `cep_ini` is `25845001`.
///
/// The range is also **sparse**: Areal's 118 CEPs are scattered across the
/// ~4,900 numbers between `25845001` and `25849899`. That is what `ceps` is
/// for. The pair is a span, not a list — walking from `cep_ini` to `cep_fim`
/// produces thousands of CEPs that do not exist either.
struct Faixa {
std::string localidade;
std::string uf;
std::string estado;
std::string regiao;
std::string ibge;
std::string cep_ini;
std::string cep_fim;
std::int64_t ceps = 0;
/// Reads the API's `faixa` block, or `std::nullopt` when it cannot be read.
///
/// Anything unexpected — a missing block, a non-object, an empty one, a
/// municipality without a name — yields `std::nullopt` rather than an
/// error. The 404 is already the answer; failing to read the hint attached
/// to it must not turn a clean not-found into a parse failure.
static std::optional<Faixa> from_json(const Json& data);
};
/// The response of `GET /health`.
///
/// Good for two things: checking that the API answers, and knowing **which
/// version of the database** is live. The DNE database changes every two
/// weeks; `dne` identifies it.
struct Health {
std::string status;
std::string dne;
std::int64_t ceps = 0;
std::int64_t geo = 0;
std::string base_gerada_em;
/// `true` when the API reports itself healthy.
[[nodiscard]] bool ok() const noexcept { return status == "ok"; }
/// Builds from the object the API returned.
static Health from_json(const Json& data);
};
} // namespace cepis
#endif // CEPIS_MODELS_HPP
|