fix macos
This commit is contained in:
parent
4f6f5e6724
commit
44c2c99468
7 changed files with 303 additions and 248 deletions
|
@ -33,194 +33,6 @@ namespace {
|
|||
using namespace std::chrono;
|
||||
using os::linux::PkgCountCacheData, os::linux::PackageManagerInfo;
|
||||
|
||||
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> {
|
||||
const auto& [pmId, dbPath, countQuery] = pmInfo;
|
||||
|
||||
if (Result<PkgCountCacheData, DracError> cachedDataResult = ReadPkgCountCache(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 = WritePkgCountCache(pmId, dataToCache); !writeResult)
|
||||
error_at(writeResult.error());
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
fn GetPackageCountInternalDir(
|
||||
const String& pmId,
|
||||
const fs::path& dirPath,
|
||||
|
@ -338,27 +150,6 @@ namespace os::linux {
|
|||
return *countResult - 1;
|
||||
}
|
||||
|
||||
fn GetNixPackageCount() -> Result<u64, DracError> {
|
||||
debug_log("Attempting to get Nix package count.");
|
||||
|
||||
const PackageManagerInfo nixInfo = {
|
||||
.id = "nix",
|
||||
.dbPath = "/nix/var/nix/db/db.sqlite",
|
||||
.countQuery = "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL",
|
||||
};
|
||||
|
||||
if (std::error_code errc; !fs::exists(nixInfo.dbPath, errc)) {
|
||||
if (errc) {
|
||||
warn_log("Filesystem error checking for Nix DB at '{}': {}", nixInfo.dbPath.string(), errc.message());
|
||||
return Err(DracError(DracErrorCode::IoError, "Filesystem error checking Nix DB: " + errc.message()));
|
||||
}
|
||||
|
||||
return Err(DracError(DracErrorCode::ApiUnavailable, "Nix db not found: " + nixInfo.dbPath.string()));
|
||||
}
|
||||
|
||||
return GetPackageCountInternalDb(nixInfo);
|
||||
}
|
||||
|
||||
fn GetPacmanPackageCount() -> Result<u64, DracError> {
|
||||
return GetPackageCountInternalDir(
|
||||
"Pacman", fs::current_path().root_path() / "var" / "lib" / "pacman" / "local", "", true
|
||||
|
|
|
@ -13,41 +13,46 @@
|
|||
// clang-format on
|
||||
|
||||
using namespace util::types;
|
||||
using util::error::DracError, util::error::DracErrorCode;
|
||||
|
||||
fn os::GetMemInfo() -> Result<u64, DraconisError> {
|
||||
fn os::GetMemInfo() -> Result<u64, DracError> {
|
||||
u64 mem = 0;
|
||||
usize size = sizeof(mem);
|
||||
|
||||
if (sysctlbyname("hw.memsize", &mem, &size, nullptr, 0) == -1)
|
||||
return Err(DraconisError::withErrno("Failed to get memory info"));
|
||||
return Err(DracError::withErrno("Failed to get memory info"));
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
fn os::GetNowPlaying() -> Result<MediaInfo, DraconisError> { return GetCurrentPlayingInfo(); }
|
||||
fn os::GetNowPlaying() -> Result<MediaInfo, DracError> { return GetCurrentPlayingInfo(); }
|
||||
|
||||
fn os::GetOSVersion() -> Result<String, DraconisError> { return GetMacOSVersion(); }
|
||||
fn os::GetOSVersion() -> Result<String, DracError> { return GetMacOSVersion(); }
|
||||
|
||||
fn os::GetDesktopEnvironment() -> Option<String> { return None; }
|
||||
fn os::GetDesktopEnvironment() -> Result<String, DracError> {
|
||||
return Err(DracError(DracErrorCode::Other, "Not implemented on macOS"));
|
||||
}
|
||||
|
||||
fn os::GetWindowManager() -> Option<String> { return None; }
|
||||
fn os::GetWindowManager() -> Result<String, DracError> {
|
||||
return Err(DracError(DracErrorCode::Other, "Not implemented on macOS"));
|
||||
}
|
||||
|
||||
fn os::GetKernelVersion() -> Result<String, DraconisError> {
|
||||
fn os::GetKernelVersion() -> Result<String, DracError> {
|
||||
std::array<char, 256> kernelVersion {};
|
||||
usize kernelVersionLen = sizeof(kernelVersion);
|
||||
|
||||
if (sysctlbyname("kern.osrelease", kernelVersion.data(), &kernelVersionLen, nullptr, 0) == -1)
|
||||
return Err(DraconisError::withErrno("Failed to get kernel version"));
|
||||
return Err(DracError::withErrno("Failed to get kernel version"));
|
||||
|
||||
return kernelVersion.data();
|
||||
}
|
||||
|
||||
fn os::GetHost() -> Result<String, DraconisError> {
|
||||
fn os::GetHost() -> Result<String, DracError> {
|
||||
std::array<char, 256> hwModel {};
|
||||
size_t hwModelLen = sizeof(hwModel);
|
||||
|
||||
if (sysctlbyname("hw.model", hwModel.data(), &hwModelLen, nullptr, 0) == -1)
|
||||
return Err(DraconisError::withErrno("Failed to get host info"));
|
||||
return Err(DracError::withErrno("Failed to get host info"));
|
||||
|
||||
// taken from https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/host/host_mac.c
|
||||
// shortened a lot of the entries to remove unnecessary info
|
||||
|
@ -202,27 +207,25 @@ fn os::GetHost() -> Result<String, DraconisError> {
|
|||
|
||||
const auto iter = modelNameByHwModel.find(hwModel.data());
|
||||
if (iter == modelNameByHwModel.end())
|
||||
return Err(DraconisError::withErrno("Failed to get host info"));
|
||||
return Err(DracError::withErrno("Failed to get host info"));
|
||||
|
||||
return String(iter->second);
|
||||
}
|
||||
|
||||
fn os::GetDiskUsage() -> Result<DiskSpace, DraconisError> {
|
||||
fn os::GetDiskUsage() -> Result<DiskSpace, DracError> {
|
||||
struct statvfs vfs;
|
||||
|
||||
if (statvfs("/", &vfs) != 0)
|
||||
return Err(DraconisError::withErrno("Failed to get disk usage"));
|
||||
return Err(DracError::withErrno("Failed to get disk usage"));
|
||||
|
||||
return DiskSpace { .used_bytes = (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize,
|
||||
.total_bytes = vfs.f_blocks * vfs.f_frsize };
|
||||
}
|
||||
|
||||
fn os::GetPackageCount() -> Result<u64, DraconisError> {
|
||||
using util::error::DraconisErrorCode;
|
||||
fn os::GetPackageCount() -> Result<u64, DracError> { return shared::GetPackageCount(); }
|
||||
|
||||
return Err(DraconisError(DraconisErrorCode::Other, "Not implemented on macOS"));
|
||||
fn os::GetShell() -> Result<String, DracError> {
|
||||
return Err(DracError(DracErrorCode::Other, "Not implemented on macOS"));
|
||||
}
|
||||
|
||||
fn os::GetShell() -> Option<String> { return None; }
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "src/core/util/error.hpp"
|
||||
#include "src/core/util/types.hpp"
|
||||
// clang-format on
|
||||
using util::error::DraconisError;
|
||||
using util::error::DracError;
|
||||
using util::types::MediaInfo, util::types::String, util::types::Result;
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
@ -15,12 +15,12 @@ using util::types::MediaInfo, util::types::String, util::types::Result;
|
|||
|
||||
@interface Bridge : NSObject
|
||||
+ (void)fetchCurrentPlayingMetadata:(void (^)(Result<NSDictionary*, const char*>))completion;
|
||||
+ (Result<String, DraconisError>)macOSVersion;
|
||||
+ (Result<String, DracError>)macOSVersion;
|
||||
@end
|
||||
#else
|
||||
extern "C++" {
|
||||
fn GetCurrentPlayingInfo() -> Result<MediaInfo, DraconisError>;
|
||||
fn GetMacOSVersion() -> Result<String, DraconisError>;
|
||||
fn GetCurrentPlayingInfo() -> Result<MediaInfo, DracError>;
|
||||
fn GetMacOSVersion() -> Result<String, DracError>;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
#include "src/core/util/error.hpp"
|
||||
// clang-format on
|
||||
|
||||
using util::error::DraconisErrorCode;
|
||||
using util::types::Err, util::types::Option;
|
||||
using util::error::DracErrorCode;
|
||||
using util::types::Err, util::types::Option, util::types::None;
|
||||
|
||||
using MRMediaRemoteGetNowPlayingInfoFunction =
|
||||
void (*)(dispatch_queue_t queue, void (^handler)(NSDictionary* information));
|
||||
|
@ -66,7 +66,7 @@ using MRMediaRemoteGetNowPlayingInfoFunction =
|
|||
);
|
||||
}
|
||||
|
||||
+ (Result<String, DraconisError>)macOSVersion {
|
||||
+ (Result<String, DracError>)macOSVersion {
|
||||
NSProcessInfo* processInfo = [NSProcessInfo processInfo];
|
||||
NSOperatingSystemVersion osVersion = [processInfo operatingSystemVersion];
|
||||
|
||||
|
@ -91,21 +91,21 @@ using MRMediaRemoteGetNowPlayingInfoFunction =
|
|||
|
||||
extern "C++" {
|
||||
// NOLINTBEGIN(misc-use-internal-linkage)
|
||||
fn GetCurrentPlayingInfo() -> Result<MediaInfo, DraconisError> {
|
||||
__block Result<MediaInfo, DraconisError> result;
|
||||
fn GetCurrentPlayingInfo() -> Result<MediaInfo, DracError> {
|
||||
__block Result<MediaInfo, DracError> result;
|
||||
|
||||
const dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
|
||||
[Bridge fetchCurrentPlayingMetadata:^(std::expected<NSDictionary*, const char*> metadataResult) {
|
||||
if (!metadataResult) {
|
||||
result = Err(DraconisError(DraconisErrorCode::InternalError, metadataResult.error()));
|
||||
result = Err(DracError(DracErrorCode::InternalError, metadataResult.error()));
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
return;
|
||||
}
|
||||
|
||||
const NSDictionary* const metadata = *metadataResult;
|
||||
if (!metadata) {
|
||||
result = Err(DraconisError(DraconisErrorCode::InternalError, "No metadata"));
|
||||
result = Err(DracError(DracErrorCode::InternalError, "No metadata"));
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
return;
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ extern "C++" {
|
|||
return result;
|
||||
}
|
||||
|
||||
fn GetMacOSVersion() -> Result<String, DraconisError> { return [Bridge macOSVersion]; }
|
||||
fn GetMacOSVersion() -> Result<String, DracError> { return [Bridge macOSVersion]; }
|
||||
// NOLINTEND(misc-use-internal-linkage)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
#include <filesystem>
|
||||
#include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY}
|
||||
#include <SQLiteCpp/Exception.h> // SQLite::Exception
|
||||
#include <SQLiteCpp/Statement.h> // SQLite::Statement
|
||||
#include <chrono> // std::chrono
|
||||
#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/common.hpp> // glz::object
|
||||
#include <glaze/core/context.hpp> // glz::{context, error_code, error_ctx}
|
||||
#include <glaze/core/meta.hpp> // glz::detail::Object
|
||||
#include <ios> // std::ios::{binary, trunc}, std::ios_base
|
||||
#include <iterator> // std::istreambuf_iterator
|
||||
|
||||
#include "src/core/util/defs.hpp"
|
||||
#include "src/core/util/error.hpp"
|
||||
|
@ -8,13 +20,245 @@
|
|||
|
||||
#include "os.hpp"
|
||||
|
||||
namespace os::shared {
|
||||
using util::error::DracError, util::error::DracErrorCode;
|
||||
using util::types::u64, util::types::String, util::types::Result, util::types::Err;
|
||||
using util::error::DracError, util::error::DracErrorCode;
|
||||
using util::types::u64, util::types::i64, util::types::String, util::types::StringView, util::types::Result,
|
||||
util::types::Err, util::types::Exception;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
fn GetPackageCount() -> Result<u64, DracError> {
|
||||
namespace {
|
||||
using namespace std::chrono;
|
||||
|
||||
struct PackageManagerInfo {
|
||||
String id;
|
||||
fs::path dbPath;
|
||||
String countQuery;
|
||||
};
|
||||
|
||||
struct PkgCountCacheData {
|
||||
u64 count {};
|
||||
i64 timestampEpochSeconds {};
|
||||
|
||||
// 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)
|
||||
};
|
||||
|
||||
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> {
|
||||
const auto& [pmId, dbPath, countQuery] = pmInfo;
|
||||
|
||||
if (Result<PkgCountCacheData, DracError> cachedDataResult = ReadPkgCountCache(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 = WritePkgCountCache(pmId, dataToCache); !writeResult)
|
||||
error_at(writeResult.error());
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
fn GetNixPackageCount() -> Result<u64, DracError> {
|
||||
debug_log("Attempting to get Nix package count.");
|
||||
|
||||
const PackageManagerInfo nixInfo = {
|
||||
.id = "nix",
|
||||
.dbPath = "/nix/var/nix/db/db.sqlite",
|
||||
.countQuery = "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL",
|
||||
};
|
||||
|
||||
if (std::error_code errc; !fs::exists(nixInfo.dbPath, errc)) {
|
||||
if (errc) {
|
||||
warn_log("Filesystem error checking for Nix DB at '{}': {}", nixInfo.dbPath.string(), errc.message());
|
||||
return Err(DracError(DracErrorCode::IoError, "Filesystem error checking Nix DB: " + errc.message()));
|
||||
}
|
||||
|
||||
return Err(DracError(DracErrorCode::ApiUnavailable, "Nix db not found: " + nixInfo.dbPath.string()));
|
||||
}
|
||||
|
||||
return GetPackageCountInternalDb(nixInfo);
|
||||
}
|
||||
|
||||
fn GetCargoPackageCount() -> Result<u64, DracError> {
|
||||
using util::helpers::GetEnv;
|
||||
|
||||
fs::path cargoPath {};
|
||||
|
@ -35,6 +279,23 @@ namespace os::shared {
|
|||
|
||||
debug_log("Found {} packages in cargo directory: {}", count, cargoPath.string());
|
||||
|
||||
return count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace os::shared {
|
||||
fn GetPackageCount() -> Result<u64, DracError> {
|
||||
u64 count = 0;
|
||||
if (Result<u64, DracError> pkgCount = GetNixPackageCount())
|
||||
count += *pkgCount;
|
||||
else
|
||||
debug_at(pkgCount.error());
|
||||
|
||||
if (Result<u64, DracError> pkgCount = GetCargoPackageCount())
|
||||
count += *pkgCount;
|
||||
else
|
||||
debug_at(pkgCount.error());
|
||||
|
||||
return count;
|
||||
}
|
||||
} // namespace os::shared
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue