guhg
This commit is contained in:
parent
24b6a72614
commit
1a2fba7fb8
29 changed files with 1676 additions and 1401 deletions
|
@ -1,18 +1,37 @@
|
|||
#include <chrono>
|
||||
#include <curl/curl.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include "weather.hpp"
|
||||
|
||||
#include "weather.h"
|
||||
#include <chrono> // std::chrono::{duration, operator-}
|
||||
#include <curl/curl.h> // curl_easy_init, curl_easy_setopt, curl_easy_perform, curl_easy_cleanup
|
||||
#include <expected> // std::{expected (Result), unexpected (Err)}
|
||||
#include <filesystem> // std::filesystem::{path, remove, rename}
|
||||
#include <format> // std::format
|
||||
#include <fstream> // std::{ifstream, ofstream}
|
||||
#include <glaze/core/context.hpp> // glz::{error_ctx, error_code}
|
||||
#include <glaze/core/opts.hpp> // glz::check_partial_read
|
||||
#include <glaze/core/read.hpp> // glz::read
|
||||
#include <glaze/core/reflect.hpp> // glz::format_error
|
||||
#include <glaze/json/write.hpp> // glz::write_json
|
||||
#include <glaze/util/atoi.hpp> // glz::atoi
|
||||
#include <iterator> // std::istreambuf_iterator
|
||||
#include <system_error> // std::error_code
|
||||
#include <utility> // std::move
|
||||
#include <variant> // std::{get, holds_alternative}
|
||||
|
||||
#include "config.h"
|
||||
#include "src/util/macros.h"
|
||||
#include "src/core/util/defs.hpp"
|
||||
#include "src/core/util/logging.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::string_literals;
|
||||
|
||||
using namespace weather;
|
||||
|
||||
using util::types::i32, util::types::Err, util::types::Exception;
|
||||
|
||||
namespace {
|
||||
constexpr glz::opts glaze_opts = { .error_on_unknown_keys = false };
|
||||
using glz::opts, glz::error_ctx, glz::error_code, glz::write_json, glz::read, glz::format_error;
|
||||
|
||||
constexpr opts glaze_opts = { .error_on_unknown_keys = false };
|
||||
|
||||
fn GetCachePath() -> Result<fs::path, String> {
|
||||
std::error_code errc;
|
||||
|
@ -25,7 +44,7 @@ namespace {
|
|||
return cachePath;
|
||||
}
|
||||
|
||||
fn ReadCacheFromFile() -> Result<WeatherOutput, String> {
|
||||
fn ReadCacheFromFile() -> Result<Output, String> {
|
||||
Result<fs::path, String> cachePath = GetCachePath();
|
||||
|
||||
if (!cachePath)
|
||||
|
@ -36,27 +55,27 @@ namespace {
|
|||
if (!ifs.is_open())
|
||||
return Err("Cache file not found: " + cachePath->string());
|
||||
|
||||
DEBUG_LOG("Reading from cache file...");
|
||||
debug_log("Reading from cache file...");
|
||||
|
||||
try {
|
||||
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
WeatherOutput result;
|
||||
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
Output result;
|
||||
|
||||
if (const glz::error_ctx errc = glz::read<glaze_opts>(result, content); errc.ec != glz::error_code::none)
|
||||
return Err("JSON parse error: " + glz::format_error(errc, content));
|
||||
if (const error_ctx errc = read<glaze_opts>(result, content); errc.ec != error_code::none)
|
||||
return Err(std::format("JSON parse error: {}", format_error(errc, content)));
|
||||
|
||||
DEBUG_LOG("Successfully read from cache file.");
|
||||
debug_log("Successfully read from cache file.");
|
||||
return result;
|
||||
} catch (const std::exception& e) { return Err("Error reading cache: "s + e.what()); }
|
||||
} catch (const Exception& e) { return Err(std::format("Error reading cache: {}", e.what())); }
|
||||
}
|
||||
|
||||
fn WriteCacheToFile(const WeatherOutput& data) -> Result<void, String> {
|
||||
fn WriteCacheToFile(const Output& data) -> Result<void, String> {
|
||||
Result<fs::path, String> cachePath = GetCachePath();
|
||||
|
||||
if (!cachePath)
|
||||
return Err(cachePath.error());
|
||||
|
||||
DEBUG_LOG("Writing to cache file...");
|
||||
debug_log("Writing to cache file...");
|
||||
fs::path tempPath = *cachePath;
|
||||
tempPath += ".tmp";
|
||||
|
||||
|
@ -68,8 +87,8 @@ namespace {
|
|||
|
||||
String jsonStr;
|
||||
|
||||
if (const glz::error_ctx errc = glz::write_json(data, jsonStr); errc.ec != glz::error_code::none)
|
||||
return Err("JSON serialization error: " + glz::format_error(errc, jsonStr));
|
||||
if (const error_ctx errc = write_json(data, jsonStr); errc.ec != error_code::none)
|
||||
return Err("JSON serialization error: " + format_error(errc, jsonStr));
|
||||
|
||||
ofs << jsonStr;
|
||||
if (!ofs)
|
||||
|
@ -80,24 +99,24 @@ namespace {
|
|||
fs::rename(tempPath, *cachePath, errc);
|
||||
if (errc) {
|
||||
if (!fs::remove(tempPath, errc))
|
||||
DEBUG_LOG("Failed to remove temp file: {}", errc.message());
|
||||
debug_log("Failed to remove temp file: {}", errc.message());
|
||||
|
||||
return Err("Failed to replace cache file: " + errc.message());
|
||||
return Err(std::format("Failed to replace cache file: {}", errc.message()));
|
||||
}
|
||||
|
||||
DEBUG_LOG("Successfully wrote to cache file.");
|
||||
debug_log("Successfully wrote to cache file.");
|
||||
return {};
|
||||
} catch (const std::exception& e) { return Err("File operation error: "s + e.what()); }
|
||||
} catch (const Exception& e) { return Err(std::format("File operation error: {}", e.what())); }
|
||||
}
|
||||
|
||||
fn WriteCallback(void* contents, const size_t size, const size_t nmemb, String* str) -> size_t {
|
||||
const size_t totalSize = size * nmemb;
|
||||
fn WriteCallback(void* contents, const usize size, const usize nmemb, String* str) -> usize {
|
||||
const usize totalSize = size * nmemb;
|
||||
str->append(static_cast<char*>(contents), totalSize);
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
fn MakeApiRequest(const String& url) -> Result<WeatherOutput, String> {
|
||||
DEBUG_LOG("Making API request to URL: {}", url);
|
||||
fn MakeApiRequest(const String& url) -> Result<Output, String> {
|
||||
debug_log("Making API request to URL: {}", url);
|
||||
CURL* curl = curl_easy_init();
|
||||
String responseBuffer;
|
||||
|
||||
|
@ -116,40 +135,40 @@ namespace {
|
|||
if (res != CURLE_OK)
|
||||
return Err(std::format("cURL error: {}", curl_easy_strerror(res)));
|
||||
|
||||
WeatherOutput output;
|
||||
Output output;
|
||||
|
||||
if (const glz::error_ctx errc = glz::read<glaze_opts>(output, responseBuffer); errc.ec != glz::error_code::none)
|
||||
return Err("API response parse error: " + glz::format_error(errc, responseBuffer));
|
||||
if (const error_ctx errc = glz::read<glaze_opts>(output, responseBuffer); errc.ec != error_code::none)
|
||||
return Err("API response parse error: " + format_error(errc, responseBuffer));
|
||||
|
||||
return std::move(output);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
fn Weather::getWeatherInfo() const -> WeatherOutput {
|
||||
fn Weather::getWeatherInfo() const -> Output {
|
||||
using namespace std::chrono;
|
||||
|
||||
if (Result<WeatherOutput, String> data = ReadCacheFromFile()) {
|
||||
const WeatherOutput& dataVal = *data;
|
||||
if (Result<Output, String> data = ReadCacheFromFile()) {
|
||||
const Output& dataVal = *data;
|
||||
|
||||
if (const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
|
||||
cacheAge < 10min) {
|
||||
DEBUG_LOG("Using valid cache");
|
||||
debug_log("Using valid cache");
|
||||
return dataVal;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Cache expired");
|
||||
debug_log("Cache expired");
|
||||
} else {
|
||||
DEBUG_LOG("Cache error: {}", data.error());
|
||||
debug_log("Cache error: {}", data.error());
|
||||
}
|
||||
|
||||
fn handleApiResult = [](const Result<WeatherOutput, String>& result) -> WeatherOutput {
|
||||
fn handleApiResult = [](const Result<Output, String>& result) -> Output {
|
||||
if (!result) {
|
||||
ERROR_LOG("API request failed: {}", result.error());
|
||||
return WeatherOutput {};
|
||||
error_log("API request failed: {}", result.error());
|
||||
return Output {};
|
||||
}
|
||||
|
||||
if (Result<void, String> writeResult = WriteCacheToFile(*result); !writeResult)
|
||||
ERROR_LOG("Failed to write cache: {}", writeResult.error());
|
||||
error_log("Failed to write cache: {}", writeResult.error());
|
||||
|
||||
return *result;
|
||||
};
|
||||
|
@ -157,7 +176,7 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
|
|||
if (std::holds_alternative<String>(location)) {
|
||||
const auto& city = std::get<String>(location);
|
||||
char* escaped = curl_easy_escape(nullptr, city.c_str(), static_cast<i32>(city.length()));
|
||||
DEBUG_LOG("Requesting city: {}", escaped);
|
||||
debug_log("Requesting city: {}", escaped);
|
||||
|
||||
const String apiUrl =
|
||||
std::format("https://api.openweathermap.org/data/2.5/weather?q={}&appid={}&units={}", escaped, api_key, units);
|
||||
|
@ -167,7 +186,7 @@ fn Weather::getWeatherInfo() const -> WeatherOutput {
|
|||
}
|
||||
|
||||
const auto& [lat, lon] = std::get<Coords>(location);
|
||||
DEBUG_LOG("Requesting coordinates: lat={:.3f}, lon={:.3f}", lat, lon);
|
||||
debug_log("Requesting coordinates: lat={:.3f}, lon={:.3f}", lat, lon);
|
||||
|
||||
const String apiUrl = std::format(
|
||||
"https://api.openweathermap.org/data/2.5/weather?lat={:.3f}&lon={:.3f}&appid={}&units={}", lat, lon, api_key, units
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue