#include "cepis/models.hpp" #include #include #include #include #include #include namespace cepis { namespace { std::string as_field(const Json* value) { if (value == nullptr) { return {}; } switch (value->kind()) { case Json::Kind::String: return value->as_string(); case Json::Kind::Number: { const double number = value->as_number(); if (number == std::trunc(number) && std::fabs(number) < 9.0e15) { return std::to_string(static_cast(number)); } return Json::number(number).dump(); } case Json::Kind::Bool: return value->as_bool() ? "true" : "false"; default: return {}; } } std::optional as_double(const Json* value) { if (value == nullptr) { return std::nullopt; } if (value->is_number()) { return value->as_number(); } if (!value->is_string()) { return std::nullopt; } const std::string& text = value->as_string(); if (text.empty()) { return std::nullopt; } errno = 0; char* end = nullptr; const double parsed = std::strtod(text.c_str(), &end); if (errno != 0 || end != text.c_str() + text.size() || !std::isfinite(parsed)) { return std::nullopt; } return parsed; } std::optional as_integer(const Json* value) { if (value == nullptr) { return std::nullopt; } if (value->is_number()) { const double number = value->as_number(); if (!std::isfinite(number) || std::fabs(number) >= 9.0e18) { return std::nullopt; } return static_cast(std::trunc(number)); } if (!value->is_string()) { return std::nullopt; } const std::string& text = value->as_string(); if (text.empty()) { return std::nullopt; } errno = 0; char* end = nullptr; const long long parsed = std::strtoll(text.c_str(), &end, 10); if (errno != 0 || end != text.c_str() + text.size()) { return std::nullopt; } return static_cast(parsed); } std::optional geo_from_json(const Json* block) { if (block == nullptr || !block->is_object()) { return std::nullopt; } const std::optional lat = as_double(block->find("lat")); const std::optional lon = as_double(block->find("lon")); const std::optional raio_m = as_integer(block->find("raio_m")); const Json* precisao = block->find("precisao"); if (!lat || !lon || !raio_m || precisao == nullptr || !precisao->is_string()) { return std::nullopt; } return Geo{*lat, *lon, *raio_m, precisao->as_string()}; } } // namespace std::string Address::formatted_cep() const { if (cep.size() != 8) { return cep; } return cep.substr(0, 5) + "-" + cep.substr(5); } Address Address::from_json(const Json& data) { Address address; address.cep = as_field(data.find("cep")); address.logradouro = as_field(data.find("logradouro")); address.complemento = as_field(data.find("complemento")); address.unidade = as_field(data.find("unidade")); address.bairro = as_field(data.find("bairro")); address.localidade = as_field(data.find("localidade")); address.uf = as_field(data.find("uf")); address.estado = as_field(data.find("estado")); address.regiao = as_field(data.find("regiao")); address.ibge = as_field(data.find("ibge")); address.gia = as_field(data.find("gia")); address.ddd = as_field(data.find("ddd")); address.siafi = as_field(data.find("siafi")); address.geo = geo_from_json(data.find("geo")); address.raw = data; return address; } Health Health::from_json(const Json& data) { Health health; health.status = as_field(data.find("status")); health.dne = as_field(data.find("dne")); health.ceps = as_integer(data.find("ceps")).value_or(0); health.geo = as_integer(data.find("geo")).value_or(0); health.base_gerada_em = as_field(data.find("base_gerada_em")); return health; } } // namespace cepis