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
|
#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 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
|