oh right
This commit is contained in:
parent
9a95178095
commit
631964469f
26 changed files with 482 additions and 335 deletions
|
@ -10,10 +10,10 @@
|
||||||
#include <toml++/impl/table.hpp> // toml::table
|
#include <toml++/impl/table.hpp> // toml::table
|
||||||
#include <unistd.h> // getuid
|
#include <unistd.h> // getuid
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/helpers.hpp"
|
#include "src/util/helpers.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/logging.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@
|
||||||
#include <pwd.h> // getpwuid, passwd
|
#include <pwd.h> // getpwuid, passwd
|
||||||
#include <unistd.h> // getuid
|
#include <unistd.h> // getuid
|
||||||
|
|
||||||
#include "src/core/util/helpers.hpp"
|
#include "src/util/helpers.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/logging.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
#include "weather.hpp"
|
#include "weather.hpp"
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,14 @@
|
||||||
#include <glaze/json/read.hpp> // NOLINT(misc-include-cleaner) - glaze/json/read.hpp is needed for glz::read<glz::opts>
|
#include <glaze/json/read.hpp> // NOLINT(misc-include-cleaner) - glaze/json/read.hpp is needed for glz::read<glz::opts>
|
||||||
#include <ios> // std::ios::{binary, trunc}
|
#include <ios> // std::ios::{binary, trunc}
|
||||||
#include <iterator> // std::istreambuf_iterator
|
#include <iterator> // std::istreambuf_iterator
|
||||||
#include <system_error> // std::error_code
|
|
||||||
#include <utility> // std::move
|
#include <utility> // std::move
|
||||||
#include <variant> // std::{get, holds_alternative}
|
#include <variant> // std::{get, holds_alternative}
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/cache.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/logging.hpp"
|
||||||
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
|
||||||
|
@ -35,109 +35,10 @@ namespace {
|
||||||
using util::error::DracError, util::error::DracErrorCode;
|
using util::error::DracError, util::error::DracErrorCode;
|
||||||
using util::types::usize, util::types::Err, util::types::Exception;
|
using util::types::usize, util::types::Err, util::types::Exception;
|
||||||
using weather::Coords;
|
using weather::Coords;
|
||||||
|
using namespace util::cache;
|
||||||
|
|
||||||
constexpr opts glaze_opts = { .error_on_unknown_keys = false };
|
constexpr opts glaze_opts = { .error_on_unknown_keys = false };
|
||||||
|
|
||||||
fn GetCachePath() -> Result<fs::path, String> {
|
|
||||||
std::error_code errc;
|
|
||||||
fs::path cachePath = fs::temp_directory_path(errc);
|
|
||||||
|
|
||||||
if (errc)
|
|
||||||
return Err("Failed to get temp directory: " + errc.message());
|
|
||||||
|
|
||||||
cachePath /= "weather_cache.beve";
|
|
||||||
return cachePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ReadCacheFromFile() -> Result<Output, String> {
|
|
||||||
Result<fs::path, String> cachePath = GetCachePath();
|
|
||||||
|
|
||||||
if (!cachePath)
|
|
||||||
return Err(cachePath.error());
|
|
||||||
|
|
||||||
std::ifstream ifs(*cachePath, std::ios::binary);
|
|
||||||
|
|
||||||
if (!ifs.is_open())
|
|
||||||
return Err("Cache file not found: " + cachePath->string());
|
|
||||||
|
|
||||||
debug_log("Reading from cache file...");
|
|
||||||
|
|
||||||
try {
|
|
||||||
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
|
||||||
ifs.close();
|
|
||||||
|
|
||||||
if (content.empty())
|
|
||||||
return Err(std::format("BEVE cache file is empty: {}", cachePath->string()));
|
|
||||||
|
|
||||||
Output result;
|
|
||||||
|
|
||||||
if (const error_ctx glazeErr = read_beve(result, content); glazeErr.ec != error_code::none)
|
|
||||||
return Err(std::format(
|
|
||||||
"BEVE parse error reading cache (code {}): {}", static_cast<int>(glazeErr.ec), cachePath->string()
|
|
||||||
));
|
|
||||||
|
|
||||||
debug_log("Successfully read from cache file.");
|
|
||||||
return result;
|
|
||||||
} catch (const Exception& e) { return Err(std::format("Error reading cache: {}", e.what())); }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn WriteCacheToFile(const Output& data) -> Result<void, String> {
|
|
||||||
using util::types::isize;
|
|
||||||
|
|
||||||
Result<fs::path, String> cachePath = GetCachePath();
|
|
||||||
|
|
||||||
if (!cachePath)
|
|
||||||
return Err(cachePath.error());
|
|
||||||
|
|
||||||
debug_log("Writing to cache file...");
|
|
||||||
fs::path tempPath = *cachePath;
|
|
||||||
tempPath += ".tmp";
|
|
||||||
|
|
||||||
try {
|
|
||||||
String binaryBuffer;
|
|
||||||
|
|
||||||
if (const error_ctx glazeErr = write_beve(data, binaryBuffer); glazeErr)
|
|
||||||
return Err(std::format("BEVE serialization error writing cache (code {})", static_cast<int>(glazeErr.ec)));
|
|
||||||
|
|
||||||
{
|
|
||||||
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
|
|
||||||
if (!ofs.is_open())
|
|
||||||
return Err("Failed to open temp file: " + tempPath.string());
|
|
||||||
|
|
||||||
ofs.write(binaryBuffer.data(), static_cast<isize>(binaryBuffer.size()));
|
|
||||||
if (!ofs) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err("Failed to write to temp BEVE cache file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::error_code errc;
|
|
||||||
fs::rename(tempPath, *cachePath, errc);
|
|
||||||
if (errc) {
|
|
||||||
if (!fs::remove(tempPath, errc))
|
|
||||||
debug_log("Failed to remove temp file: {}", errc.message());
|
|
||||||
|
|
||||||
return Err(std::format("Failed to replace cache file: {}", errc.message()));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_log("Successfully wrote to cache file.");
|
|
||||||
return {};
|
|
||||||
} catch (const std::ios_base::failure& e) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err(std::format("Filesystem error writing BEVE cache file {}: {}", tempPath.string(), e.what()));
|
|
||||||
} catch (const Exception& e) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err(std::format("File operation error during BEVE cache write: {}", e.what()));
|
|
||||||
} catch (...) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err(std::format("Unknown error writing BEVE cache file: {}", tempPath.string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn WriteCallback(void* contents, const usize size, const usize nmemb, String* str) -> usize {
|
fn WriteCallback(void* contents, const usize size, const usize nmemb, String* str) -> usize {
|
||||||
const usize totalSize = size * nmemb;
|
const usize totalSize = size * nmemb;
|
||||||
str->append(static_cast<char*>(contents), totalSize);
|
str->append(static_cast<char*>(contents), totalSize);
|
||||||
|
@ -177,7 +78,7 @@ fn Weather::getWeatherInfo() const -> Result<Output, DracError> {
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using util::types::i32;
|
using util::types::i32;
|
||||||
|
|
||||||
if (Result<Output, String> data = ReadCacheFromFile()) {
|
if (Result<Output, DracError> data = ReadCache<Output>("weather")) {
|
||||||
const Output& dataVal = *data;
|
const Output& dataVal = *data;
|
||||||
|
|
||||||
if (const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
|
if (const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
|
||||||
|
@ -188,15 +89,15 @@ fn Weather::getWeatherInfo() const -> Result<Output, DracError> {
|
||||||
|
|
||||||
debug_log("Cache expired");
|
debug_log("Cache expired");
|
||||||
} else {
|
} else {
|
||||||
debug_log("Cache error: {}", data.error());
|
error_at(data.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleApiResult = [](const Result<Output, String>& result) -> Result<Output, DracError> {
|
fn handleApiResult = [](const Result<Output, String>& result) -> Result<Output, DracError> {
|
||||||
if (!result)
|
if (!result)
|
||||||
return Err(DracError(DracErrorCode::ApiUnavailable, result.error()));
|
return Err(DracError(DracErrorCode::ApiUnavailable, result.error()));
|
||||||
|
|
||||||
if (Result<void, String> writeResult = WriteCacheToFile(*result); !writeResult)
|
if (Result<void, DracError> writeResult = WriteCache("weather", *result); !writeResult)
|
||||||
error_log("Failed to write cache: {}", writeResult.error());
|
error_at(writeResult.error());
|
||||||
|
|
||||||
return *result;
|
return *result;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <glaze/core/common.hpp> // object
|
#include <glaze/core/common.hpp> // object
|
||||||
#include <glaze/core/meta.hpp> // Object
|
#include <glaze/core/meta.hpp> // Object
|
||||||
|
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
namespace weather {
|
namespace weather {
|
||||||
using glz::detail::Object, glz::object;
|
using glz::detail::Object, glz::object;
|
||||||
|
|
91
src/core/package.hpp
Normal file
91
src/core/package.hpp
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem> // std::filesystem::path
|
||||||
|
#include <future> // std::future
|
||||||
|
#include <glaze/core/common.hpp> // glz::object
|
||||||
|
#include <glaze/core/meta.hpp> // glz::detail::Object
|
||||||
|
#include <vector> // std::vector
|
||||||
|
|
||||||
|
#include "src/util/defs.hpp"
|
||||||
|
#include "src/util/error.hpp"
|
||||||
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
|
namespace packages {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using util::error::DracError;
|
||||||
|
using util::types::Result, util::types::u64, util::types::i64, util::types::String, util::types::Vec,
|
||||||
|
util::types::Future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct PkgCountCacheData
|
||||||
|
* @brief Structure for caching package count results along with a timestamp.
|
||||||
|
*
|
||||||
|
* Used to avoid redundant lookups in package manager databases or directories
|
||||||
|
* if the underlying data source hasn't changed recently.
|
||||||
|
*/
|
||||||
|
struct PkgCountCacheData {
|
||||||
|
u64 count {}; ///< The cached package count.
|
||||||
|
i64 timestampEpochSeconds {}; ///< The UNIX timestamp (seconds since epoch) when the count was cached.
|
||||||
|
|
||||||
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
|
struct [[maybe_unused]] glaze {
|
||||||
|
using T = PkgCountCacheData;
|
||||||
|
static constexpr glz::detail::Object value =
|
||||||
|
glz::object("count", &T::count, "timestamp", &T::timestampEpochSeconds);
|
||||||
|
};
|
||||||
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct PackageManagerInfo
|
||||||
|
* @brief Holds information needed to query a database-backed package manager.
|
||||||
|
*/
|
||||||
|
struct PackageManagerInfo {
|
||||||
|
String id; ///< Unique identifier for the package manager (used for cache key).
|
||||||
|
fs::path dbPath; ///< Filesystem path to the package manager's database.
|
||||||
|
String countQuery; ///< SQL query string to count the packages.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the total package count by querying all relevant package managers for the current OS.
|
||||||
|
* @details This function orchestrates the process:
|
||||||
|
* 1. Determines the set of relevant package managers (platform-specific + shared).
|
||||||
|
* 2. Launches asynchronous tasks to query each manager.
|
||||||
|
* 3. Aggregates the results, summing counts and logging errors.
|
||||||
|
* @return Result containing the total package count (u64) on success,
|
||||||
|
* or a DracError if the aggregation fails (though individual manager errors are logged).
|
||||||
|
*/
|
||||||
|
fn GetTotalCount() -> Result<u64, DracError>;
|
||||||
|
|
||||||
|
fn GetCountFromDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError>;
|
||||||
|
|
||||||
|
fn GetCountFromDirectory(
|
||||||
|
const String& pmId,
|
||||||
|
const fs::path& dirPath,
|
||||||
|
const String& fileExtensionFilter = "",
|
||||||
|
bool subtractOne = false
|
||||||
|
) -> Result<u64, DracError>;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
fn GetDpkgCount() -> Result<u64, DracError>;
|
||||||
|
fn GetPacmanCount() -> Result<u64, DracError>;
|
||||||
|
fn GetMossCount() -> Result<u64, DracError>;
|
||||||
|
fn GetRpmCount() -> Result<u64, DracError>;
|
||||||
|
fn GetZypperCount() -> Result<u64, DracError>;
|
||||||
|
fn GetPortageCount() -> Result<u64, DracError>;
|
||||||
|
fn GetApkCount() -> Result<u64, DracError>;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
fn GetHomebrewCount() -> Result<u64, DracError>;
|
||||||
|
fn GetMacPortsCount() -> Result<u64, DracError>;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
fn GetWinRTCount() -> Result<u64, DracError>;
|
||||||
|
fn GetChocolateyCount() -> Result<u64, DracError>;
|
||||||
|
fn GetScoopCount() -> Result<u64, DracError>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
fn GetNixCount() -> Result<u64, DracError>;
|
||||||
|
#endif
|
||||||
|
fn GetCargoCount() -> Result<u64, DracError>;
|
||||||
|
} // namespace packages
|
|
@ -7,10 +7,10 @@
|
||||||
|
|
||||||
#include "src/config/config.hpp"
|
#include "src/config/config.hpp"
|
||||||
#include "src/config/weather.hpp"
|
#include "src/config/weather.hpp"
|
||||||
#include "src/core/util/defs.hpp"
|
|
||||||
#include "src/core/util/error.hpp"
|
|
||||||
#include "src/core/util/types.hpp"
|
|
||||||
#include "src/os/os.hpp"
|
#include "src/os/os.hpp"
|
||||||
|
#include "src/util/defs.hpp"
|
||||||
|
#include "src/util/error.hpp"
|
||||||
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
using util::error::DracError, util::error::DracErrorCode;
|
using util::error::DracError, util::error::DracErrorCode;
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,8 @@
|
||||||
|
|
||||||
#include "src/config/config.hpp" // Config
|
#include "src/config/config.hpp" // Config
|
||||||
#include "src/config/weather.hpp" // weather::Output
|
#include "src/config/weather.hpp" // weather::Output
|
||||||
|
#include "src/util/defs.hpp"
|
||||||
#include "util/defs.hpp"
|
#include "src/util/types.hpp"
|
||||||
#include "util/types.hpp"
|
|
||||||
|
|
||||||
struct Config;
|
struct Config;
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
#include "src/config/config.hpp"
|
#include "src/config/config.hpp"
|
||||||
#include "src/config/weather.hpp"
|
#include "src/config/weather.hpp"
|
||||||
#include "src/core/system_data.hpp"
|
#include "src/core/system_data.hpp"
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/logging.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
using ftxui::Color;
|
using ftxui::Color;
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
#include <unistd.h> // readlink
|
#include <unistd.h> // readlink
|
||||||
#include <utility> // std::move
|
#include <utility> // std::move
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/helpers.hpp"
|
#include "src/util/helpers.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/logging.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
#include "src/wrappers/dbus.hpp"
|
#include "src/wrappers/dbus.hpp"
|
||||||
#include "src/wrappers/wayland.hpp"
|
#include "src/wrappers/wayland.hpp"
|
||||||
#include "src/wrappers/xcb.hpp"
|
#include "src/wrappers/xcb.hpp"
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
#include <iterator> // std::istreambuf_iterator
|
#include <iterator> // std::istreambuf_iterator
|
||||||
#include <glaze/beve/read.hpp> // glz::read_beve
|
#include <glaze/beve/read.hpp> // glz::read_beve
|
||||||
#include <glaze/beve/write.hpp> // glz::write_beve
|
#include <glaze/beve/write.hpp> // glz::write_beve
|
||||||
#include <glaze/core/context.hpp> // glz::{context, error_code, error_ctx}
|
|
||||||
#include <system_error> // std::error_code
|
#include <system_error> // std::error_code
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/cache.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/logging.hpp"
|
||||||
|
#include "src/util/types.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
using util::error::DracError, util::error::DracErrorCode;
|
using util::error::DracError, util::error::DracErrorCode;
|
||||||
|
@ -31,6 +31,7 @@ using util::types::u64, util::types::i64, util::types::Result, util::types::Err,
|
||||||
namespace {
|
namespace {
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
using namespace util::cache;
|
||||||
using os::linux::PkgCountCacheData, os::linux::PackageManagerInfo;
|
using os::linux::PkgCountCacheData, os::linux::PackageManagerInfo;
|
||||||
|
|
||||||
fn GetPackageCountInternalDir(
|
fn GetPackageCountInternalDir(
|
||||||
|
@ -111,6 +112,68 @@ namespace {
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> {
|
||||||
|
const auto& [pmId, dbPath, countQuery] = pmInfo;
|
||||||
|
|
||||||
|
if (Result<PkgCountCacheData, DracError> cachedDataResult = ReadCache<PkgCountCacheData>(pmId)) {
|
||||||
|
const auto& [count, timestamp] = *cachedDataResult;
|
||||||
|
std::error_code errc;
|
||||||
|
const std::filesystem::file_time_type dbModTime = fs::last_write_time(dbPath, errc);
|
||||||
|
|
||||||
|
if (errc) {
|
||||||
|
warn_log(
|
||||||
|
"Could not get modification time for '{}': {}. Invalidating {} cache.", dbPath.string(), errc.message(), pmId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (const system_clock::time_point cacheTimePoint = system_clock::time_point(seconds(timestamp));
|
||||||
|
cacheTimePoint.time_since_epoch() >= dbModTime.time_since_epoch()) {
|
||||||
|
debug_log(
|
||||||
|
"Using valid {} package count cache (DB file unchanged since {}).",
|
||||||
|
pmId,
|
||||||
|
std::format("{:%F %T %Z}", floor<seconds>(cacheTimePoint))
|
||||||
|
);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
debug_log("{} package count cache stale (DB file modified).", pmId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cachedDataResult.error().code != DracErrorCode::NotFound)
|
||||||
|
debug_at(cachedDataResult.error());
|
||||||
|
debug_log("{} package count cache not found or unreadable.", pmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log("Fetching fresh {} package count from database: {}", pmId, dbPath.string());
|
||||||
|
u64 count = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const SQLite::Database database(dbPath.string(), SQLite::OPEN_READONLY);
|
||||||
|
if (SQLite::Statement query(database, countQuery); query.executeStep()) {
|
||||||
|
const i64 countInt64 = query.getColumn(0).getInt64();
|
||||||
|
if (countInt64 < 0)
|
||||||
|
return Err(
|
||||||
|
DracError(DracErrorCode::ParseError, std::format("Negative count returned by {} DB COUNT query.", pmId))
|
||||||
|
);
|
||||||
|
count = static_cast<u64>(countInt64);
|
||||||
|
} else {
|
||||||
|
return Err(DracError(DracErrorCode::ParseError, std::format("No rows returned by {} DB COUNT query.", pmId)));
|
||||||
|
}
|
||||||
|
} catch (const SQLite::Exception& e) {
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::ApiUnavailable, std::format("SQLite error occurred accessing {} DB: {}", pmId, e.what())
|
||||||
|
));
|
||||||
|
} catch (const Exception& e) { return Err(DracError(DracErrorCode::InternalError, e.what())); } catch (...) {
|
||||||
|
return Err(DracError(DracErrorCode::Other, std::format("Unknown error occurred accessing {} DB", pmId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const i64 nowEpochSeconds = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
||||||
|
const PkgCountCacheData dataToCache = { .count = count, .timestampEpochSeconds = nowEpochSeconds };
|
||||||
|
|
||||||
|
if (Result<void, DracError> writeResult = WriteCache(pmId, dataToCache); !writeResult)
|
||||||
|
error_at(writeResult.error());
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace os::linux {
|
namespace os::linux {
|
||||||
|
@ -159,10 +222,9 @@ namespace os::linux {
|
||||||
fn GetTotalPackageCount() -> Result<u64, DracError> {
|
fn GetTotalPackageCount() -> Result<u64, DracError> {
|
||||||
using util::types::Array, util::types::Future;
|
using util::types::Array, util::types::Future;
|
||||||
|
|
||||||
Array<Future<Result<u64, DracError>>, 4> futures = {
|
Array<Future<Result<u64, DracError>>, 3> futures = {
|
||||||
std::async(std::launch::async, GetDpkgPackageCount),
|
std::async(std::launch::async, GetDpkgPackageCount),
|
||||||
std::async(std::launch::async, GetMossPackageCount),
|
std::async(std::launch::async, GetMossPackageCount),
|
||||||
std::async(std::launch::async, GetNixPackageCount),
|
|
||||||
std::async(std::launch::async, GetPacmanPackageCount),
|
std::async(std::launch::async, GetPacmanPackageCount),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
#include <glaze/core/common.hpp> // glz::object
|
#include <glaze/core/common.hpp> // glz::object
|
||||||
#include <glaze/core/meta.hpp> // glz::detail::Object
|
#include <glaze/core/meta.hpp> // glz::detail::Object
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
namespace os::linux {
|
namespace os::linux {
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
#include "macos/bridge.hpp"
|
#include "macos/bridge.hpp"
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
using namespace util::types;
|
using namespace util::types;
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
using util::error::DracError;
|
using util::error::DracError;
|
||||||
using util::types::MediaInfo, util::types::String, util::types::Result;
|
using util::types::MediaInfo, util::types::String, util::types::Result;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
using util::error::DracErrorCode;
|
using util::error::DracErrorCode;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @namespace os
|
* @namespace os
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
#include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY}
|
#include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY}
|
||||||
#include <SQLiteCpp/Exception.h> // SQLite::Exception
|
#include <SQLiteCpp/Exception.h> // SQLite::Exception
|
||||||
#include <SQLiteCpp/Statement.h> // SQLite::Statement
|
#include <SQLiteCpp/Statement.h> // SQLite::Statement
|
||||||
#include <chrono> // std::chrono
|
#include <chrono> // std::chrono
|
||||||
#include <filesystem> // std::filesystem
|
#include <filesystem> // std::filesystem
|
||||||
#include <format> // std::format
|
#include <format> // std::format
|
||||||
#include <fstream> // std::{ifstream, ofstream}
|
#include <fstream> // std::{ifstream, ofstream}
|
||||||
#include <glaze/beve/read.hpp> // glz::read_beve
|
#include <glaze/beve/read.hpp> // glz::read_beve
|
||||||
#include <glaze/beve/write.hpp> // glz::write_beve
|
#include <glaze/beve/write.hpp> // glz::write_beve
|
||||||
#include <glaze/core/common.hpp> // glz::object
|
#include <glaze/core/common.hpp> // glz::object
|
||||||
#include <glaze/core/context.hpp> // glz::{context, error_code, error_ctx}
|
#include <glaze/core/meta.hpp> // glz::detail::Object
|
||||||
#include <glaze/core/meta.hpp> // glz::detail::Object
|
#include <ios> // std::ios::{binary, trunc}, std::ios_base
|
||||||
#include <ios> // std::ios::{binary, trunc}, std::ios_base
|
#include <iterator> // std::istreambuf_iterator
|
||||||
#include <iterator> // std::istreambuf_iterator
|
#include <system_error> // std::error_code
|
||||||
#include <system_error> // std::error_code
|
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/cache.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/helpers.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/helpers.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/logging.hpp"
|
||||||
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
using namespace util::cache;
|
||||||
|
|
||||||
struct PackageManagerInfo {
|
struct PackageManagerInfo {
|
||||||
String id;
|
String id;
|
||||||
|
@ -51,136 +52,10 @@ namespace {
|
||||||
// NOLINTEND(readability-identifier-naming)
|
// NOLINTEND(readability-identifier-naming)
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr StringView ALLOWED_PMID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-";
|
|
||||||
|
|
||||||
fn GetPkgCountCachePath(const String& pmId) -> Result<fs::path, DracError> {
|
|
||||||
std::error_code errc;
|
|
||||||
const fs::path cacheDir = fs::temp_directory_path(errc);
|
|
||||||
|
|
||||||
if (errc)
|
|
||||||
return Err(DracError(DracErrorCode::IoError, "Failed to get temp directory: " + errc.message()));
|
|
||||||
|
|
||||||
if (pmId.empty() || pmId.find_first_not_of(ALLOWED_PMID_CHARS) != String::npos)
|
|
||||||
return Err(DracError(DracErrorCode::ParseError, "Invalid package manager ID for cache path: " + pmId));
|
|
||||||
|
|
||||||
return cacheDir / (pmId + "_pkg_count_cache.beve");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ReadPkgCountCache(const String& pmId) -> Result<PkgCountCacheData, DracError> {
|
|
||||||
Result<fs::path, DracError> cachePathResult = GetPkgCountCachePath(pmId);
|
|
||||||
|
|
||||||
if (!cachePathResult)
|
|
||||||
return Err(cachePathResult.error());
|
|
||||||
|
|
||||||
const fs::path& cachePath = *cachePathResult;
|
|
||||||
|
|
||||||
if (!fs::exists(cachePath))
|
|
||||||
return Err(DracError(DracErrorCode::NotFound, "Cache file not found: " + cachePath.string()));
|
|
||||||
|
|
||||||
std::ifstream ifs(cachePath, std::ios::binary);
|
|
||||||
if (!ifs.is_open())
|
|
||||||
return Err(DracError(DracErrorCode::IoError, "Failed to open cache file for reading: " + cachePath.string()));
|
|
||||||
|
|
||||||
try {
|
|
||||||
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
|
||||||
ifs.close();
|
|
||||||
|
|
||||||
if (content.empty())
|
|
||||||
return Err(DracError(DracErrorCode::ParseError, "BEVE cache file is empty: " + cachePath.string()));
|
|
||||||
|
|
||||||
PkgCountCacheData result;
|
|
||||||
const glz::context ctx {};
|
|
||||||
|
|
||||||
if (glz::error_ctx glazeResult = glz::read_beve(result, content); glazeResult.ec != glz::error_code::none)
|
|
||||||
return Err(DracError(
|
|
||||||
DracErrorCode::ParseError,
|
|
||||||
std::format(
|
|
||||||
"BEVE parse error reading cache (code {}): {}", static_cast<int>(glazeResult.ec), cachePath.string()
|
|
||||||
)
|
|
||||||
));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (const std::ios_base::failure& e) {
|
|
||||||
return Err(DracError(
|
|
||||||
DracErrorCode::IoError, std::format("Filesystem error reading cache file {}: {}", cachePath.string(), e.what())
|
|
||||||
));
|
|
||||||
} catch (const Exception& e) {
|
|
||||||
return Err(DracError(DracErrorCode::InternalError, std::format("Error reading package count cache: {}", e.what()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn WritePkgCountCache(const String& pmId, const PkgCountCacheData& data) -> Result<void, DracError> {
|
|
||||||
using util::types::isize;
|
|
||||||
|
|
||||||
Result<fs::path, DracError> cachePathResult = GetPkgCountCachePath(pmId);
|
|
||||||
|
|
||||||
if (!cachePathResult)
|
|
||||||
return Err(cachePathResult.error());
|
|
||||||
|
|
||||||
const fs::path& cachePath = *cachePathResult;
|
|
||||||
fs::path tempPath = cachePath;
|
|
||||||
tempPath += ".tmp";
|
|
||||||
|
|
||||||
try {
|
|
||||||
String binaryBuffer;
|
|
||||||
|
|
||||||
PkgCountCacheData mutableData = data;
|
|
||||||
|
|
||||||
if (glz::error_ctx glazeErr = glz::write_beve(mutableData, binaryBuffer); glazeErr)
|
|
||||||
return Err(DracError(
|
|
||||||
DracErrorCode::ParseError,
|
|
||||||
std::format("BEVE serialization error writing cache (code {})", static_cast<int>(glazeErr.ec))
|
|
||||||
));
|
|
||||||
|
|
||||||
{
|
|
||||||
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
|
|
||||||
if (!ofs.is_open())
|
|
||||||
return Err(DracError(DracErrorCode::IoError, "Failed to open temp cache file: " + tempPath.string()));
|
|
||||||
|
|
||||||
ofs.write(binaryBuffer.data(), static_cast<isize>(binaryBuffer.size()));
|
|
||||||
|
|
||||||
if (!ofs) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err(DracError(DracErrorCode::IoError, "Failed to write to temp cache file: " + tempPath.string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::error_code errc;
|
|
||||||
fs::rename(tempPath, cachePath, errc);
|
|
||||||
if (errc) {
|
|
||||||
fs::remove(tempPath, errc);
|
|
||||||
return Err(DracError(
|
|
||||||
DracErrorCode::IoError,
|
|
||||||
std::format("Failed to replace cache file '{}': {}", cachePath.string(), errc.message())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
} catch (const std::ios_base::failure& e) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err(DracError(
|
|
||||||
DracErrorCode::IoError, std::format("Filesystem error writing cache file {}: {}", tempPath.string(), e.what())
|
|
||||||
));
|
|
||||||
} catch (const Exception& e) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err(DracError(DracErrorCode::InternalError, std::format("Error writing package count cache: {}", e.what()))
|
|
||||||
);
|
|
||||||
} catch (...) {
|
|
||||||
std::error_code removeEc;
|
|
||||||
fs::remove(tempPath, removeEc);
|
|
||||||
return Err(DracError(DracErrorCode::Other, std::format("Unknown error writing cache file: {}", tempPath.string()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> {
|
fn GetPackageCountInternalDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> {
|
||||||
const auto& [pmId, dbPath, countQuery] = pmInfo;
|
const auto& [pmId, dbPath, countQuery] = pmInfo;
|
||||||
|
|
||||||
if (Result<PkgCountCacheData, DracError> cachedDataResult = ReadPkgCountCache(pmId)) {
|
if (Result<PkgCountCacheData, DracError> cachedDataResult = ReadCache<PkgCountCacheData>(pmId)) {
|
||||||
const auto& [count, timestamp] = *cachedDataResult;
|
const auto& [count, timestamp] = *cachedDataResult;
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
const std::filesystem::file_time_type dbModTime = fs::last_write_time(dbPath, errc);
|
const std::filesystem::file_time_type dbModTime = fs::last_write_time(dbPath, errc);
|
||||||
|
@ -233,12 +108,13 @@ namespace {
|
||||||
const i64 nowEpochSeconds = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
const i64 nowEpochSeconds = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
||||||
const PkgCountCacheData dataToCache = { .count = count, .timestampEpochSeconds = nowEpochSeconds };
|
const PkgCountCacheData dataToCache = { .count = count, .timestampEpochSeconds = nowEpochSeconds };
|
||||||
|
|
||||||
if (Result<void, DracError> writeResult = WritePkgCountCache(pmId, dataToCache); !writeResult)
|
if (Result<void, DracError> writeResult = WriteCache(pmId, dataToCache); !writeResult)
|
||||||
error_at(writeResult.error());
|
error_at(writeResult.error());
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
fn GetNixPackageCount() -> Result<u64, DracError> {
|
fn GetNixPackageCount() -> Result<u64, DracError> {
|
||||||
debug_log("Attempting to get Nix package count.");
|
debug_log("Attempting to get Nix package count.");
|
||||||
|
|
||||||
|
@ -259,6 +135,7 @@ namespace {
|
||||||
|
|
||||||
return GetPackageCountInternalDb(nixInfo);
|
return GetPackageCountInternalDb(nixInfo);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
fn GetCargoPackageCount() -> Result<u64, DracError> {
|
fn GetCargoPackageCount() -> Result<u64, DracError> {
|
||||||
using util::helpers::GetEnv;
|
using util::helpers::GetEnv;
|
||||||
|
@ -288,12 +165,15 @@ namespace {
|
||||||
namespace os::shared {
|
namespace os::shared {
|
||||||
fn GetPackageCount() -> Result<u64, DracError> {
|
fn GetPackageCount() -> Result<u64, DracError> {
|
||||||
u64 count = 0;
|
u64 count = 0;
|
||||||
if (Result<u64, DracError> pkgCount = GetNixPackageCount())
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
if (const Result<u64, DracError> pkgCount = GetNixPackageCount())
|
||||||
count += *pkgCount;
|
count += *pkgCount;
|
||||||
else
|
else
|
||||||
debug_at(pkgCount.error());
|
debug_at(pkgCount.error());
|
||||||
|
#endif
|
||||||
|
|
||||||
if (Result<u64, DracError> pkgCount = GetCargoPackageCount())
|
if (const Result<u64, DracError> pkgCount = GetCargoPackageCount())
|
||||||
count += *pkgCount;
|
count += *pkgCount;
|
||||||
else
|
else
|
||||||
debug_at(pkgCount.error());
|
debug_at(pkgCount.error());
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
#include <winrt/base.h>
|
#include <winrt/base.h>
|
||||||
#include <winrt/impl/Windows.Media.Control.2.h>
|
#include <winrt/impl/Windows.Media.Control.2.h>
|
||||||
|
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/helpers.hpp"
|
#include "src/util/helpers.hpp"
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/util/logging.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
#include "os.hpp"
|
#include "os.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
216
src/util/cache.hpp
Normal file
216
src/util/cache.hpp
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem> // std::filesystem
|
||||||
|
#include <fstream> // std::{ifstream, ofstream}
|
||||||
|
#include <glaze/beve/read.hpp> // glz::read_beve
|
||||||
|
#include <glaze/beve/write.hpp> // glz::write_beve
|
||||||
|
#include <glaze/core/context.hpp> // glz::{context, error_code, error_ctx}
|
||||||
|
#include <iterator> // std::istreambuf_iterator
|
||||||
|
#include <string> // std::string
|
||||||
|
#include <system_error> // std::error_code
|
||||||
|
#include <type_traits> // std::decay_t
|
||||||
|
|
||||||
|
#include "src/util/defs.hpp"
|
||||||
|
#include "src/util/error.hpp"
|
||||||
|
#include "src/util/logging.hpp"
|
||||||
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
|
namespace util::cache {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using error::DracError, error::DracErrorCode;
|
||||||
|
using types::Err, types::Exception, types::Result, types::String, types::isize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the full path for a cache file based on a unique key.
|
||||||
|
* @param cache_key A unique identifier for the cache (e.g., "weather", "pkg_count_pacman").
|
||||||
|
* Should ideally only contain filesystem-safe characters.
|
||||||
|
* @return Result containing the filesystem path on success, or a DracError on failure.
|
||||||
|
*/
|
||||||
|
inline fn GetCachePath(const String& cache_key) -> Result<fs::path, DracError> {
|
||||||
|
if (cache_key.empty())
|
||||||
|
return Err(DracError(DracErrorCode::InvalidArgument, "Cache key cannot be empty."));
|
||||||
|
|
||||||
|
// Basic check for potentially problematic characters in the key for filename safety.
|
||||||
|
// You might want to expand this or implement more robust sanitization if needed.
|
||||||
|
if (cache_key.find_first_of("/\\:*?\"<>|") != String::npos)
|
||||||
|
return Err(
|
||||||
|
DracError(DracErrorCode::InvalidArgument, std::format("Cache key '{}' contains invalid characters.", cache_key))
|
||||||
|
);
|
||||||
|
|
||||||
|
std::error_code errc;
|
||||||
|
const fs::path cacheDir = fs::temp_directory_path(errc);
|
||||||
|
|
||||||
|
if (errc)
|
||||||
|
return Err(DracError(DracErrorCode::IoError, "Failed to get system temporary directory: " + errc.message()));
|
||||||
|
|
||||||
|
// Use a consistent naming scheme
|
||||||
|
return cacheDir / (cache_key + "_cache.beve");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads and deserializes data from a BEVE cache file.
|
||||||
|
* @tparam T The type of the object to deserialize from the cache. Must be Glaze-compatible.
|
||||||
|
* @param cache_key The unique identifier for the cache.
|
||||||
|
* @return Result containing the deserialized object of type T on success, or a DracError on failure.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
fn ReadCache(const String& cache_key) -> Result<T, DracError> {
|
||||||
|
Result<fs::path, DracError> cachePathResult = GetCachePath(cache_key);
|
||||||
|
if (!cachePathResult)
|
||||||
|
return Err(cachePathResult.error());
|
||||||
|
|
||||||
|
const fs::path& cachePath = *cachePathResult;
|
||||||
|
|
||||||
|
if (std::error_code exists_ec; !fs::exists(cachePath, exists_ec) || exists_ec) {
|
||||||
|
if (exists_ec) {
|
||||||
|
// Log if there was an error checking existence, but still return NotFound
|
||||||
|
warn_log("Error checking existence of cache file '{}': {}", cachePath.string(), exists_ec.message());
|
||||||
|
}
|
||||||
|
return Err(DracError(DracErrorCode::NotFound, "Cache file not found: " + cachePath.string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream ifs(cachePath, std::ios::binary);
|
||||||
|
if (!ifs.is_open())
|
||||||
|
return Err(DracError(DracErrorCode::IoError, "Failed to open cache file for reading: " + cachePath.string()));
|
||||||
|
|
||||||
|
debug_log("Reading cache for key '{}' from: {}", cache_key, cachePath.string());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read the entire file content
|
||||||
|
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||||
|
ifs.close(); // Close the file as soon as content is read
|
||||||
|
|
||||||
|
if (content.empty())
|
||||||
|
return Err(DracError(DracErrorCode::ParseError, "BEVE cache file is empty: " + cachePath.string()));
|
||||||
|
|
||||||
|
// Ensure T is default constructible for Glaze
|
||||||
|
static_assert(std::is_default_constructible_v<T>, "Cache type T must be default constructible for Glaze.");
|
||||||
|
T result {};
|
||||||
|
|
||||||
|
if (glz::error_ctx glazeErr = glz::read_beve(result, content); glazeErr.ec != glz::error_code::none) {
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::ParseError,
|
||||||
|
std::format(
|
||||||
|
"BEVE parse error reading cache '{}' (code {}): {}",
|
||||||
|
cachePath.string(),
|
||||||
|
static_cast<int>(glazeErr.ec),
|
||||||
|
glz::format_error(glazeErr, content)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log("Successfully read cache for key '{}'.", cache_key);
|
||||||
|
return result;
|
||||||
|
} catch (const std::ios_base::failure& e) {
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::IoError, std::format("Filesystem error reading cache file {}: {}", cachePath.string(), e.what())
|
||||||
|
));
|
||||||
|
} catch (const Exception& e) {
|
||||||
|
// Catching std::exception or a project-specific base exception
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::InternalError,
|
||||||
|
std::format("Standard exception reading cache file {}: {}", cachePath.string(), e.what())
|
||||||
|
));
|
||||||
|
} catch (...) {
|
||||||
|
return Err(DracError(DracErrorCode::Other, "Unknown error reading cache file: " + cachePath.string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Serializes and writes data to a BEVE cache file safely.
|
||||||
|
* @tparam T The type of the object to serialize. Must be Glaze-compatible.
|
||||||
|
* @param cache_key The unique identifier for the cache.
|
||||||
|
* @param data The data object of type T to write to the cache.
|
||||||
|
* @return Result containing void on success, or a DracError on failure.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
fn WriteCache(const String& cache_key, const T& data) -> Result<void, DracError> {
|
||||||
|
Result<fs::path, DracError> cachePathResult = GetCachePath(cache_key);
|
||||||
|
if (!cachePathResult)
|
||||||
|
return Err(cachePathResult.error());
|
||||||
|
|
||||||
|
const fs::path& cachePath = *cachePathResult;
|
||||||
|
fs::path tempPath = cachePath;
|
||||||
|
tempPath += ".tmp"; // Use a temporary file for atomic write
|
||||||
|
|
||||||
|
debug_log("Writing cache for key '{}' to: {}", cache_key, cachePath.string());
|
||||||
|
|
||||||
|
try {
|
||||||
|
String binaryBuffer;
|
||||||
|
|
||||||
|
// Use Glaze to serialize
|
||||||
|
// Need to decay T in case it's a reference type from the caller
|
||||||
|
using DecayedT = std::decay_t<T>;
|
||||||
|
DecayedT dataToSerialize = data; // Make a copy if needed for non-const Glaze operations
|
||||||
|
|
||||||
|
if (glz::error_ctx glazeErr = glz::write_beve(dataToSerialize, binaryBuffer); glazeErr) {
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::ParseError,
|
||||||
|
std::format(
|
||||||
|
"BEVE serialization error writing cache for key '{}' (code {}): {}",
|
||||||
|
cache_key,
|
||||||
|
static_cast<int>(glazeErr.ec),
|
||||||
|
glz::format_error(glazeErr, binaryBuffer)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scope for ofstream to ensure it's closed before rename
|
||||||
|
{
|
||||||
|
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!ofs.is_open())
|
||||||
|
return Err(DracError(DracErrorCode::IoError, "Failed to open temporary cache file: " + tempPath.string()));
|
||||||
|
|
||||||
|
ofs.write(binaryBuffer.data(), static_cast<isize>(binaryBuffer.size()));
|
||||||
|
|
||||||
|
if (!ofs) {
|
||||||
|
// Attempt cleanup before returning error
|
||||||
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc); // Ignore error on cleanup attempt
|
||||||
|
return Err(DracError(DracErrorCode::IoError, "Failed to write to temporary cache file: " + tempPath.string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// ofstream automatically closed here
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to atomically replace the old cache file
|
||||||
|
std::error_code rename_ec;
|
||||||
|
fs::rename(tempPath, cachePath, rename_ec);
|
||||||
|
if (rename_ec) {
|
||||||
|
// If rename failed, attempt to clean up the temp file
|
||||||
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc); // Ignore error on cleanup attempt
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::IoError,
|
||||||
|
std::format(
|
||||||
|
"Failed to replace cache file '{}' with temporary file '{}': {}",
|
||||||
|
cachePath.string(),
|
||||||
|
tempPath.string(),
|
||||||
|
rename_ec.message()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log("Successfully wrote cache for key '{}'.", cache_key);
|
||||||
|
return {}; // Success
|
||||||
|
} catch (const std::ios_base::failure& e) {
|
||||||
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc); // Cleanup attempt
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::IoError, std::format("Filesystem error writing cache file {}: {}", tempPath.string(), e.what())
|
||||||
|
));
|
||||||
|
} catch (const Exception& e) {
|
||||||
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc); // Cleanup attempt
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::InternalError,
|
||||||
|
std::format("Standard exception writing cache file {}: {}", tempPath.string(), e.what())
|
||||||
|
));
|
||||||
|
} catch (...) {
|
||||||
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc); // Cleanup attempt
|
||||||
|
return Err(DracError(DracErrorCode::Other, "Unknown error writing cache file: " + tempPath.string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util::cache
|
|
@ -9,7 +9,7 @@
|
||||||
#include <winrt/base.h> // winrt::hresult_error
|
#include <winrt/base.h> // winrt::hresult_error
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
namespace util::error {
|
namespace util::error {
|
||||||
using types::u8, types::i32, types::String, types::StringView, types::Exception;
|
using types::u8, types::i32, types::String, types::StringView, types::Exception;
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
namespace util::helpers {
|
namespace util::helpers {
|
||||||
using error::DracError, error::DracErrorCode;
|
using error::DracError, error::DracErrorCode;
|
|
@ -1,9 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <chrono> // std::chrono::{days, floor, seconds, system_clock}
|
#include <chrono> // std::chrono::{days, floor, seconds, system_clock}
|
||||||
#include <ctime> // For time_t, tm, localtime_s, localtime_r, strftime (needed for cross-platform local time)
|
#include <filesystem> // std::filesystem::path
|
||||||
#include <filesystem> // std::filesystem::path
|
#include <format> // std::format
|
||||||
#include <format> // std::format
|
|
||||||
#include <ftxui/screen/color.hpp> // ftxui::Color
|
#include <ftxui/screen/color.hpp> // ftxui::Color
|
||||||
#include <print> // std::print
|
#include <print> // std::print
|
||||||
#include <utility> // std::forward
|
#include <utility> // std::forward
|
||||||
|
@ -12,9 +11,9 @@
|
||||||
#include <source_location> // std::source_location
|
#include <source_location> // std::source_location
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
namespace util::logging {
|
namespace util::logging {
|
||||||
using types::usize, types::u8, types::i32, types::i64, types::CStr, types::String, types::StringView, types::Array,
|
using types::usize, types::u8, types::i32, types::i64, types::CStr, types::String, types::StringView, types::Array,
|
||||||
|
@ -74,10 +73,8 @@ namespace util::logging {
|
||||||
* @param color The FTXUI color
|
* @param color The FTXUI color
|
||||||
* @return Styled string with ANSI codes
|
* @return Styled string with ANSI codes
|
||||||
*/
|
*/
|
||||||
inline fn Colorize(StringView text, const ftxui::Color::Palette16& color) -> String {
|
inline fn Colorize(const StringView text, const ftxui::Color::Palette16& color) -> String {
|
||||||
std::ostringstream oss;
|
return std::format("{}{}{}", LogLevelConst::COLOR_CODE_LITERALS.at(color), text, LogLevelConst::RESET_CODE);
|
||||||
oss << LogLevelConst::COLOR_CODE_LITERALS.at(static_cast<i32>(color)) << text << LogLevelConst::RESET_CODE;
|
|
||||||
return oss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,7 +83,7 @@ namespace util::logging {
|
||||||
* @return Bold text
|
* @return Bold text
|
||||||
*/
|
*/
|
||||||
inline fn Bold(const StringView text) -> String {
|
inline fn Bold(const StringView text) -> String {
|
||||||
return String(LogLevelConst::BOLD_START) + String(text) + String(LogLevelConst::BOLD_END);
|
return std::format("{}{}{}", LogLevelConst::BOLD_START, text, LogLevelConst::BOLD_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,7 +92,7 @@ namespace util::logging {
|
||||||
* @return Italic text
|
* @return Italic text
|
||||||
*/
|
*/
|
||||||
inline fn Italic(const StringView text) -> String {
|
inline fn Italic(const StringView text) -> String {
|
||||||
return String(LogLevelConst::ITALIC_START) + String(text) + String(LogLevelConst::ITALIC_END);
|
return std::format("{}{}{}", LogLevelConst::ITALIC_START, text, LogLevelConst::ITALIC_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,11 +140,14 @@ namespace util::logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once CppDoxygenUnresolvedReference
|
||||||
/**
|
/**
|
||||||
* @brief Logs a message with the specified log level, source location, and format string.
|
* @brief Logs a message with the specified log level, source location, and format string.
|
||||||
* @tparam Args Parameter pack for format arguments.
|
* @tparam Args Parameter pack for format arguments.
|
||||||
* @param level The log level (DEBUG, INFO, WARN, ERROR).
|
* @param level The log level (DEBUG, INFO, WARN, ERROR).
|
||||||
|
* \ifnot NDEBUG
|
||||||
* @param loc The source location of the log message (only in Debug builds).
|
* @param loc The source location of the log message (only in Debug builds).
|
||||||
|
* \endif
|
||||||
* @param fmt The format string.
|
* @param fmt The format string.
|
||||||
* @param args The arguments for the format string.
|
* @param args The arguments for the format string.
|
||||||
*/
|
*/
|
||||||
|
@ -163,8 +163,6 @@ namespace util::logging {
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using std::filesystem::path;
|
using std::filesystem::path;
|
||||||
|
|
||||||
using Buffer = Array<char, 512>;
|
|
||||||
|
|
||||||
const auto nowTp = system_clock::now();
|
const auto nowTp = system_clock::now();
|
||||||
const std::time_t nowTt = system_clock::to_time_t(nowTp);
|
const std::time_t nowTt = system_clock::to_time_t(nowTp);
|
||||||
std::tm localTm;
|
std::tm localTm;
|
||||||
|
@ -176,7 +174,7 @@ namespace util::logging {
|
||||||
#else
|
#else
|
||||||
if (localtime_r(&nowTt, &localTm) != nullptr) {
|
if (localtime_r(&nowTt, &localTm) != nullptr) {
|
||||||
#endif
|
#endif
|
||||||
Array<char, 64> timeBuffer;
|
Array<char, 64> timeBuffer {};
|
||||||
|
|
||||||
if (std::strftime(timeBuffer.data(), sizeof(timeBuffer), LogLevelConst::TIMESTAMP_FORMAT, &localTm) > 0)
|
if (std::strftime(timeBuffer.data(), sizeof(timeBuffer), LogLevelConst::TIMESTAMP_FORMAT, &localTm) > 0)
|
||||||
timestamp = timeBuffer.data();
|
timestamp = timeBuffer.data();
|
||||||
|
@ -187,7 +185,7 @@ namespace util::logging {
|
||||||
|
|
||||||
const String message = std::format(fmt, std::forward<Args>(args)...);
|
const String message = std::format(fmt, std::forward<Args>(args)...);
|
||||||
|
|
||||||
Buffer buffer {};
|
Array<char, 128> buffer {};
|
||||||
|
|
||||||
// Use the locally formatted timestamp string here
|
// Use the locally formatted timestamp string here
|
||||||
auto* iter = std::format_to(
|
auto* iter = std::format_to(
|
|
@ -9,9 +9,9 @@
|
||||||
#include <format> // std::format
|
#include <format> // std::format
|
||||||
#include <type_traits> // std::is_convertible_v
|
#include <type_traits> // std::is_convertible_v
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
namespace dbus {
|
namespace dbus {
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include <wayland-client.h> // Wayland client library
|
#include <wayland-client.h> // Wayland client library
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
struct wl_display;
|
struct wl_display;
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#include <xcb/xcb.h> // XCB library
|
#include <xcb/xcb.h> // XCB library
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
namespace xcb {
|
namespace xcb {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue