umm i think this is good
This commit is contained in:
parent
854f116f2d
commit
2be2613e12
3 changed files with 251 additions and 116 deletions
|
@ -473,6 +473,6 @@ fn os::GetDiskUsage() -> Result<DiskSpace, DraconisError> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn os::GetPackageCount() -> Result<u64, DraconisError> { return linux::GetNixPackageCount(); }
|
fn os::GetPackageCount() -> Result<u64, DraconisError> { return linux::GetTotalPackageCount(); }
|
||||||
|
|
||||||
#endif // __linux__
|
#endif // __linux__
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
#include <SQLiteCpp/SQLiteCpp.h>
|
#include <SQLiteCpp/SQLiteCpp.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <glaze/beve/read.hpp>
|
||||||
|
#include <glaze/beve/write.hpp>
|
||||||
#include <glaze/core/common.hpp>
|
#include <glaze/core/common.hpp>
|
||||||
#include <glaze/core/read.hpp>
|
#include <glaze/core/read.hpp>
|
||||||
#include <glaze/core/reflect.hpp>
|
#include <glaze/core/reflect.hpp>
|
||||||
#include <glaze/json/write.hpp>
|
|
||||||
|
|
||||||
#include "src/core/util/logging.hpp"
|
#include "src/core/util/logging.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/core/util/types.hpp"
|
||||||
|
@ -18,79 +19,80 @@ namespace {
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
struct NixPkgCacheData {
|
struct PkgCountCacheData {
|
||||||
u64 count {};
|
u64 count {};
|
||||||
system_clock::time_point timestamp;
|
i64 timestamp_epoch_seconds {};
|
||||||
|
|
||||||
// NOLINTBEGIN(readability-identifier-naming) - Needs to specifically use `glaze`
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
struct [[maybe_unused]] glaze {
|
struct [[maybe_unused]] glaze {
|
||||||
using T = NixPkgCacheData;
|
using T = PkgCountCacheData;
|
||||||
static constexpr auto value = glz::object("count", &T::count, "timestamp", [](auto& self) -> auto& {
|
static constexpr auto value = glz::object("count", &T::count, "timestamp", &T::timestamp_epoch_seconds);
|
||||||
thread_local auto epoch_seconds = duration_cast<seconds>(self.timestamp.time_since_epoch()).count();
|
|
||||||
return epoch_seconds;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
// NOLINTEND(readability-identifier-naming)
|
// NOLINTEND(readability-identifier-naming)
|
||||||
};
|
};
|
||||||
|
|
||||||
fn GetPkgCountCachePath() -> Result<fs::path, DraconisError> {
|
fn GetPkgCountCachePath(const String& pm_id) -> Result<fs::path, DraconisError> {
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
const fs::path cacheDir = fs::temp_directory_path(errc);
|
const fs::path cacheDir = fs::temp_directory_path(errc);
|
||||||
if (errc) {
|
|
||||||
|
if (errc)
|
||||||
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to get temp directory: " + errc.message()));
|
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to get temp directory: " + errc.message()));
|
||||||
}
|
|
||||||
return cacheDir / "nix_pkg_count_cache.json";
|
if (pm_id.empty() ||
|
||||||
|
pm_id.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") != String::npos)
|
||||||
|
return Err(DraconisError(DraconisErrorCode::ParseError, "Invalid package manager ID for cache path: " + pm_id));
|
||||||
|
|
||||||
|
return cacheDir / (pm_id + "_pkg_count_cache.beve");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ReadPkgCountCache() -> Result<NixPkgCacheData, DraconisError> {
|
fn ReadPkgCountCache(const String& pm_id) -> Result<PkgCountCacheData, DraconisError> {
|
||||||
auto cachePathResult = GetPkgCountCachePath();
|
Result<fs::path, DraconisError> cachePathResult = GetPkgCountCachePath(pm_id);
|
||||||
if (!cachePathResult) {
|
|
||||||
|
if (!cachePathResult)
|
||||||
return Err(cachePathResult.error());
|
return Err(cachePathResult.error());
|
||||||
}
|
|
||||||
const fs::path& cachePath = *cachePathResult;
|
const fs::path& cachePath = *cachePathResult;
|
||||||
|
|
||||||
if (!fs::exists(cachePath)) {
|
if (!fs::exists(cachePath))
|
||||||
return Err(DraconisError(DraconisErrorCode::NotFound, "Cache file not found: " + cachePath.string()));
|
return Err(DraconisError(DraconisErrorCode::NotFound, "Cache file not found: " + cachePath.string()));
|
||||||
}
|
|
||||||
|
|
||||||
std::ifstream ifs(cachePath, std::ios::binary);
|
std::ifstream ifs(cachePath, std::ios::binary);
|
||||||
if (!ifs.is_open()) {
|
if (!ifs.is_open())
|
||||||
return Err(
|
return Err(
|
||||||
DraconisError(DraconisErrorCode::IoError, "Failed to open cache file for reading: " + cachePath.string())
|
DraconisError(DraconisErrorCode::IoError, "Failed to open cache file for reading: " + cachePath.string())
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
debug_log("Reading Nix package count from cache file: {}", cachePath.string());
|
// Update log message
|
||||||
|
debug_log("Reading {} package count from cache file: {}", pm_id, cachePath.string());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Read the entire binary content
|
||||||
|
// Using std::string buffer is fine, it can hold arbitrary binary data
|
||||||
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
const String content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||||
NixPkgCacheData result;
|
ifs.close(); // Close the file stream after reading
|
||||||
glz::context ctx {};
|
|
||||||
|
|
||||||
if (auto glazeResult = glz::read<glz::opts { .error_on_unknown_keys = false }>(result, content, ctx);
|
if (content.empty()) {
|
||||||
glazeResult.ec != glz::error_code::none) {
|
return Err(DraconisError(DraconisErrorCode::ParseError, "BEVE cache file is empty: " + cachePath.string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
PkgCountCacheData result;
|
||||||
|
const glz::context ctx {};
|
||||||
|
|
||||||
|
if (auto glazeResult = glz::read_beve(result, content); glazeResult.ec != glz::error_code::none)
|
||||||
return Err(DraconisError(
|
return Err(DraconisError(
|
||||||
DraconisErrorCode::ParseError,
|
DraconisErrorCode::ParseError,
|
||||||
std::format("JSON parse error reading cache: {}", glz::format_error(glazeResult, content))
|
std::format(
|
||||||
|
"BEVE parse error reading cache (code {}): {}", static_cast<int>(glazeResult.ec), cachePath.string()
|
||||||
|
)
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
|
||||||
if (size_t tsPos = content.find("\"timestamp\""); tsPos != String::npos) {
|
debug_log("Successfully read {} package count from BEVE cache file.", pm_id);
|
||||||
size_t colonPos = content.find(':', tsPos);
|
|
||||||
if (size_t valueStart = content.find_first_of("0123456789", colonPos); valueStart != String::npos) {
|
|
||||||
long long timestampSeconds = 0;
|
|
||||||
char* endPtr = nullptr;
|
|
||||||
timestampSeconds = std::strtoll(content.c_str() + valueStart, &endPtr, 10);
|
|
||||||
result.timestamp = system_clock::time_point(seconds(timestampSeconds));
|
|
||||||
} else {
|
|
||||||
return Err(DraconisError(DraconisErrorCode::ParseError, "Could not parse timestamp value from cache JSON."));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(DraconisError(DraconisErrorCode::ParseError, "Timestamp field not found in cache JSON."));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_log("Successfully read package count from cache file.");
|
|
||||||
return result;
|
return result;
|
||||||
|
} catch (const std::ios_base::failure& e) {
|
||||||
|
return Err(DraconisError(
|
||||||
|
DraconisErrorCode::IoError,
|
||||||
|
std::format("Filesystem error reading cache file {}: {}", cachePath.string(), e.what())
|
||||||
|
));
|
||||||
} catch (const Exception& e) {
|
} catch (const Exception& e) {
|
||||||
return Err(
|
return Err(
|
||||||
DraconisError(DraconisErrorCode::InternalError, std::format("Error reading package count cache: {}", e.what()))
|
DraconisError(DraconisErrorCode::InternalError, std::format("Error reading package count cache: {}", e.what()))
|
||||||
|
@ -98,120 +100,242 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn WritePkgCountCache(const NixPkgCacheData& data) -> Result<void, DraconisError> {
|
// Modified to take pm_id and PkgCountCacheData
|
||||||
auto cachePathResult = GetPkgCountCachePath();
|
fn WritePkgCountCache(const String& pm_id, const PkgCountCacheData& data) -> Result<void, DraconisError> {
|
||||||
if (!cachePathResult) {
|
using util::types::isize;
|
||||||
|
|
||||||
|
Result<fs::path, DraconisError> cachePathResult = GetPkgCountCachePath(pm_id);
|
||||||
|
|
||||||
|
if (!cachePathResult)
|
||||||
return Err(cachePathResult.error());
|
return Err(cachePathResult.error());
|
||||||
}
|
|
||||||
const fs::path& cachePath = *cachePathResult;
|
const fs::path& cachePath = *cachePathResult;
|
||||||
fs::path tempPath = cachePath;
|
fs::path tempPath = cachePath;
|
||||||
tempPath += ".tmp";
|
tempPath += ".tmp";
|
||||||
|
|
||||||
debug_log("Writing Nix package count to cache file: {}", cachePath.string());
|
debug_log("Writing {} package count to BEVE cache file: {}", pm_id, cachePath.string());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
String binaryBuffer;
|
||||||
|
|
||||||
|
PkgCountCacheData mutableData = data;
|
||||||
|
|
||||||
|
if (auto glazeErr = glz::write_beve(mutableData, binaryBuffer); glazeErr) {
|
||||||
|
return Err(DraconisError(
|
||||||
|
DraconisErrorCode::ParseError,
|
||||||
|
std::format("BEVE serialization error writing cache (code {})", static_cast<int>(glazeErr.ec))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
|
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
|
||||||
if (!ofs.is_open()) {
|
if (!ofs.is_open()) {
|
||||||
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to open temp cache file: " + tempPath.string()));
|
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to open temp cache file: " + tempPath.string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String jsonStr;
|
ofs.write(binaryBuffer.data(), static_cast<isize>(binaryBuffer.size()));
|
||||||
|
|
||||||
NixPkgCacheData mutableData = data;
|
|
||||||
|
|
||||||
if (auto glazeErr = glz::write_json(mutableData, jsonStr); glazeErr.ec != glz::error_code::none) {
|
|
||||||
return Err(DraconisError(
|
|
||||||
DraconisErrorCode::ParseError,
|
|
||||||
std::format("JSON serialization error writing cache: {}", glz::format_error(glazeErr, jsonStr))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
ofs << jsonStr;
|
|
||||||
if (!ofs) {
|
if (!ofs) {
|
||||||
return Err(DraconisError(DraconisErrorCode::IoError, "Failed to write to temp cache file"));
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc);
|
||||||
|
return Err(
|
||||||
|
DraconisError(DraconisErrorCode::IoError, "Failed to write to temp cache file: " + tempPath.string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Atomically replace the old cache file with the new one
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
fs::rename(tempPath, cachePath, errc);
|
fs::rename(tempPath, cachePath, errc);
|
||||||
if (errc) {
|
if (errc) {
|
||||||
fs::remove(tempPath);
|
fs::remove(tempPath, errc); // Clean up temp file on failure (ignore error)
|
||||||
return Err(DraconisError(
|
return Err(DraconisError(
|
||||||
DraconisErrorCode::IoError,
|
DraconisErrorCode::IoError,
|
||||||
std::format("Failed to replace cache file '{}': {}", cachePath.string(), errc.message())
|
std::format("Failed to replace cache file '{}': {}", cachePath.string(), errc.message())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_log("Successfully wrote package count to cache file.");
|
debug_log("Successfully wrote {} package count to BEVE cache file.", pm_id);
|
||||||
return {};
|
return {};
|
||||||
} catch (const Exception& e) {
|
} catch (const std::ios_base::failure& e) {
|
||||||
fs::remove(tempPath);
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc);
|
||||||
return Err(DraconisError(
|
return Err(DraconisError(
|
||||||
DraconisErrorCode::InternalError, std::format("File operation error writing package count cache: {}", e.what())
|
DraconisErrorCode::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(
|
||||||
|
DraconisError(DraconisErrorCode::InternalError, std::format("Error writing package count cache: {}", e.what()))
|
||||||
|
);
|
||||||
|
} catch (...) {
|
||||||
|
std::error_code removeEc;
|
||||||
|
fs::remove(tempPath, removeEc);
|
||||||
|
return Err(
|
||||||
|
DraconisError(DraconisErrorCode::Other, std::format("Unknown error writing cache file: {}", tempPath.string()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
fn GetPackageCountInternal(const os::linux::PackageManagerInfo& pmInfo) -> Result<u64, DraconisError> {
|
||||||
|
// Use info from the struct
|
||||||
|
const fs::path& dbPath = pmInfo.db_path;
|
||||||
|
const String& pmId = pmInfo.id;
|
||||||
|
const String& queryStr = pmInfo.count_query;
|
||||||
|
|
||||||
fn os::linux::GetNixPackageCount() -> Result<u64, DraconisError> {
|
// Try reading from cache using pm_id
|
||||||
const fs::path nixDbPath = "/nix/var/nix/db/db.sqlite";
|
if (Result<PkgCountCacheData, DraconisError> cachedDataResult = ReadPkgCountCache(pmId)) {
|
||||||
|
|
||||||
if (Result<NixPkgCacheData, DraconisError> cachedDataResult = ReadPkgCountCache()) {
|
|
||||||
const auto& [count, timestamp] = *cachedDataResult;
|
const auto& [count, timestamp] = *cachedDataResult;
|
||||||
|
|
||||||
std::error_code errc;
|
std::error_code errc;
|
||||||
std::filesystem::file_time_type dbModTime = fs::last_write_time(nixDbPath, errc);
|
const std::filesystem::file_time_type dbModTime = fs::last_write_time(dbPath, errc);
|
||||||
|
|
||||||
if (errc) {
|
if (errc) {
|
||||||
warn_log("Could not get modification time for '{}': {}. Invalidating cache.", nixDbPath.string(), errc.message());
|
warn_log(
|
||||||
|
"Could not get modification time for '{}': {}. Invalidating {} cache.", dbPath.string(), errc.message(), pmId
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
if (timestamp.time_since_epoch() >= dbModTime.time_since_epoch()) {
|
if (const auto cacheTimePoint = system_clock::time_point(seconds(timestamp));
|
||||||
|
cacheTimePoint.time_since_epoch() >= dbModTime.time_since_epoch()) {
|
||||||
|
// Use cacheTimePoint for logging as well
|
||||||
debug_log(
|
debug_log(
|
||||||
"Using valid Nix package count cache (DB file unchanged since {}). Count: {}",
|
"Using valid {} package count cache (DB file unchanged since {}). Count: {}",
|
||||||
std::format("{:%F %T %Z}", floor<seconds>(timestamp)),
|
pmId,
|
||||||
|
std::format("{:%F %T %Z}", floor<seconds>(cacheTimePoint)), // Format the time_point
|
||||||
count
|
count
|
||||||
);
|
);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
debug_log("Nix package count cache stale (DB file modified).");
|
debug_log("{} package count cache stale (DB file modified).", pmId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cachedDataResult.error().code != DraconisErrorCode::NotFound)
|
if (cachedDataResult.error().code != DraconisErrorCode::NotFound)
|
||||||
debug_at(cachedDataResult.error());
|
debug_at(cachedDataResult.error());
|
||||||
debug_log("Nix package count cache not found or unreadable.");
|
debug_log("{} package count cache not found or unreadable.", pmId);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_log("Fetching fresh Nix package count from database: {}", nixDbPath.string());
|
debug_log("Fetching fresh {} package count from database: {}", pmId, dbPath.string());
|
||||||
u64 count = 0;
|
u64 count = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const SQLite::Database database("/nix/var/nix/db/db.sqlite", SQLite::OPEN_READONLY);
|
const SQLite::Database database(dbPath.string(), SQLite::OPEN_READONLY);
|
||||||
|
if (SQLite::Statement query(database, queryStr); query.executeStep()) {
|
||||||
if (SQLite::Statement query(database, "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL");
|
|
||||||
query.executeStep()) {
|
|
||||||
const i64 countInt64 = query.getColumn(0).getInt64();
|
const i64 countInt64 = query.getColumn(0).getInt64();
|
||||||
if (countInt64 < 0)
|
if (countInt64 < 0)
|
||||||
return Err(DraconisError(DraconisErrorCode::ParseError, "Negative count returned by Nix DB COUNT(*) query."));
|
return Err(DraconisError(
|
||||||
|
DraconisErrorCode::ParseError, std::format("Negative count returned by {} DB COUNT query.", pmId)
|
||||||
|
));
|
||||||
count = static_cast<u64>(countInt64);
|
count = static_cast<u64>(countInt64);
|
||||||
} else {
|
} else {
|
||||||
return Err(DraconisError(DraconisErrorCode::ParseError, "No rows returned by Nix DB COUNT(*) query."));
|
return Err(
|
||||||
|
DraconisError(DraconisErrorCode::ParseError, std::format("No rows returned by {} DB COUNT query.", pmId))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (const SQLite::Exception& e) {
|
} catch (const SQLite::Exception& e) {
|
||||||
return Err(DraconisError(
|
return Err(DraconisError(
|
||||||
DraconisErrorCode::ApiUnavailable, std::format("SQLite error occurred accessing Nix DB: {}", e.what())
|
DraconisErrorCode::ApiUnavailable, std::format("SQLite error occurred accessing {} DB: {}", pmId, e.what())
|
||||||
));
|
));
|
||||||
} catch (const Exception& e) { return Err(DraconisError(DraconisErrorCode::InternalError, e.what())); } catch (...) {
|
} catch (const Exception& e) {
|
||||||
return Err(DraconisError(DraconisErrorCode::Other, "Unknown error occurred accessing Nix DB"));
|
return Err(DraconisError(DraconisErrorCode::InternalError, e.what()));
|
||||||
|
} catch (...) {
|
||||||
|
return Err(DraconisError(DraconisErrorCode::Other, std::format("Unknown error occurred accessing {} DB", pmId)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const NixPkgCacheData dataToCache = { .count = count, .timestamp = system_clock::now() };
|
const i64 nowEpochSeconds = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
||||||
if (Result<void, DraconisError> writeResult = WritePkgCountCache(dataToCache); !writeResult) {
|
const PkgCountCacheData dataToCache = { .count = count, .timestamp_epoch_seconds = nowEpochSeconds };
|
||||||
|
|
||||||
|
if (Result<void, DraconisError> writeResult = WritePkgCountCache(pmId, dataToCache); !writeResult) {
|
||||||
warn_at(writeResult.error());
|
warn_at(writeResult.error());
|
||||||
warn_log("Failed to write Nix package count to cache.");
|
warn_log("Failed to write {} package count to cache.", pmId);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_log("Fetched fresh Nix package count: {}", count);
|
debug_log("Fetched fresh {} package count: {}", pmId, count);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace os::linux {
|
||||||
|
fn GetMossPackageCount() -> Result<u64, DraconisError> {
|
||||||
|
debug_log("Attempting to get Moss package count.");
|
||||||
|
|
||||||
|
const PackageManagerInfo mossInfo = {
|
||||||
|
.id = "moss",
|
||||||
|
.db_path = "/.moss/db/install",
|
||||||
|
.count_query = "SELECT COUNT(*) FROM meta",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (std::error_code errc; !fs::exists(mossInfo.db_path, errc)) {
|
||||||
|
if (errc) {
|
||||||
|
warn_log("Filesystem error checking for Moss DB at '{}': {}", mossInfo.db_path.string(), errc.message());
|
||||||
|
return Err(DraconisError(DraconisErrorCode::IoError, "Filesystem error checking Moss DB: " + errc.message()));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log("Moss database not found at '{}'. Assuming 0 Moss packages.", mossInfo.db_path.string());
|
||||||
|
|
||||||
|
return Err(DraconisError(DraconisErrorCode::ApiUnavailable, "Moss db not found: " + mossInfo.db_path.string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log("Moss database found at '{}'. Proceeding with count.", mossInfo.db_path.string());
|
||||||
|
|
||||||
|
return GetPackageCountInternal(mossInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn GetNixPackageCount() -> Result<u64, DraconisError> {
|
||||||
|
debug_log("Attempting to get Nix package count.");
|
||||||
|
const PackageManagerInfo nixInfo = {
|
||||||
|
.id = "nix",
|
||||||
|
.db_path = "/nix/var/nix/db/db.sqlite",
|
||||||
|
.count_query = "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (std::error_code errc; !fs::exists(nixInfo.db_path, errc)) {
|
||||||
|
if (errc) {
|
||||||
|
warn_log("Filesystem error checking for Nix DB at '{}': {}", nixInfo.db_path.string(), errc.message());
|
||||||
|
return Err(DraconisError(DraconisErrorCode::IoError, "Filesystem error checking Nix DB: " + errc.message()));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log("Nix database not found at '{}'. Assuming 0 Nix packages.", nixInfo.db_path.string());
|
||||||
|
|
||||||
|
return Err(DraconisError(DraconisErrorCode::ApiUnavailable, "Nix db not found: " + nixInfo.db_path.string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log("Nix database found at '{}'. Proceeding with count.", nixInfo.db_path.string());
|
||||||
|
|
||||||
|
return GetPackageCountInternal(nixInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn GetTotalPackageCount() -> Result<u64, DraconisError> {
|
||||||
|
debug_log("Attempting to get total package count from all package managers.");
|
||||||
|
|
||||||
|
const PackageManagerInfo mossInfo = {
|
||||||
|
.id = "moss",
|
||||||
|
.db_path = "/.moss/db/install",
|
||||||
|
.count_query = "SELECT COUNT(*) FROM meta",
|
||||||
|
};
|
||||||
|
|
||||||
|
const PackageManagerInfo nixInfo = {
|
||||||
|
.id = "nix",
|
||||||
|
.db_path = "/nix/var/nix/db/db.sqlite",
|
||||||
|
.count_query = "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL",
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 totalCount = 0;
|
||||||
|
|
||||||
|
if (Result<u64, DraconisError> mossCountResult = GetMossPackageCount(); mossCountResult) {
|
||||||
|
// `moss list installed` returns 1 less than the db count,
|
||||||
|
// so we subtract 1 for consistency.
|
||||||
|
totalCount += (*mossCountResult - 1);
|
||||||
|
} else {
|
||||||
|
debug_at(mossCountResult.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Result<u64, DraconisError> nixCountResult = GetNixPackageCount(); nixCountResult) {
|
||||||
|
totalCount += *nixCountResult;
|
||||||
|
} else {
|
||||||
|
debug_at(nixCountResult.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalCount;
|
||||||
|
}
|
||||||
|
} // namespace os::linux
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include "src/core/util/defs.hpp"
|
#include "src/core/util/defs.hpp"
|
||||||
#include "src/core/util/error.hpp"
|
#include "src/core/util/error.hpp"
|
||||||
#include "src/core/util/types.hpp"
|
#include "src/core/util/types.hpp"
|
||||||
|
@ -8,6 +10,12 @@ namespace os::linux {
|
||||||
using util::error::DraconisError;
|
using util::error::DraconisError;
|
||||||
using util::types::Result, util::types::u64;
|
using util::types::Result, util::types::u64;
|
||||||
|
|
||||||
|
struct PackageManagerInfo {
|
||||||
|
util::types::String id;
|
||||||
|
std::filesystem::path db_path;
|
||||||
|
util::types::String count_query;
|
||||||
|
};
|
||||||
|
|
||||||
// Get package count from dpkg (Debian/Ubuntu)
|
// Get package count from dpkg (Debian/Ubuntu)
|
||||||
fn GetDpkgPackageCount() -> Result<u64, DraconisError>;
|
fn GetDpkgPackageCount() -> Result<u64, DraconisError>;
|
||||||
|
|
||||||
|
@ -26,6 +34,9 @@ namespace os::linux {
|
||||||
// Get package count from apk (Alpine)
|
// Get package count from apk (Alpine)
|
||||||
fn GetApkPackageCount() -> Result<u64, DraconisError>;
|
fn GetApkPackageCount() -> Result<u64, DraconisError>;
|
||||||
|
|
||||||
|
// Get package count from moss (AerynOS)
|
||||||
|
fn GetMossPackageCount() -> Result<u64, DraconisError>;
|
||||||
|
|
||||||
// Get package count from nix
|
// Get package count from nix
|
||||||
fn GetNixPackageCount() -> Result<u64, DraconisError>;
|
fn GetNixPackageCount() -> Result<u64, DraconisError>;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue