woof
This commit is contained in:
parent
996ca1122d
commit
e127a89e06
7 changed files with 204 additions and 112 deletions
|
@ -10,10 +10,11 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "src/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
#include "package.hpp"
|
#include "package.hpp"
|
||||||
|
|
||||||
#if !defined(__serenity_) && !defined(_WIN32)
|
#if !defined(__serenity__) && !defined(_WIN32)
|
||||||
#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
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp> // pugi::{xml_document, xml_node, xml_parse_result}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#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 <future> // std::{async, future, launch}
|
#include <future> // std::{async, future, launch}
|
||||||
#include <system_error> // std::error_code
|
#include <system_error> // std::{errc, error_code}
|
||||||
|
|
||||||
#include "src/util/cache.hpp"
|
#include "src/util/cache.hpp"
|
||||||
#include "src/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
|
@ -22,14 +22,15 @@
|
||||||
#include "src/util/logging.hpp"
|
#include "src/util/logging.hpp"
|
||||||
#include "src/util/types.hpp"
|
#include "src/util/types.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
#include "include/matchit.hpp"
|
||||||
using namespace std::chrono;
|
|
||||||
using util::cache::ReadCache, util::cache::WriteCache;
|
|
||||||
using util::error::DracError, util::error::DracErrorCode;
|
|
||||||
using util::types::Err, util::types::Exception, util::types::Future, util::types::Result, util::types::String,
|
|
||||||
util::types::Vec, util::types::i64, util::types::u64, util::types::Option, util::types::None;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using std::chrono::system_clock, std::chrono::seconds, std::chrono::floor, std::chrono::duration_cast;
|
||||||
|
using util::cache::ReadCache, util::cache::WriteCache;
|
||||||
|
using util::error::DracError, util::error::DracErrorCode;
|
||||||
|
using util::types::Err, util::types::Exception, util::types::Result, util::types::String, util::types::u64, util::types::i64, util::types::Option;
|
||||||
|
|
||||||
fn GetCountFromDirectoryImpl(
|
fn GetCountFromDirectoryImpl(
|
||||||
const String& pmId,
|
const String& pmId,
|
||||||
const fs::path& dirPath,
|
const fs::path& dirPath,
|
||||||
|
@ -89,9 +90,7 @@ namespace {
|
||||||
std::format("Filesystem error checking if '{}' is a directory: {}", dirPath.string(), fsErrCode.message())
|
std::format("Filesystem error checking if '{}' is a directory: {}", dirPath.string(), fsErrCode.message())
|
||||||
));
|
));
|
||||||
|
|
||||||
return Err(
|
return Err(DracError(DracErrorCode::NotFound, std::format("{} path is not a directory: {}", pmId, dirPath.string())));
|
||||||
DracError(DracErrorCode::NotFound, std::format("{} path is not a directory: {}", pmId, dirPath.string()))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fsErrCode.clear();
|
fsErrCode.clear();
|
||||||
|
@ -160,6 +159,9 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace package {
|
namespace package {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using util::types::Err, util::types::None, util::types::Option, util::types::Result, util::types::String, util::types::u64;
|
||||||
|
|
||||||
fn GetCountFromDirectory(
|
fn GetCountFromDirectory(
|
||||||
const String& pmId,
|
const String& pmId,
|
||||||
const fs::path& dirPath,
|
const fs::path& dirPath,
|
||||||
|
@ -183,14 +185,17 @@ namespace package {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__serenity__) && !defined(_WIN32)
|
#if !defined(__serenity__) && !defined(_WIN32)
|
||||||
fn GetCountFromDb(const PackageManagerInfo& pmInfo) -> Result<u64> {
|
fn GetCountFromDb(const String& pmId, const fs::path& dbPath, const String& countQuery) -> Result<u64> {
|
||||||
const auto& [pmId, dbPath, countQuery] = pmInfo;
|
using util::cache::ReadCache, util::cache::WriteCache;
|
||||||
const String cacheKey = "pkg_count_" + pmId; // More specific cache key
|
using util::error::DracError, util::error::DracErrorCode;
|
||||||
|
using util::types::Exception, util::types::i64;
|
||||||
|
|
||||||
|
const String cacheKey = "pkg_count_" + pmId;
|
||||||
|
|
||||||
if (Result<PkgCountCacheData> cachedDataResult = ReadCache<PkgCountCacheData>(cacheKey)) {
|
if (Result<PkgCountCacheData> cachedDataResult = ReadCache<PkgCountCacheData>(cacheKey)) {
|
||||||
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 fs::file_time_type dbModTime = fs::last_write_time(dbPath, errc);
|
||||||
|
|
||||||
if (errc) {
|
if (errc) {
|
||||||
warn_log(
|
warn_log(
|
||||||
|
@ -230,7 +235,7 @@ namespace package {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SQLite::Database database(dbPath.string(), SQLite::OPEN_READONLY);
|
const SQLite::Database database(dbPath.string(), SQLite::OPEN_READONLY);
|
||||||
SQLite::Statement queryStmt(database, countQuery); // Use query directly
|
SQLite::Statement queryStmt(database, countQuery);
|
||||||
|
|
||||||
if (queryStmt.executeStep()) {
|
if (queryStmt.executeStep()) {
|
||||||
const i64 countInt64 = queryStmt.getColumn(0).getInt64();
|
const i64 countInt64 = queryStmt.getColumn(0).getInt64();
|
||||||
|
@ -268,20 +273,21 @@ namespace package {
|
||||||
#endif // __serenity__ || _WIN32
|
#endif // __serenity__ || _WIN32
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
fn GetCountFromPlist(const String& pmId, const std::filesystem::path& plistPath) -> Result<u64> {
|
fn GetCountFromPlist(const String& pmId, const fs::path& plistPath) -> Result<u64> {
|
||||||
using namespace pugi;
|
using pugi::xml_document, pugi::xml_node, pugi::xml_parse_result;
|
||||||
using util::types::StringView;
|
using util::cache::ReadCache, util::cache::WriteCache;
|
||||||
|
using util::error::DracError, util::error::DracErrorCode;
|
||||||
|
using util::types::i64, util::types::StringView;
|
||||||
|
|
||||||
const String cacheKey = "pkg_count_" + pmId;
|
const String cacheKey = "pkg_count_" + pmId;
|
||||||
std::error_code fsErrCode;
|
std::error_code fsErrCode;
|
||||||
|
|
||||||
// Cache check
|
|
||||||
if (Result<PkgCountCacheData> cachedDataResult = ReadCache<PkgCountCacheData>(cacheKey)) {
|
if (Result<PkgCountCacheData> cachedDataResult = ReadCache<PkgCountCacheData>(cacheKey)) {
|
||||||
const auto& [cachedCount, timestamp] = *cachedDataResult;
|
const auto& [cachedCount, timestamp] = *cachedDataResult;
|
||||||
if (fs::exists(plistPath, fsErrCode) && !fsErrCode) {
|
if (fs::exists(plistPath, fsErrCode) && !fsErrCode) {
|
||||||
const fs::file_time_type plistModTime = fs::last_write_time(plistPath, fsErrCode);
|
const fs::file_time_type plistModTime = fs::last_write_time(plistPath, fsErrCode);
|
||||||
if (!fsErrCode) {
|
if (!fsErrCode) {
|
||||||
if (const std::chrono::system_clock::time_point cacheTimePoint = std::chrono::system_clock::time_point(std::chrono::seconds(timestamp));
|
if (const system_clock::time_point cacheTimePoint = system_clock::time_point(seconds(timestamp));
|
||||||
cacheTimePoint.time_since_epoch() >= plistModTime.time_since_epoch()) {
|
cacheTimePoint.time_since_epoch() >= plistModTime.time_since_epoch()) {
|
||||||
debug_log("Using valid {} plist count cache (file '{}' unchanged since {}). Count: {}", pmId, plistPath.string(), std::format("{:%F %T %Z}", std::chrono::floor<std::chrono::seconds>(cacheTimePoint)), cachedCount);
|
debug_log("Using valid {} plist count cache (file '{}' unchanged since {}). Count: {}", pmId, plistPath.string(), std::format("{:%F %T %Z}", std::chrono::floor<std::chrono::seconds>(cacheTimePoint)), cachedCount);
|
||||||
return cachedCount;
|
return cachedCount;
|
||||||
|
@ -296,17 +302,16 @@ namespace package {
|
||||||
debug_log("{} plist count cache not found or unreadable", pmId);
|
debug_log("{} plist count cache not found or unreadable", pmId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse plist and count
|
|
||||||
xml_document doc;
|
xml_document doc;
|
||||||
xml_parse_result result = doc.load_file(plistPath.c_str());
|
xml_parse_result result = doc.load_file(plistPath.c_str());
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return Err(util::error::DracError(util::error::DracErrorCode::ParseError, std::format("Failed to parse plist file '{}': {}", plistPath.string(), result.description())));
|
return Err(DracError(DracErrorCode::ParseError, std::format("Failed to parse plist file '{}': {}", plistPath.string(), result.description())));
|
||||||
|
|
||||||
xml_node dict = doc.child("plist").child("dict");
|
xml_node dict = doc.child("plist").child("dict");
|
||||||
|
|
||||||
if (!dict)
|
if (!dict)
|
||||||
return Err(util::error::DracError(util::error::DracErrorCode::ParseError, std::format("No <dict> in plist file '{}'.", plistPath.string())));
|
return Err(DracError(DracErrorCode::ParseError, std::format("No <dict> in plist file '{}'.", plistPath.string())));
|
||||||
|
|
||||||
u64 count = 0;
|
u64 count = 0;
|
||||||
|
|
||||||
|
@ -340,7 +345,7 @@ namespace package {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return Err(util::error::DracError(util::error::DracErrorCode::NotFound, std::format("No installed packages found in plist file '{}'.", plistPath.string())));
|
return Err(DracError(DracErrorCode::NotFound, std::format("No installed packages found in plist file '{}'.", plistPath.string())));
|
||||||
|
|
||||||
const i64 timestampEpochSeconds = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
const i64 timestampEpochSeconds = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
const PkgCountCacheData dataToCache(count, timestampEpochSeconds);
|
const PkgCountCacheData dataToCache(count, timestampEpochSeconds);
|
||||||
|
@ -351,27 +356,13 @@ namespace package {
|
||||||
#endif // __linux__
|
#endif // __linux__
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
fn GetNixCount() -> Result<u64> {
|
fn CountNix() -> Result<u64> {
|
||||||
const PackageManagerInfo nixInfo = {
|
return GetCountFromDb("nix", "/nix/var/nix/db/db.sqlite", "SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL");
|
||||||
.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 GetCountFromDb(nixInfo);
|
|
||||||
}
|
}
|
||||||
#endif // __linux__ || __APPLE__
|
#endif // __linux__ || __APPLE__
|
||||||
|
|
||||||
fn CountCargo() -> Result<u64> {
|
fn CountCargo() -> Result<u64> {
|
||||||
|
using util::error::DracError, util::error::DracErrorCode;
|
||||||
using util::helpers::GetEnv;
|
using util::helpers::GetEnv;
|
||||||
|
|
||||||
fs::path cargoPath {};
|
fs::path cargoPath {};
|
||||||
|
@ -388,55 +379,87 @@ namespace package {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetTotalCount() -> Result<u64> {
|
fn GetTotalCount() -> Result<u64> {
|
||||||
Vec<Future<Result<u64>>> futures;
|
using util::error::DracError;
|
||||||
|
using util::types::Array, util::types::Exception, util::types::Future;
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// futures.push_back(std::async(std::launch::async, GetApkCount));
|
constexpr size_t platformSpecificCount = 6; // Apk, Dpkg, Moss, Pacman, Rpm, Xbps
|
||||||
futures.push_back(std::async(std::launch::async, GetDpkgCount));
|
|
||||||
futures.push_back(std::async(std::launch::async, GetMossCount));
|
|
||||||
futures.push_back(std::async(std::launch::async, GetPacmanCount));
|
|
||||||
// futures.push_back(std::async(std::launch::async, GetPortageCount));
|
|
||||||
futures.push_back(std::async(std::launch::async, GetRpmCount));
|
|
||||||
futures.push_back(std::async(std::launch::async, GetXbpsCount));
|
|
||||||
// futures.push_back(std::async(std::launch::async, GetZypperCount));
|
|
||||||
#elifdef __APPLE__
|
#elifdef __APPLE__
|
||||||
futures.push_back(std::async(std::launch::async, GetHomebrewCount));
|
constexpr size_t platformSpecificCount = 2; // Homebrew, MacPorts
|
||||||
futures.push_back(std::async(std::launch::async, GetMacPortsCount));
|
|
||||||
#elifdef _WIN32
|
#elifdef _WIN32
|
||||||
futures.push_back(std::async(std::launch::async, CountWinGet));
|
constexpr size_t platformSpecificCount = 3; // WinGet, Chocolatey, Scoop
|
||||||
futures.push_back(std::async(std::launch::async, CountChocolatey));
|
|
||||||
futures.push_back(std::async(std::launch::async, CountScoop));
|
|
||||||
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||||
futures.push_back(std::async(std::launch::async, GetPkgNgCount));
|
constexpr size_t platformSpecificCount = 1; // GetPkgNgCount
|
||||||
#elifdef __NetBSD__
|
#elifdef __NetBSD__
|
||||||
futures.push_back(std::async(std::launch::async, GetPkgSrcCount));
|
constexpr size_t platformSpecificCount = 1; // GetPkgSrcCount
|
||||||
#elifdef __HAIKU__
|
#elifdef __HAIKU__
|
||||||
futures.push_back(std::async(std::launch::async, GetHaikuCount));
|
constexpr size_t platformSpecificCount = 1; // GetHaikuCount
|
||||||
#elifdef __serenity__
|
#elifdef __serenity__
|
||||||
futures.push_back(std::async(std::launch::async, GetSerenityCount));
|
constexpr size_t platformSpecificCount = 1; // GetSerenityCount
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__APPLE__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
futures.push_back(std::async(std::launch::async, GetNixCount));
|
// platform specific + cargo + nix
|
||||||
|
constexpr size_t numFutures = platformSpecificCount + 2;
|
||||||
|
#else
|
||||||
|
// platform specific + cargo
|
||||||
|
constexpr size_t numFutures = platformSpecificCount + 1;
|
||||||
#endif
|
#endif
|
||||||
futures.push_back(std::async(std::launch::async, CountCargo));
|
|
||||||
|
Array<Future<Result<u64>>, numFutures>
|
||||||
|
futures = {
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
std::async(std::launch::async, CountApk),
|
||||||
|
std::async(std::launch::async, CountDpkg),
|
||||||
|
std::async(std::launch::async, CountMoss),
|
||||||
|
std::async(std::launch::async, CountPacman),
|
||||||
|
std::async(std::launch::async, CountRpm),
|
||||||
|
std::async(std::launch::async, CountXbps),
|
||||||
|
// std::async(std::launch::async, CountZypper),
|
||||||
|
#elifdef __APPLE__
|
||||||
|
std::async(std::launch::async, GetHomebrewCount),
|
||||||
|
std::async(std::launch::async, GetMacPortsCount),
|
||||||
|
#elifdef _WIN32
|
||||||
|
std::async(std::launch::async, CountWinGet),
|
||||||
|
std::async(std::launch::async, CountChocolatey),
|
||||||
|
std::async(std::launch::async, CountScoop),
|
||||||
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||||
|
std::async(std::launch::async, GetPkgNgCount),
|
||||||
|
#elifdef __NetBSD__
|
||||||
|
std::async(std::launch::async, GetPkgSrcCount),
|
||||||
|
#elifdef __HAIKU__
|
||||||
|
std::async(std::launch::async, GetHaikuCount),
|
||||||
|
#elifdef __serenity__
|
||||||
|
std::async(std::launch::async, GetSerenityCount),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
|
std::async(std::launch::async, CountNix),
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::async(std::launch::async, CountCargo),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
u64 totalCount = 0;
|
u64 totalCount = 0;
|
||||||
bool oneSucceeded = false;
|
bool oneSucceeded = false;
|
||||||
|
|
||||||
for (Future<Result<u64>>& fut : futures) {
|
for (Future<Result<u64>>& fut : futures) {
|
||||||
try {
|
try {
|
||||||
|
using matchit::match, matchit::is, matchit::or_, matchit::_;
|
||||||
using enum util::error::DracErrorCode;
|
using enum util::error::DracErrorCode;
|
||||||
|
|
||||||
if (Result<u64> result = fut.get()) {
|
if (Result<u64> result = fut.get()) {
|
||||||
totalCount += *result;
|
totalCount += *result;
|
||||||
oneSucceeded = true;
|
oneSucceeded = true;
|
||||||
debug_log("Added {} packages. Current total: {}", *result, totalCount);
|
debug_log("Added {} packages. Current total: {}", *result, totalCount);
|
||||||
} else if (result.error().code != NotFound && result.error().code != ApiUnavailable &&
|
} else {
|
||||||
result.error().code != NotSupported) {
|
match(result.error().code)(
|
||||||
error_at(result.error());
|
is | or_(NotFound, ApiUnavailable, NotSupported) = [&] -> void { debug_at(result.error()); },
|
||||||
} else
|
is | _ = [&] -> void { error_at(result.error()); }
|
||||||
debug_at(result.error());
|
);
|
||||||
|
}
|
||||||
} catch (const Exception& exc) {
|
} catch (const Exception& exc) {
|
||||||
error_log("Caught exception while getting package count future: {}", exc.what());
|
error_log("Caught exception while getting package count future: {}", exc.what());
|
||||||
} catch (...) { error_log("Caught unknown exception while getting package count future."); }
|
} catch (...) { error_log("Caught unknown exception while getting package count future."); }
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace package {
|
||||||
* @param pmInfo Information about the package manager database.
|
* @param pmInfo Information about the package manager database.
|
||||||
* @return Result containing the count (u64) or a DracError.
|
* @return Result containing the count (u64) or a DracError.
|
||||||
*/
|
*/
|
||||||
fn GetCountFromDb(const PackageManagerInfo& pmInfo) -> Result<u64>;
|
fn GetCountFromDb(const String& pmId, const fs::path& dbPath, const String& countQuery) -> Result<u64>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets package count by iterating entries in a directory, optionally filtering and subtracting.
|
* @brief Gets package count by iterating entries in a directory, optionally filtering and subtracting.
|
||||||
|
@ -101,14 +101,12 @@ namespace package {
|
||||||
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath) -> Result<u64>;
|
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath) -> Result<u64>;
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
fn GetApkCount() -> Result<u64>;
|
fn CountApk() -> Result<u64>;
|
||||||
fn GetDpkgCount() -> Result<u64>;
|
fn CountDpkg() -> Result<u64>;
|
||||||
fn GetMossCount() -> Result<u64>;
|
fn CountMoss() -> Result<u64>;
|
||||||
fn GetPacmanCount() -> Result<u64>;
|
fn CountPacman() -> Result<u64>;
|
||||||
// fn GetPortageCount() -> Result<u64>;
|
fn CountRpm() -> Result<u64>;
|
||||||
fn GetRpmCount() -> Result<u64>;
|
fn CountXbps() -> Result<u64>;
|
||||||
fn GetXbpsCount() -> Result<u64>;
|
|
||||||
// fn GetZypperCount() -> Result<u64>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Counts installed packages in a plist file (used by xbps and potentially others).
|
* @brief Counts installed packages in a plist file (used by xbps and potentially others).
|
||||||
|
@ -136,7 +134,7 @@ namespace package {
|
||||||
|
|
||||||
// Common (potentially cross-platform)
|
// Common (potentially cross-platform)
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
fn GetNixCount() -> Result<u64>;
|
fn CountNix() -> Result<u64>;
|
||||||
#endif
|
#endif
|
||||||
fn CountCargo() -> Result<u64>;
|
fn CountCargo() -> Result<u64>;
|
||||||
} // namespace package
|
} // namespace package
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib> // EXIT_FAILURE, EXIT_SUCCESS
|
||||||
#include <ftxui/dom/elements.hpp> // ftxui::{Element, hbox, vbox, text, separator, filler, etc.}
|
#include <ftxui/dom/elements.hpp> // ftxui::{Element, hbox, vbox, text, separator, filler, etc.}
|
||||||
#include <ftxui/dom/node.hpp> // ftxui::{Render}
|
#include <ftxui/dom/node.hpp> // ftxui::{Render}
|
||||||
#include <ftxui/screen/screen.hpp> // ftxui::{Screen, Dimension::Full}
|
#include <ftxui/screen/screen.hpp> // ftxui::{Screen, Dimension::Full}
|
||||||
|
|
112
src/os/linux.cpp
112
src/os/linux.cpp
|
@ -26,6 +26,7 @@
|
||||||
#include <utility> // std::move
|
#include <utility> // std::move
|
||||||
|
|
||||||
#include "src/core/package.hpp"
|
#include "src/core/package.hpp"
|
||||||
|
#include "src/util/cache.hpp"
|
||||||
#include "src/util/defs.hpp"
|
#include "src/util/defs.hpp"
|
||||||
#include "src/util/error.hpp"
|
#include "src/util/error.hpp"
|
||||||
#include "src/util/helpers.hpp"
|
#include "src/util/helpers.hpp"
|
||||||
|
@ -494,18 +495,97 @@ namespace os {
|
||||||
namespace package {
|
namespace package {
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
fn GetDpkgCount() -> Result<u64> {
|
fn CountApk() -> Result<u64> {
|
||||||
|
using namespace util::cache;
|
||||||
|
|
||||||
|
const String pmId = "apk";
|
||||||
|
const fs::path apkDbPath = "/lib/apk/db/installed";
|
||||||
|
const String cacheKey = "pkg_count_" + pmId;
|
||||||
|
|
||||||
|
std::error_code fsErrCode;
|
||||||
|
|
||||||
|
if (!fs::exists(apkDbPath, fsErrCode)) {
|
||||||
|
if (fsErrCode) {
|
||||||
|
warn_log("Filesystem error checking for Apk DB at '{}': {}", apkDbPath.string(), fsErrCode.message());
|
||||||
|
return Err(DracError(DracErrorCode::IoError, "Filesystem error checking Apk DB: " + fsErrCode.message()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(DracError(DracErrorCode::NotFound, std::format("Apk database path '{}' does not exist", apkDbPath.string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Result<PkgCountCacheData> cachedDataResult = ReadCache<PkgCountCacheData>(cacheKey)) {
|
||||||
|
const auto& [cachedCount, timestamp] = *cachedDataResult;
|
||||||
|
std::error_code modTimeErrCode;
|
||||||
|
const fs::file_time_type dbModTime = fs::last_write_time(apkDbPath, modTimeErrCode);
|
||||||
|
|
||||||
|
if (modTimeErrCode) {
|
||||||
|
warn_log(
|
||||||
|
"Could not get modification time for '{}': {}. Invalidating {} cache.",
|
||||||
|
apkDbPath.string(),
|
||||||
|
modTimeErrCode.message(),
|
||||||
|
pmId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
using std::chrono::system_clock, std::chrono::seconds, std::chrono::floor;
|
||||||
|
const system_clock::time_point cacheTimePoint = system_clock::time_point(seconds(timestamp));
|
||||||
|
|
||||||
|
if (cacheTimePoint.time_since_epoch() >= dbModTime.time_since_epoch()) {
|
||||||
|
debug_log("Using valid {} package count cache (DB file unchanged since {}). Count: {}", pmId, std::format("{:%F %T %Z}", floor<seconds>(cacheTimePoint)), cachedCount);
|
||||||
|
return cachedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 file: {}", pmId, apkDbPath.string());
|
||||||
|
|
||||||
|
std::ifstream file(apkDbPath);
|
||||||
|
if (!file.is_open())
|
||||||
|
return Err(DracError(DracErrorCode::IoError, std::format("Failed to open Apk database file '{}'", apkDbPath.string())));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
|
||||||
|
u64 count = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (std::getline(file, line))
|
||||||
|
if (line.empty())
|
||||||
|
count++;
|
||||||
|
} catch (const std::ios_base::failure& e) {
|
||||||
|
return Err(DracError(
|
||||||
|
DracErrorCode::IoError,
|
||||||
|
std::format("Error reading Apk database file '{}': {}", apkDbPath.string(), e.what())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.bad())
|
||||||
|
return Err(DracError(DracErrorCode::IoError, std::format("IO error while reading Apk database file '{}'", apkDbPath.string())));
|
||||||
|
|
||||||
|
{
|
||||||
|
using std::chrono::duration_cast, std::chrono::system_clock, std::chrono::seconds;
|
||||||
|
|
||||||
|
const i64 timestampEpochSeconds = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
const PkgCountCacheData dataToCache(count, timestampEpochSeconds);
|
||||||
|
|
||||||
|
if (Result writeResult = WriteCache(cacheKey, dataToCache); !writeResult)
|
||||||
|
debug_at(writeResult.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CountDpkg() -> Result<u64> {
|
||||||
return GetCountFromDirectory("Dpkg", fs::current_path().root_path() / "var" / "lib" / "dpkg" / "info", ".list"s);
|
return GetCountFromDirectory("Dpkg", fs::current_path().root_path() / "var" / "lib" / "dpkg" / "info", ".list"s);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetMossCount() -> Result<u64> {
|
fn CountMoss() -> Result<u64> {
|
||||||
const PackageManagerInfo mossInfo = {
|
Result<u64> countResult = GetCountFromDb("moss", "/.moss/db/install", "SELECT COUNT(*) FROM meta");
|
||||||
.id = "moss",
|
|
||||||
.dbPath = "/.moss/db/install",
|
|
||||||
.countQuery = "SELECT COUNT(*) FROM meta",
|
|
||||||
};
|
|
||||||
|
|
||||||
Result<u64> countResult = GetCountFromDb(mossInfo);
|
|
||||||
|
|
||||||
if (countResult)
|
if (countResult)
|
||||||
if (*countResult > 0)
|
if (*countResult > 0)
|
||||||
|
@ -514,21 +594,15 @@ namespace package {
|
||||||
return countResult;
|
return countResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetPacmanCount() -> Result<u64> {
|
fn CountPacman() -> Result<u64> {
|
||||||
return GetCountFromDirectory("Pacman", fs::current_path().root_path() / "var" / "lib" / "pacman" / "local", true);
|
return GetCountFromDirectory("Pacman", fs::current_path().root_path() / "var" / "lib" / "pacman" / "local", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetRpmCount() -> Result<u64> {
|
fn CountRpm() -> Result<u64> {
|
||||||
const PackageManagerInfo rpmInfo = {
|
return GetCountFromDb("rpm", "/var/lib/rpm/rpmdb.sqlite", "SELECT COUNT(*) FROM Installtid");
|
||||||
.id = "rpm",
|
|
||||||
.dbPath = "/var/lib/rpm/rpmdb.sqlite",
|
|
||||||
.countQuery = "SELECT COUNT(*) FROM Installtid",
|
|
||||||
};
|
|
||||||
|
|
||||||
return GetCountFromDb(rpmInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn GetXbpsCount() -> Result<u64> {
|
fn CountXbps() -> Result<u64> {
|
||||||
const StringView xbpsDbPath = "/var/db/xbps";
|
const StringView xbpsDbPath = "/var/db/xbps";
|
||||||
const String pmId = "xbps";
|
const String pmId = "xbps";
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,7 @@
|
||||||
* (found in linux.cpp, windows.cpp, macos.cpp).
|
* (found in linux.cpp, windows.cpp, macos.cpp).
|
||||||
*/
|
*/
|
||||||
namespace os {
|
namespace os {
|
||||||
using util::error::DracError;
|
using util::types::u64, util::types::String, util::types::Result, util::types::MediaInfo, util::types::DiskSpace;
|
||||||
using util::types::u64, util::types::String, util::types::Option, util::types::Result, util::types::MediaInfo,
|
|
||||||
util::types::DiskSpace;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the total amount of physical RAM installed in the system.
|
* @brief Get the total amount of physical RAM installed in the system.
|
||||||
|
|
|
@ -15,17 +15,15 @@ struct wl_display;
|
||||||
namespace wl {
|
namespace wl {
|
||||||
using display = wl_display;
|
using display = wl_display;
|
||||||
|
|
||||||
// NOLINTBEGIN(readability-identifier-naming)
|
inline fn Connect(const char* name) -> display* {
|
||||||
inline fn connect(const char* name) -> display* {
|
|
||||||
return wl_display_connect(name);
|
return wl_display_connect(name);
|
||||||
}
|
}
|
||||||
inline fn disconnect(display* display) -> void {
|
inline fn Disconnect(display* display) -> void {
|
||||||
wl_display_disconnect(display);
|
wl_display_disconnect(display);
|
||||||
}
|
}
|
||||||
inline fn get_fd(display* display) -> int {
|
inline fn GetFd(display* display) -> int {
|
||||||
return wl_display_get_fd(display);
|
return wl_display_get_fd(display);
|
||||||
}
|
}
|
||||||
// NOLINTEND(readability-identifier-naming)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RAII wrapper for Wayland display connections
|
* RAII wrapper for Wayland display connections
|
||||||
|
@ -70,12 +68,12 @@ namespace wl {
|
||||||
});
|
});
|
||||||
|
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) - needs to come after wl_log_set_handler_client
|
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) - needs to come after wl_log_set_handler_client
|
||||||
m_display = connect(nullptr);
|
m_display = Connect(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
~DisplayGuard() {
|
~DisplayGuard() {
|
||||||
if (m_display)
|
if (m_display)
|
||||||
disconnect(m_display);
|
Disconnect(m_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-copyable
|
// Non-copyable
|
||||||
|
@ -88,7 +86,7 @@ namespace wl {
|
||||||
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard& {
|
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard& {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
if (m_display)
|
if (m_display)
|
||||||
disconnect(m_display);
|
Disconnect(m_display);
|
||||||
|
|
||||||
m_display = std::exchange(other.m_display, nullptr);
|
m_display = std::exchange(other.m_display, nullptr);
|
||||||
}
|
}
|
||||||
|
@ -104,7 +102,7 @@ namespace wl {
|
||||||
return m_display;
|
return m_display;
|
||||||
}
|
}
|
||||||
[[nodiscard]] fn fd() const -> util::types::i32 {
|
[[nodiscard]] fn fd() const -> util::types::i32 {
|
||||||
return get_fd(m_display);
|
return GetFd(m_display);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace wl
|
} // namespace wl
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue