24ms average on WINDOWS is wild

This commit is contained in:
Mars 2025-05-05 01:41:50 -04:00
parent 7e7678f5f0
commit 0027fa6520
22 changed files with 627 additions and 529 deletions

View file

@ -52,21 +52,21 @@ location = "London" # Your city name
Vec<fs::path> possiblePaths;
#ifdef _WIN32
if (Result<String, DracError> result = GetEnv("LOCALAPPDATA"))
if (Result<String> result = GetEnv("LOCALAPPDATA"))
possiblePaths.push_back(fs::path(*result) / "draconis++" / "config.toml");
if (Result<String, DracError> result = GetEnv("USERPROFILE")) {
if (Result<String> result = GetEnv("USERPROFILE")) {
possiblePaths.push_back(fs::path(*result) / ".config" / "draconis++" / "config.toml");
possiblePaths.push_back(fs::path(*result) / "AppData" / "Local" / "draconis++" / "config.toml");
}
if (Result<String, DracError> result = GetEnv("APPDATA"))
if (Result<String> result = GetEnv("APPDATA"))
possiblePaths.push_back(fs::path(*result) / "draconis++" / "config.toml");
#else
if (Result<String, DracError> result = GetEnv("XDG_CONFIG_HOME"))
if (Result<String> result = GetEnv("XDG_CONFIG_HOME"))
possiblePaths.emplace_back(fs::path(*result) / "draconis++" / "config.toml");
if (Result<String, DracError> result = GetEnv("HOME")) {
if (Result<String> result = GetEnv("HOME")) {
possiblePaths.emplace_back(fs::path(*result) / ".config" / "draconis++" / "config.toml");
possiblePaths.emplace_back(fs::path(*result) / ".draconis++" / "config.toml");
}
@ -121,8 +121,8 @@ location = "London" # Your city name
const passwd* pwd = getpwuid(getuid());
CStr pwdName = pwd ? pwd->pw_name : nullptr;
const Result<String, DracError> envUser = util::helpers::GetEnv("USER");
const Result<String, DracError> envLogname = util::helpers::GetEnv("LOGNAME");
const Result<String> envUser = util::helpers::GetEnv("USER");
const Result<String> envLogname = util::helpers::GetEnv("LOGNAME");
defaultName = pwdName ? pwdName : envUser ? *envUser : envLogname ? *envLogname : "User";
#endif

View file

@ -57,11 +57,11 @@ struct General {
return pwd->pw_name;
// Try to get the username using environment variables
if (Result<String, DracError> envUser = GetEnv("USER"))
if (Result<String> envUser = GetEnv("USER"))
return *envUser;
// Finally, try to get the username using LOGNAME
if (Result<String, DracError> envLogname = GetEnv("LOGNAME"))
if (Result<String> envLogname = GetEnv("LOGNAME"))
return *envLogname;
// If all else fails, return a default name
@ -151,7 +151,7 @@ struct Weather {
* API key, and units. It returns a WeatherOutput object containing the
* retrieved weather data.
*/
[[nodiscard]] fn getWeatherInfo() const -> Result<weather::Output, DracError>;
[[nodiscard]] fn getWeatherInfo() const -> Result<weather::Output>;
};
/**

View file

@ -13,7 +13,6 @@
#include <glaze/core/opts.hpp> // glz::opts
#include <glaze/core/reflect.hpp> // glz::format_error
#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 <iterator> // std::istreambuf_iterator
#include <variant> // std::{get, holds_alternative}
@ -44,13 +43,13 @@ namespace {
return totalSize;
}
fn MakeApiRequest(const String& url) -> Result<Output, String> {
fn MakeApiRequest(const String& url) -> Result<Output> {
debug_log("Making API request to URL: {}", url);
CURL* curl = curl_easy_init();
String responseBuffer;
if (!curl)
return Err("Failed to initialize cURL");
return Err(DracError(DracErrorCode::ApiUnavailable, "Failed to initialize cURL"));
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
@ -62,41 +61,40 @@ namespace {
curl_easy_cleanup(curl);
if (res != CURLE_OK)
return Err(std::format("cURL error: {}", curl_easy_strerror(res)));
return Err(DracError(DracErrorCode::ApiUnavailable, std::format("cURL error: {}", curl_easy_strerror(res))));
Output output;
if (const error_ctx errc = read<glaze_opts>(output, responseBuffer); errc.ec != error_code::none)
return Err("API response parse error: " + format_error(errc, responseBuffer));
return Err(DracError(
DracErrorCode::ParseError, std::format("Failed to parse JSON response: {}", format_error(errc, responseBuffer))
));
return output;
}
} // namespace
fn Weather::getWeatherInfo() const -> Result<Output, DracError> {
fn Weather::getWeatherInfo() const -> Result<Output> {
using namespace std::chrono;
using util::types::i32;
if (Result<Output, DracError> data = ReadCache<Output>("weather")) {
if (Result<Output> data = ReadCache<Output>("weather")) {
const Output& dataVal = *data;
if (const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
cacheAge < 60min) { // NOLINT(misc-include-cleaner) - inherited from <chrono>
debug_log("Using valid cache");
cacheAge < 60min) // NOLINT(misc-include-cleaner) - inherited from <chrono>
return dataVal;
}
debug_log("Cache expired");
} else {
} else
error_at(data.error());
}
fn handleApiResult = [](const Result<Output, String>& result) -> Result<Output, DracError> {
fn handleApiResult = [](const Result<Output>& result) -> Result<Output> {
if (!result)
return Err(DracError(DracErrorCode::ApiUnavailable, result.error()));
return Err(result.error());
if (Result<void, DracError> writeResult = WriteCache("weather", *result); !writeResult)
error_at(writeResult.error());
if (Result writeResult = WriteCache("weather", *result); !writeResult)
return Err(writeResult.error());
return *result;
};
@ -106,8 +104,6 @@ fn Weather::getWeatherInfo() const -> Result<Output, DracError> {
char* escaped = curl_easy_escape(nullptr, city.c_str(), static_cast<i32>(city.length()));
debug_log("Requesting city: {}", escaped);
const String apiUrl =
std::format("https://api.openweathermap.org/data/2.5/weather?q={}&appid={}&units={}", escaped, apiKey, units);
@ -118,7 +114,6 @@ fn Weather::getWeatherInfo() const -> Result<Output, DracError> {
if (std::holds_alternative<Coords>(location)) {
const auto& [lat, lon] = std::get<Coords>(location);
debug_log("Requesting coordinates: lat={:.3f}, lon={:.3f}", lat, lon);
const String apiUrl = std::format(
"https://api.openweathermap.org/data/2.5/weather?lat={:.3f}&lon={:.3f}&appid={}&units={}", lat, lon, apiKey, units

View file

@ -1,10 +1,10 @@
#include "package.hpp"
#ifndef __serenity__
#if !defined(__serenity_) && !defined(_WIN32)
#include <SQLiteCpp/Database.h> // SQLite::{Database, OPEN_READONLY}
#include <SQLiteCpp/Exception.h> // SQLite::Exception
#include <SQLiteCpp/Statement.h> // SQLite::Statement
#endif // __serenity__
#endif
#include <chrono> // std::chrono
#include <filesystem> // std::filesystem
@ -23,93 +23,131 @@ 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::Vec, util::types::i64, util::types::u64, util::types::Option, util::types::None;
namespace {
fn GetCountFromDirectoryImpl(
const String& pmId,
const fs::path& dirPath,
const String& fileExtensionFilter,
const bool subtractOne
) -> Result<u64, DracError> {
debug_log("Counting packages for '{}' in directory: {}", pmId, dirPath.string());
const String& pmId,
const fs::path& dirPath,
const Option<String>& fileExtensionFilter,
const bool subtractOne
) -> Result<u64> {
using package::PkgCountCacheData;
std::error_code errc;
std::error_code fsErrCode;
if (!fs::exists(dirPath, errc)) {
if (errc)
warn_log("Filesystem error checking {} directory '{}': {}", pmId, dirPath.string(), errc.message());
if (Result<PkgCountCacheData> cachedDataResult = ReadCache<PkgCountCacheData>(pmId)) {
const auto& [cachedCount, timestamp] = *cachedDataResult;
return Err(DracError(DracErrorCode::NotFound, std::format("{} directory not found: {}", pmId, dirPath.string())));
}
if (!fs::exists(dirPath, fsErrCode) || fsErrCode)
warn_log(
"Error checking existence for directory '{}' before cache validation: {}, Invalidating {} cache",
dirPath.string(),
fsErrCode.message(),
pmId
);
else {
fsErrCode.clear();
const fs::file_time_type dirModTime = fs::last_write_time(dirPath, fsErrCode);
errc.clear();
if (fsErrCode)
warn_log(
"Could not get modification time for directory '{}': {}. Invalidating {} cache",
dirPath.string(),
fsErrCode.message(),
pmId
);
else {
if (const system_clock::time_point cacheTimePoint = system_clock::time_point(seconds(timestamp));
cacheTimePoint.time_since_epoch() >= dirModTime.time_since_epoch()) {
debug_log(
"Using valid {} directory count cache (Dir '{}' unchanged since {}). Count: {}",
pmId,
dirPath.string(),
std::format("{:%F %T %Z}", floor<seconds>(cacheTimePoint)),
cachedCount
);
return cachedCount;
}
}
}
} else if (cachedDataResult.error().code != DracErrorCode::NotFound) {
debug_at(cachedDataResult.error());
} else
debug_log("{} directory count cache not found or unreadable", pmId, pmId);
if (!fs::is_directory(dirPath, errc)) {
if (errc)
fsErrCode.clear();
if (!fs::is_directory(dirPath, fsErrCode)) {
if (fsErrCode)
return Err(DracError(
DracErrorCode::IoError,
std::format("Filesystem error checking if '{}' is a directory: {}", dirPath.string(), errc.message())
std::format("Filesystem error checking if '{}' is a directory: {}", dirPath.string(), fsErrCode.message())
));
return Err(
DracError(DracErrorCode::IoError, std::format("{} path is not a directory: {}", pmId, dirPath.string()))
);
}
fsErrCode.clear();
errc.clear();
u64 count = 0;
bool filterActive = !fileExtensionFilter.empty();
u64 count = 0;
try {
const fs::directory_iterator dirIter(dirPath, fs::directory_options::skip_permission_denied, errc);
const fs::directory_iterator dirIter(dirPath, fs::directory_options::skip_permission_denied, fsErrCode);
if (errc) {
if (fsErrCode)
return Err(DracError(
DracErrorCode::IoError,
std::format("Failed to create iterator for {} directory '{}': {}", pmId, dirPath.string(), errc.message())
std::format(
"Failed to create iterator for {} directory '{}': {}", pmId, dirPath.string(), fsErrCode.message()
)
));
}
errc.clear();
for (const fs::directory_entry& entry : dirIter) {
fsErrCode.clear();
if (entry.path().empty())
continue;
std::error_code entryStatErr;
bool isFile = false;
if (fileExtensionFilter) {
bool isFile = false;
isFile = entry.is_regular_file(fsErrCode);
if (filterActive) {
isFile = entry.is_regular_file(entryStatErr);
if (entryStatErr) {
warn_log(
"Error stating entry '{}' in {} directory: {}", entry.path().string(), pmId, entryStatErr.message()
);
entryStatErr.clear();
if (fsErrCode) {
warn_log("Error stating entry '{}' in {} directory: {}", entry.path().string(), pmId, fsErrCode.message());
continue;
}
if (isFile && entry.path().extension().string() == *fileExtensionFilter)
count++;
continue;
}
if (filterActive) {
if (isFile && entry.path().extension().string() == fileExtensionFilter)
count++;
} else
if (!fileExtensionFilter)
count++;
}
} catch (const fs::filesystem_error& e) {
return Err(DracError(DracErrorCode::IoError, std::format("Filesystem error during {} directory iteration", pmId))
);
} catch (const Exception& e) { return Err(DracError(DracErrorCode::InternalError, e.what())); } catch (...) {
} catch (const fs::filesystem_error& fsCatchErr) {
return Err(DracError(
DracErrorCode::IoError,
std::format("Filesystem error during {} directory iteration: {}", pmId, fsCatchErr.what())
));
} catch (const Exception& exc) { return Err(DracError(DracErrorCode::InternalError, exc.what())); } catch (...) {
return Err(DracError(DracErrorCode::Other, std::format("Unknown error iterating {} directory", pmId)));
}
if (subtractOne && count > 0)
count--;
debug_log("Successfully counted {} packages for '{}': {}", std::to_string(count), pmId, dirPath.string());
if (count == 0)
return Err(DracError(DracErrorCode::NotFound, std::format("No packages found in {} directory", pmId)));
const i64 nowEpochSeconds = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
const PkgCountCacheData dataToCache = { .count = count, .timestampEpochSeconds = nowEpochSeconds };
if (Result writeResult = WriteCache(pmId, dataToCache); !writeResult)
error_at(writeResult.error());
return count;
}
} // namespace
@ -120,32 +158,29 @@ namespace package {
const fs::path& dirPath,
const String& fileExtensionFilter,
const bool subtractOne
) -> Result<u64, DracError> {
) -> Result<u64> {
return GetCountFromDirectoryImpl(pmId, dirPath, fileExtensionFilter, subtractOne);
}
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath, const String& fileExtensionFilter)
-> Result<u64, DracError> {
-> Result<u64> {
return GetCountFromDirectoryImpl(pmId, dirPath, fileExtensionFilter, false);
}
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath, const bool subtractOne)
-> Result<u64, DracError> {
const String noFilter;
return GetCountFromDirectoryImpl(pmId, dirPath, noFilter, subtractOne);
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath, const bool subtractOne) -> Result<u64> {
return GetCountFromDirectoryImpl(pmId, dirPath, None, subtractOne);
}
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath) -> Result<u64, DracError> {
const String noFilter;
return GetCountFromDirectoryImpl(pmId, dirPath, noFilter, false);
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath) -> Result<u64> {
return GetCountFromDirectoryImpl(pmId, dirPath, None, false);
}
#ifndef __serenity__
fn GetCountFromDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError> {
#if !defined(__serenity__) && !defined(_WIN32)
fn GetCountFromDb(const PackageManagerInfo& pmInfo) -> Result<u64> {
const auto& [pmId, dbPath, countQuery] = pmInfo;
const String cacheKey = "pkg_count_" + pmId; // More specific cache key
if (Result<PkgCountCacheData, DracError> cachedDataResult = ReadCache<PkgCountCacheData>(cacheKey)) {
if (Result<PkgCountCacheData> cachedDataResult = ReadCache<PkgCountCacheData>(cacheKey)) {
const auto& [count, timestamp] = *cachedDataResult;
std::error_code errc;
const std::filesystem::file_time_type dbModTime = fs::last_write_time(dbPath, errc);
@ -177,7 +212,6 @@ namespace package {
u64 count = 0;
try {
// Ensure database file exists before trying to open
std::error_code existsErr;
if (!fs::exists(dbPath, existsErr) || existsErr) {
if (existsErr) {
@ -198,20 +232,13 @@ namespace package {
DracError(DracErrorCode::ParseError, std::format("Negative count returned by {} DB COUNT query.", pmId))
);
count = static_cast<u64>(countInt64);
} else {
// It's possible a query legitimately returns 0 rows (e.g., no packages)
debug_log("No rows returned by {} DB COUNT query for '{}', assuming count is 0.", pmId, dbPath.string());
count = 0;
// return Err(DracError(DracErrorCode::ParseError, std::format("No rows returned by {} DB COUNT query.",
// pmId)));
}
} else
return Err(DracError(DracErrorCode::ParseError, std::format("No rows returned by {} DB COUNT query.", pmId)));
} catch (const SQLite::Exception& e) {
// Log specific SQLite errors but return a more general error type
error_log("SQLite error occurred accessing {} DB '{}': {}", pmId, dbPath.string(), e.what());
return Err(DracError(
DracErrorCode::ApiUnavailable, // Or IoError?
std::format("Failed to query {} database: {}", pmId, dbPath.string())
));
return Err(
DracError(DracErrorCode::ApiUnavailable, std::format("Failed to query {} database: {}", pmId, dbPath.string()))
);
} catch (const Exception& e) {
error_log("Standard exception accessing {} DB '{}': {}", pmId, dbPath.string(), e.what());
return Err(DracError(DracErrorCode::InternalError, e.what()));
@ -225,15 +252,15 @@ namespace package {
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(cacheKey, dataToCache); !writeResult)
error_at(writeResult.error()); // Log cache write error but return the count anyway
if (Result writeResult = WriteCache(cacheKey, dataToCache); !writeResult)
error_at(writeResult.error());
return count;
}
#endif // __serenity__
#if defined(__linux__) || defined(__APPLE__)
fn GetNixCount() -> Result<u64, DracError> {
fn GetNixCount() -> Result<u64> {
const PackageManagerInfo nixInfo = {
.id = "nix",
.dbPath = "/nix/var/nix/db/db.sqlite",
@ -253,14 +280,14 @@ namespace package {
}
#endif // __linux__ || __APPLE__
fn GetCargoCount() -> Result<u64, DracError> {
fn CountCargo() -> Result<u64> {
using util::helpers::GetEnv;
fs::path cargoPath {};
if (const Result<String, DracError> cargoHome = GetEnv("CARGO_HOME"))
if (const Result<String> cargoHome = GetEnv("CARGO_HOME"))
cargoPath = fs::path(*cargoHome) / "bin";
else if (const Result<String, DracError> homeDir = GetEnv("HOME"))
else if (const Result<String> homeDir = GetEnv("HOME"))
cargoPath = fs::path(*homeDir) / ".cargo" / "bin";
if (cargoPath.empty() || !fs::exists(cargoPath))
@ -277,8 +304,8 @@ namespace package {
return count;
}
fn GetTotalCount() -> Result<u64, DracError> {
Vec<Future<Result<u64, DracError>>> futures;
fn GetTotalCount() -> Result<u64> {
Vec<Future<Result<u64>>> futures;
#ifdef __linux__
futures.push_back(std::async(std::launch::async, GetDpkgCount));
@ -292,9 +319,9 @@ namespace package {
futures.push_back(std::async(std::launch::async, GetHomebrewCount));
futures.push_back(std::async(std::launch::async, GetMacPortsCount));
#elifdef _WIN32
futures.push_back(std::async(std::launch::async, GetWinRTCount));
futures.push_back(std::async(std::launch::async, GetChocolateyCount));
futures.push_back(std::async(std::launch::async, GetScoopCount));
futures.push_back(std::async(std::launch::async, CountWinGet));
futures.push_back(std::async(std::launch::async, CountChocolatey));
futures.push_back(std::async(std::launch::async, CountScoop));
#elif defined(__FreeBSD__) || defined(__DragonFly__)
futures.push_back(std::async(std::launch::async, GetPkgNgCount));
#elifdef __NetBSD__
@ -307,27 +334,27 @@ namespace package {
#if defined(__linux__) || defined(__APPLE__)
futures.push_back(std::async(std::launch::async, GetNixCount));
#endif // __linux__ || __APPLE__
futures.push_back(std::async(std::launch::async, GetCargoCount));
#endif
futures.push_back(std::async(std::launch::async, CountCargo));
u64 totalCount = 0;
bool oneSucceeded = false;
for (Future<Result<u64, DracError>>& fut : futures) {
for (Future<Result<u64>>& fut : futures) {
try {
if (Result<u64, DracError> result = fut.get()) {
using enum util::error::DracErrorCode;
if (Result<u64> result = fut.get()) {
totalCount += *result;
oneSucceeded = true;
debug_log("Added {} packages. Current total: {}", *result, totalCount);
} else {
if (result.error().code != DracErrorCode::NotFound && result.error().code != DracErrorCode::ApiUnavailable &&
result.error().code != DracErrorCode::NotSupported) {
error_at(result.error());
} else
debug_at(result.error());
}
} catch (const Exception& e) {
error_log("Caught exception while getting package count future: {}", e.what());
} else if (result.error().code != NotFound && result.error().code != ApiUnavailable &&
result.error().code != NotSupported) {
error_at(result.error());
} else
debug_at(result.error());
} catch (const Exception& exc) {
error_log("Caught exception while getting package count future: {}", exc.what());
} catch (...) { error_log("Caught unknown exception while getting package count future."); }
}

View file

@ -4,10 +4,9 @@
#include <glaze/core/common.hpp> // glz::object
#include <glaze/core/meta.hpp> // glz::detail::Object
// Include necessary type headers used in declarations
#include "src/util/defs.hpp"
#include "src/util/error.hpp"
#include "src/util/types.hpp" // Brings in Result, u64, etc.
#include "src/util/types.hpp"
namespace package {
namespace fs = std::filesystem;
@ -51,14 +50,14 @@ namespace package {
* @return Result containing the total package count (u64) on success,
* or a DracError if aggregation fails (individual errors logged).
*/
fn GetTotalCount() -> Result<u64, DracError>;
fn GetTotalCount() -> Result<u64>;
/**
* @brief Gets package count from a database using SQLite.
* @param pmInfo Information about the package manager database.
* @return Result containing the count (u64) or a DracError.
*/
fn GetCountFromDb(const PackageManagerInfo& pmInfo) -> Result<u64, DracError>;
fn GetCountFromDb(const PackageManagerInfo& pmInfo) -> Result<u64>;
/**
* @brief Gets package count by iterating entries in a directory, optionally filtering and subtracting.
@ -73,7 +72,7 @@ namespace package {
const fs::path& dirPath,
const String& fileExtensionFilter,
bool subtractOne
) -> Result<u64, DracError>;
) -> Result<u64>;
/**
* @brief Gets package count by iterating entries in a directory, filtering by extension.
@ -83,7 +82,7 @@ namespace package {
* @return Result containing the count (u64) or a DracError. Defaults subtractOne to false.
*/
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath, const String& fileExtensionFilter)
-> Result<u64, DracError>;
-> Result<u64>;
/**
* @brief Gets package count by iterating entries in a directory, optionally subtracting one.
@ -92,7 +91,7 @@ namespace package {
* @param subtractOne Subtract one from the final count.
* @return Result containing the count (u64) or a DracError. Defaults fileExtensionFilter to "".
*/
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath, bool subtractOne) -> Result<u64, DracError>;
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath, bool subtractOne) -> Result<u64>;
/**
* @brief Gets package count by iterating all entries in a directory.
@ -100,36 +99,36 @@ namespace package {
* @param dirPath Path to the directory to iterate.
* @return Result containing the count (u64) or a DracError. Defaults filter to "" and subtractOne to false.
*/
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath) -> Result<u64, DracError>;
fn GetCountFromDirectory(const String& pmId, const fs::path& dirPath) -> Result<u64>;
#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>;
fn GetDpkgCount() -> Result<u64>;
fn GetPacmanCount() -> Result<u64>;
fn GetMossCount() -> Result<u64>;
fn GetRpmCount() -> Result<u64>;
fn GetZypperCount() -> Result<u64>;
fn GetPortageCount() -> Result<u64>;
fn GetApkCount() -> Result<u64>;
#elifdef __APPLE__
fn GetHomebrewCount() -> Result<u64, DracError>;
fn GetMacPortsCount() -> Result<u64, DracError>;
fn GetHomebrewCount() -> Result<u64>;
fn GetMacPortsCount() -> Result<u64>;
#elifdef _WIN32
fn GetWinRTCount() -> Result<u64, DracError>;
fn GetChocolateyCount() -> Result<u64, DracError>;
fn GetScoopCount() -> Result<u64, DracError>;
fn CountWinGet() -> Result<u64>;
fn CountChocolatey() -> Result<u64>;
fn CountScoop() -> Result<u64>;
#elif defined(__FreeBSD__) || defined(__DragonFly__)
fn GetPkgNgCount() -> Result<u64, DracError>;
fn GetPkgNgCount() -> Result<u64>;
#elifdef __NetBSD__
fn GetPkgSrcCount() -> Result<u64, DracError>;
fn GetPkgSrcCount() -> Result<u64>;
#elifdef __HAIKU__
fn GetHaikuCount() -> Result<u64, DracError>;
fn GetHaikuCount() -> Result<u64>;
#elifdef __serenity__
fn GetSerenityCount() -> Result<u64, DracError>;
fn GetSerenityCount() -> Result<u64>;
#endif
// Common (potentially cross-platform)
#ifndef _WIN32
fn GetNixCount() -> Result<u64, DracError>;
fn GetNixCount() -> Result<u64>;
#endif
fn GetCargoCount() -> Result<u64, DracError>;
fn CountCargo() -> Result<u64>;
} // namespace package

View file

@ -30,7 +30,7 @@ namespace {
}
}
fn getDate() -> Result<String, DracError> {
fn getDate() -> Result<String> {
using std::chrono::system_clock;
using util::types::String, util::types::usize, util::types::Err;
@ -68,21 +68,20 @@ namespace {
namespace os {
SystemData::SystemData(const Config& config) {
using enum std::launch;
using package::GetTotalCount;
using util::types::Future, util::types::Err;
using weather::Output;
Future<Result<String, DracError>> hostFut = std::async(async, GetHost);
Future<Result<String, DracError>> kernelFut = std::async(async, GetKernelVersion);
Future<Result<String, DracError>> osFut = std::async(async, GetOSVersion);
Future<Result<u64, DracError>> memFut = std::async(async, GetMemInfo);
Future<Result<String, DracError>> deFut = std::async(async, GetDesktopEnvironment);
Future<Result<String, DracError>> wmFut = std::async(async, GetWindowManager);
Future<Result<DiskSpace, DracError>> diskFut = std::async(async, GetDiskUsage);
Future<Result<String, DracError>> shellFut = std::async(async, GetShell);
Future<Result<u64, DracError>> pkgFut = std::async(async, package::GetTotalCount);
Future<Result<MediaInfo, DracError>> npFut =
std::async(config.nowPlaying.enabled ? async : deferred, GetNowPlaying);
Future<Result<Output, DracError>> wthrFut =
Future<Result<String>> hostFut = std::async(async, GetHost);
Future<Result<String>> kernelFut = std::async(async, GetKernelVersion);
Future<Result<String>> osFut = std::async(async, GetOSVersion);
Future<Result<u64>> memFut = std::async(async, GetMemInfo);
Future<Result<String>> deFut = std::async(async, GetDesktopEnvironment);
Future<Result<String>> wmFut = std::async(async, GetWindowManager);
Future<Result<DiskSpace>> diskFut = std::async(async, GetDiskUsage);
Future<Result<String>> shellFut = std::async(async, GetShell);
Future<Result<u64>> pkgFut = std::async(async, GetTotalCount);
Future<Result<MediaInfo>> npFut = std::async(config.nowPlaying.enabled ? async : deferred, GetNowPlaying);
Future<Result<weather::Output>> wthrFut =
std::async(config.weather.enabled ? async : deferred, [&config] { return config.weather.getWeatherInfo(); });
this->date = getDate();

View file

@ -34,7 +34,7 @@ constexpr u64 GIB = 1'073'741'824;
*
* @code{.cpp}
* #include <format>
* #include "system_data.h" // Assuming BytesToGiB is defined here
* #include "system_data.h"
*
* i32 main() {
* BytesToGiB data_size{2'147'483'648}; // 2 GiB
@ -66,18 +66,18 @@ namespace os {
* in order to display it at all at once during runtime.
*/
struct SystemData {
Result<String, DracError> date; ///< Current date (e.g., "April 26th").
Result<String, DracError> host; ///< Host/product family (e.g., "MacBook Air").
Result<String, DracError> kernelVersion; ///< OS kernel version (e.g., "6.14.4").
Result<String, DracError> osVersion; ///< OS pretty name (e.g., "Ubuntu 24.04.2 LTS").
Result<u64, DracError> memInfo; ///< Total physical RAM in bytes.
Result<String, DracError> desktopEnv; ///< Desktop environment (e.g., "KDE").
Result<String, DracError> windowMgr; ///< Window manager (e.g., "KWin").
Result<DiskSpace, DracError> diskUsage; ///< Used/Total disk space for root filesystem.
Result<String, DracError> shell; ///< Name of the current user shell (e.g., "zsh").
Result<u64, DracError> packageCount; ///< Total number of packages installed.
Result<MediaInfo, DracError> nowPlaying; ///< Result of fetching media info.
Result<weather::Output, DracError> weather; ///< Result of fetching weather info.
Result<String> date; ///< Current date (e.g., "April 26th").
Result<String> host; ///< Host/product family (e.g., "MacBook Air").
Result<String> kernelVersion; ///< OS kernel version (e.g., "6.14.4").
Result<String> osVersion; ///< OS pretty name (e.g., "Ubuntu 24.04.2 LTS").
Result<u64> memInfo; ///< Total physical RAM in bytes.
Result<String> desktopEnv; ///< Desktop environment (e.g., "KDE").
Result<String> windowMgr; ///< Window manager (e.g., "KWin").
Result<DiskSpace> diskUsage; ///< Used/Total disk space for root filesystem.
Result<String> shell; ///< Name of the current user shell (e.g., "zsh").
Result<u64> packageCount; ///< Total number of packages installed.
Result<MediaInfo> nowPlaying; ///< Result of fetching media info.
Result<weather::Output> weather; ///< Result of fetching weather info.
/**
* @brief Constructs a SystemData object and initializes its members.

View file

@ -1,13 +1,17 @@
#include <cmath> // std::lround
#include <format> // std::format
#include <ftxui/dom/elements.hpp> // ftxui::{Element, hbox, vbox, text, separator, filler, etc.}
#include <ftxui/dom/node.hpp> // ftxui::{Render}
#include <ftxui/screen/color.hpp> // ftxui::Color
#include <ftxui/screen/screen.hpp> // ftxui::{Screen, Dimension::Full}
#include <ftxui/screen/string.hpp> // ftxui::string_width
#include <iostream> // std::cout
#include <ranges> // std::ranges::{iota, to, transform}
#ifdef __cpp_lib_print
#include <print> // std::print
#else
#include <iostream> // std::cout
#endif
#include "src/config/config.hpp"
#include "src/config/weather.hpp"
#include "src/core/system_data.hpp"
@ -116,7 +120,7 @@ namespace {
};
fn CreateColorCircles() -> Element {
fn colorView =
auto colorView =
std::views::iota(0, 16) | std::views::transform([](i32 colorIndex) {
return ftxui::hbox(
{ ftxui::text("") | ftxui::bold | ftxui::color(static_cast<ftxui::Color::Palette256>(colorIndex)),
@ -124,9 +128,7 @@ namespace {
);
});
Elements elementsContainer(std::ranges::begin(colorView), std::ranges::end(colorView));
return hbox(elementsContainer);
return hbox(Elements(std::ranges::begin(colorView), std::ranges::end(colorView)));
}
fn get_visual_width(const String& str) -> usize { return ftxui::string_width(str); }
@ -151,56 +153,64 @@ namespace {
std::vector<RowInfo> envInfoRows; // DE, WM
if (data.date)
initialRows.push_back({ calendarIcon, "Date", *data.date });
initialRows.push_back({ .icon = calendarIcon, .label = "Date", .value = *data.date });
if (weather.enabled && data.weather) {
const weather::Output& weatherInfo = *data.weather;
String weatherValue = weather.showTownName
? std::format("{}°F in {}", std::lround(weatherInfo.main.temp), weatherInfo.name)
: std::format("{}°F, {}", std::lround(weatherInfo.main.temp), weatherInfo.weather[0].description);
initialRows.push_back({ weatherIcon, "Weather", std::move(weatherValue) });
}
initialRows.push_back({ .icon = weatherIcon, .label = "Weather", .value = std::move(weatherValue) });
} else if (weather.enabled && !data.weather.has_value())
debug_at(data.weather.error());
if (data.host && !data.host->empty())
systemInfoRows.push_back({ hostIcon, "Host", *data.host });
systemInfoRows.push_back({ .icon = hostIcon, .label = "Host", .value = *data.host });
if (data.osVersion)
systemInfoRows.push_back({ osIcon, "OS", *data.osVersion });
systemInfoRows.push_back({ .icon = osIcon, .label = "OS", .value = *data.osVersion });
if (data.kernelVersion)
systemInfoRows.push_back({ kernelIcon, "Kernel", *data.kernelVersion });
systemInfoRows.push_back({ .icon = kernelIcon, .label = "Kernel", .value = *data.kernelVersion });
if (data.memInfo)
systemInfoRows.push_back({ memoryIcon, "RAM", std::format("{}", BytesToGiB { *data.memInfo }) });
systemInfoRows.push_back(
{ .icon = memoryIcon, .label = "RAM", .value = std::format("{}", BytesToGiB { *data.memInfo }) }
);
else if (!data.memInfo.has_value())
debug_at(data.memInfo.error());
if (data.diskUsage)
systemInfoRows.push_back(
{ diskIcon,
"Disk",
std::format("{}/{}", BytesToGiB { data.diskUsage->used_bytes }, BytesToGiB { data.diskUsage->total_bytes }) }
{
.icon = diskIcon,
.label = "Disk",
.value =
std::format("{}/{}", BytesToGiB { data.diskUsage->usedBytes }, BytesToGiB { data.diskUsage->totalBytes }),
}
);
if (data.shell)
systemInfoRows.push_back({ shellIcon, "Shell", *data.shell });
systemInfoRows.push_back({ .icon = shellIcon, .label = "Shell", .value = *data.shell });
if (data.packageCount) {
if (*data.packageCount > 0)
systemInfoRows.push_back({ packageIcon, "Packages", std::format("{}", *data.packageCount) });
systemInfoRows.push_back(
{ .icon = packageIcon, .label = "Packages", .value = std::format("{}", *data.packageCount) }
);
else
debug_log("Package count is 0, skipping");
}
bool addedDe = false;
if (data.desktopEnv && (!data.windowMgr || *data.desktopEnv != *data.windowMgr)) {
envInfoRows.push_back({ deIcon, "DE", *data.desktopEnv });
envInfoRows.push_back({ .icon = deIcon, .label = "DE", .value = *data.desktopEnv });
addedDe = true;
}
if (data.windowMgr) {
if (!addedDe || (data.desktopEnv && *data.desktopEnv != *data.windowMgr))
envInfoRows.push_back({ wmIcon, "WM", *data.windowMgr });
envInfoRows.push_back({ .icon = wmIcon, .label = "WM", .value = *data.windowMgr });
}
bool nowPlayingActive = false;
@ -215,22 +225,23 @@ namespace {
usize maxContentWidth = 0;
usize greetingWidth = get_visual_width_sv(userIcon) + get_visual_width_sv("Hello ") + get_visual_width(name) +
const usize greetingWidth = get_visual_width_sv(userIcon) + get_visual_width_sv("Hello ") + get_visual_width(name) +
get_visual_width_sv("! ");
maxContentWidth = std::max(maxContentWidth, greetingWidth);
usize paletteWidth = get_visual_width_sv(userIcon) + (16 * (get_visual_width_sv("") + get_visual_width_sv(" ")));
maxContentWidth = std::max(maxContentWidth, paletteWidth);
const usize paletteWidth =
get_visual_width_sv(userIcon) + (16 * (get_visual_width_sv("") + get_visual_width_sv(" ")));
maxContentWidth = std::max(maxContentWidth, paletteWidth);
usize iconActualWidth = get_visual_width_sv(userIcon);
const usize iconActualWidth = get_visual_width_sv(userIcon);
usize maxLabelWidthInitial = find_max_label_len(initialRows);
usize maxLabelWidthSystem = find_max_label_len(systemInfoRows);
usize maxLabelWidthEnv = find_max_label_len(envInfoRows);
const usize maxLabelWidthInitial = find_max_label_len(initialRows);
const usize maxLabelWidthSystem = find_max_label_len(systemInfoRows);
const usize maxLabelWidthEnv = find_max_label_len(envInfoRows);
usize requiredWidthInitialW = iconActualWidth + maxLabelWidthInitial;
usize requiredWidthSystemW = iconActualWidth + maxLabelWidthSystem;
usize requiredWidthEnvW = iconActualWidth + maxLabelWidthEnv;
const usize requiredWidthInitialW = iconActualWidth + maxLabelWidthInitial;
const usize requiredWidthSystemW = iconActualWidth + maxLabelWidthSystem;
const usize requiredWidthEnvW = iconActualWidth + maxLabelWidthEnv;
fn calculateRowVisualWidth = [&](const RowInfo& row, const usize requiredLabelVisualWidth) -> usize {
return requiredLabelVisualWidth + get_visual_width(row.value) + get_visual_width_sv(" ");
@ -245,7 +256,7 @@ namespace {
for (const RowInfo& row : envInfoRows)
maxContentWidth = std::max(maxContentWidth, calculateRowVisualWidth(row, requiredWidthEnvW));
usize targetBoxWidth = maxContentWidth + 2;
const usize targetBoxWidth = maxContentWidth + 2;
usize npFixedWidthLeft = 0;
usize npFixedWidthRight = 0;
@ -289,9 +300,9 @@ namespace {
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
content.push_back(hbox({ text(String(paletteIcon)) | color(ui::DEFAULT_THEME.icon), CreateColorCircles() }));
bool section1Present = !initialRows.empty();
bool section2Present = !systemInfoRows.empty();
bool section3Present = !envInfoRows.empty();
const bool section1Present = !initialRows.empty();
const bool section2Present = !systemInfoRows.empty();
const bool section3Present = !envInfoRows.empty();
if (section1Present)
content.push_back(separator() | color(ui::DEFAULT_THEME.border));
@ -342,7 +353,11 @@ fn main() -> i32 {
Render(screen, document);
screen.Print();
#ifdef __cpp_lib_print
std::println();
#else
std::cout << '\n';
#endif
return 0;
}

View file

@ -33,7 +33,7 @@ using util::error::DracError, util::error::DracErrorCode;
namespace {
#ifdef __FreeBSD__
fn GetPathByPid(pid_t pid) -> Result<String, DracError> {
fn GetPathByPid(pid_t pid) -> Result<String> {
Array<char, PATH_MAX> exePathBuf;
usize size = exePathBuf.size();
Array<i32, 4> mib;
@ -57,7 +57,7 @@ namespace {
}
#endif
fn GetX11WindowManager() -> Result<String, DracError> {
fn GetX11WindowManager() -> Result<String> {
using namespace xcb;
const DisplayGuard conn;
@ -81,7 +81,7 @@ namespace {
return std::format("Unknown Error Code ({})", err);
}()));
fn internAtom = [&conn](const StringView name) -> Result<atom_t, DracError> {
fn internAtom = [&conn](const StringView name) -> Result<atom_t> {
const ReplyGuard<intern_atom_reply_t> reply(
intern_atom_reply(conn.get(), intern_atom(conn.get(), 0, static_cast<u16>(name.size()), name.data()), nullptr)
);
@ -94,9 +94,9 @@ namespace {
return reply->atom;
};
const Result<atom_t, DracError> supportingWmCheckAtom = internAtom("_NET_SUPPORTING_WM_CHECK");
const Result<atom_t, DracError> wmNameAtom = internAtom("_NET_WM_NAME");
const Result<atom_t, DracError> utf8StringAtom = internAtom("UTF8_STRING");
const Result<atom_t> supportingWmCheckAtom = internAtom("_NET_SUPPORTING_WM_CHECK");
const Result<atom_t> wmNameAtom = internAtom("_NET_WM_NAME");
const Result<atom_t> utf8StringAtom = internAtom("UTF8_STRING");
if (!supportingWmCheckAtom || !wmNameAtom || !utf8StringAtom) {
if (!supportingWmCheckAtom)
@ -136,7 +136,7 @@ namespace {
return String(nameData, length);
}
fn GetWaylandCompositor() -> Result<String, DracError> {
fn GetWaylandCompositor() -> Result<String> {
#ifndef __FreeBSD__
return "Wayland Compositor";
#else
@ -163,7 +163,7 @@ namespace {
if (peerPid <= 0)
return Err(DracError(DracErrorCode::PlatformSpecific, "Failed to obtain a valid peer PID"));
Result<String, DracError> exePathResult = GetPathByPid(peerPid);
Result<String> exePathResult = GetPathByPid(peerPid);
if (!exePathResult)
return Err(std::move(exePathResult).error());
@ -199,7 +199,7 @@ namespace {
namespace os {
using util::helpers::GetEnv;
fn GetOSVersion() -> Result<String, DracError> {
fn GetOSVersion() -> Result<String> {
constexpr CStr path = "/etc/os-release";
std::ifstream file(path);
@ -234,7 +234,7 @@ namespace os {
return osName;
}
fn GetMemInfo() -> Result<u64, DracError> {
fn GetMemInfo() -> Result<u64> {
u64 mem = 0;
usize size = sizeof(mem);
@ -247,10 +247,10 @@ namespace os {
return mem;
}
fn GetNowPlaying() -> Result<MediaInfo, DracError> {
fn GetNowPlaying() -> Result<MediaInfo> {
using namespace dbus;
Result<Connection, DracError> connectionResult = Connection::busGet(DBUS_BUS_SESSION);
Result<Connection> connectionResult = Connection::busGet(DBUS_BUS_SESSION);
if (!connectionResult)
return Err(connectionResult.error());
@ -259,12 +259,12 @@ namespace os {
Option<String> activePlayer = None;
{
Result<Message, DracError> listNamesResult =
Result<Message> listNamesResult =
Message::newMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
if (!listNamesResult)
return Err(listNamesResult.error());
Result<Message, DracError> listNamesReplyResult = connection.sendWithReplyAndBlock(*listNamesResult, 100);
Result<Message> listNamesReplyResult = connection.sendWithReplyAndBlock(*listNamesResult, 100);
if (!listNamesReplyResult)
return Err(listNamesReplyResult.error());
@ -292,7 +292,7 @@ namespace os {
if (!activePlayer)
return Err(DracError(DracErrorCode::NotFound, "No active MPRIS players found"));
Result<Message, DracError> msgResult = Message::newMethodCall(
Result<Message> msgResult = Message::newMethodCall(
activePlayer->c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"
);
@ -304,7 +304,7 @@ namespace os {
if (!msg.appendArgs("org.mpris.MediaPlayer2.Player", "Metadata"))
return Err(DracError(DracErrorCode::InternalError, "Failed to append arguments to Properties.Get message"));
Result<Message, DracError> replyResult = connection.sendWithReplyAndBlock(msg, 100);
Result<Message> replyResult = connection.sendWithReplyAndBlock(msg, 100);
if (!replyResult)
return Err(replyResult.error());
@ -378,20 +378,20 @@ namespace os {
return MediaInfo(std::move(title), std::move(artist));
}
fn GetWindowManager() -> Result<String, DracError> {
fn GetWindowManager() -> Result<String> {
if (!GetEnv("DISPLAY") && !GetEnv("WAYLAND_DISPLAY") && !GetEnv("XDG_SESSION_TYPE"))
return Err(DracError(DracErrorCode::NotFound, "Could not find a graphical session"));
if (Result<String, DracError> waylandResult = GetWaylandCompositor())
if (Result<String> waylandResult = GetWaylandCompositor())
return *waylandResult;
if (Result<String, DracError> x11Result = GetX11WindowManager())
if (Result<String> x11Result = GetX11WindowManager())
return *x11Result;
return Err(DracError(DracErrorCode::NotFound, "Could not detect window manager (Wayland/X11) or both failed"));
}
fn GetDesktopEnvironment() -> Result<String, DracError> {
fn GetDesktopEnvironment() -> Result<String> {
if (!GetEnv("DISPLAY") && !GetEnv("WAYLAND_DISPLAY") && !GetEnv("XDG_SESSION_TYPE"))
return Err(DracError(DracErrorCode::NotFound, "Could not find a graphical session"));
@ -402,11 +402,11 @@ namespace os {
return xdgDesktop;
})
.or_else([](const DracError&) -> Result<String, DracError> { return GetEnv("DESKTOP_SESSION"); });
.or_else([](const DracError&) -> Result<String> { return GetEnv("DESKTOP_SESSION"); });
}
fn GetShell() -> Result<String, DracError> {
if (const Result<String, DracError> shellPath = GetEnv("SHELL")) {
fn GetShell() -> Result<String> {
if (const Result<String> shellPath = GetEnv("SHELL")) {
// clang-format off
constexpr Array<Pair<StringView, StringView>, 5> shellMap {{
{ "bash", "Bash" },
@ -427,7 +427,7 @@ namespace os {
return Err(DracError(DracErrorCode::NotFound, "Could not find SHELL environment variable"));
}
fn GetHost() -> Result<String, DracError> {
fn GetHost() -> Result<String> {
Array<char, 256> buffer {};
usize size = buffer.size();
@ -459,7 +459,7 @@ namespace os {
return String(buffer.data());
}
fn GetKernelVersion() -> Result<String, DracError> {
fn GetKernelVersion() -> Result<String> {
utsname uts;
if (uname(&uts) == -1)
@ -471,7 +471,7 @@ namespace os {
return uts.release;
}
fn GetDiskUsage() -> Result<DiskSpace, DracError> {
fn GetDiskUsage() -> Result<DiskSpace> {
struct statvfs stat;
if (statvfs("/", &stat) == -1)
@ -486,11 +486,11 @@ namespace os {
namespace package {
#ifdef __NetBSD__
fn GetPkgSrcCount() -> Result<u64, DracError> {
fn GetPkgSrcCount() -> Result<u64> {
return GetCountFromDirectory("pkgsrc", fs::current_path().root_path() / "usr" / "pkg" / "pkgdb", true);
}
#else
fn GetPkgNgCount() -> Result<u64, DracError> {
fn GetPkgNgCount() -> Result<u64> {
const PackageManagerInfo pkgInfo = {
.id = "pkgng",
.dbPath = "/var/db/pkg/local.sqlite",

View file

@ -31,7 +31,7 @@ using util::error::DracError, util::error::DracErrorCode;
using util::helpers::GetEnv;
namespace os {
fn GetOSVersion() -> Result<String, DracError> {
fn GetOSVersion() -> Result<String> {
BFile file;
status_t status = file.SetTo("/boot/system/lib/libbe.so", B_READ_ONLY);
@ -58,7 +58,7 @@ namespace os {
return std::format("Haiku {}", versionShortString);
}
fn GetMemInfo() -> Result<u64, DracError> {
fn GetMemInfo() -> Result<u64> {
system_info sysinfo;
const status_t status = get_system_info(&sysinfo);
@ -68,16 +68,16 @@ namespace os {
return static_cast<u64>(sysinfo.max_pages) * B_PAGE_SIZE;
}
fn GetNowPlaying() -> Result<MediaInfo, DracError> {
fn GetNowPlaying() -> Result<MediaInfo> {
return Err(DracError(DracErrorCode::NotSupported, "Now playing is not supported on Haiku"));
}
fn GetWindowManager() -> Result<String, DracError> { return "app_server"; }
fn GetWindowManager() -> Result<String> { return "app_server"; }
fn GetDesktopEnvironment() -> Result<String, DracError> { return "Haiku Desktop Environment"; }
fn GetDesktopEnvironment() -> Result<String> { return "Haiku Desktop Environment"; }
fn GetShell() -> Result<String, DracError> {
if (const Result<String, DracError> shellPath = GetEnv("SHELL")) {
fn GetShell() -> Result<String> {
if (const Result<String> shellPath = GetEnv("SHELL")) {
// clang-format off
constexpr Array<Pair<StringView, StringView>, 5> shellMap {{
{ "bash", "Bash" },
@ -98,7 +98,7 @@ namespace os {
return Err(DracError(DracErrorCode::NotFound, "Could not find SHELL environment variable"));
}
fn GetHost() -> Result<String, DracError> {
fn GetHost() -> Result<String> {
Array<char, HOST_NAME_MAX + 1> hostnameBuffer {};
if (gethostname(hostnameBuffer.data(), hostnameBuffer.size()) != 0)
@ -111,7 +111,7 @@ namespace os {
return String(hostnameBuffer.data(), hostnameBuffer.size());
}
fn GetKernelVersion() -> Result<String, DracError> {
fn GetKernelVersion() -> Result<String> {
system_info sysinfo;
const status_t status = get_system_info(&sysinfo);
@ -121,7 +121,7 @@ namespace os {
return std::to_string(sysinfo.kernel_version);
}
fn GetDiskUsage() -> Result<DiskSpace, DracError> {
fn GetDiskUsage() -> Result<DiskSpace> {
struct statvfs stat;
if (statvfs("/boot", &stat) == -1)
@ -135,7 +135,7 @@ namespace os {
} // namespace os
namespace package {
fn GetHaikuCount() -> Result<u64, DracError> {
fn GetHaikuCount() -> Result<u64> {
BPackageKit::BPackageRoster roster;
BPackageKit::BPackageInfoSet packageList;

View file

@ -42,7 +42,7 @@ using util::error::DracError, util::error::DracErrorCode;
using util::helpers::GetEnv;
namespace {
fn GetX11WindowManager() -> Result<String, DracError> {
fn GetX11WindowManager() -> Result<String> {
using namespace xcb;
const DisplayGuard conn;
@ -66,7 +66,7 @@ namespace {
return std::format("Unknown Error Code ({})", err);
}()));
fn internAtom = [&conn](const StringView name) -> Result<atom_t, DracError> {
fn internAtom = [&conn](const StringView name) -> Result<atom_t> {
const ReplyGuard<intern_atom_reply_t> reply(
intern_atom_reply(conn.get(), intern_atom(conn.get(), 0, static_cast<u16>(name.size()), name.data()), nullptr)
);
@ -79,9 +79,9 @@ namespace {
return reply->atom;
};
const Result<atom_t, DracError> supportingWmCheckAtom = internAtom("_NET_SUPPORTING_WM_CHECK");
const Result<atom_t, DracError> wmNameAtom = internAtom("_NET_WM_NAME");
const Result<atom_t, DracError> utf8StringAtom = internAtom("UTF8_STRING");
const Result<atom_t> supportingWmCheckAtom = internAtom("_NET_SUPPORTING_WM_CHECK");
const Result<atom_t> wmNameAtom = internAtom("_NET_WM_NAME");
const Result<atom_t> utf8StringAtom = internAtom("UTF8_STRING");
if (!supportingWmCheckAtom || !wmNameAtom || !utf8StringAtom) {
if (!supportingWmCheckAtom)
@ -121,7 +121,7 @@ namespace {
return String(nameData, length);
}
fn GetWaylandCompositor() -> Result<String, DracError> {
fn GetWaylandCompositor() -> Result<String> {
const wl::DisplayGuard display;
if (!display)
@ -194,7 +194,7 @@ namespace {
} // namespace
namespace os {
fn GetOSVersion() -> Result<String, DracError> {
fn GetOSVersion() -> Result<String> {
constexpr CStr path = "/etc/os-release";
std::ifstream file(path);
@ -225,7 +225,7 @@ namespace os {
return Err(DracError(DracErrorCode::NotFound, std::format("PRETTY_NAME line not found in {}", path)));
}
fn GetMemInfo() -> Result<u64, DracError> {
fn GetMemInfo() -> Result<u64> {
struct sysinfo info;
if (sysinfo(&info) != 0)
@ -243,10 +243,10 @@ namespace os {
return info.totalram * info.mem_unit;
}
fn GetNowPlaying() -> Result<MediaInfo, DracError> {
fn GetNowPlaying() -> Result<MediaInfo> {
using namespace dbus;
Result<Connection, DracError> connectionResult = Connection::busGet(DBUS_BUS_SESSION);
Result<Connection> connectionResult = Connection::busGet(DBUS_BUS_SESSION);
if (!connectionResult)
return Err(connectionResult.error());
@ -255,12 +255,12 @@ namespace os {
Option<String> activePlayer = None;
{
Result<Message, DracError> listNamesResult =
Result<Message> listNamesResult =
Message::newMethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
if (!listNamesResult)
return Err(listNamesResult.error());
Result<Message, DracError> listNamesReplyResult = connection.sendWithReplyAndBlock(*listNamesResult, 100);
Result<Message> listNamesReplyResult = connection.sendWithReplyAndBlock(*listNamesResult, 100);
if (!listNamesReplyResult)
return Err(listNamesReplyResult.error());
@ -288,7 +288,7 @@ namespace os {
if (!activePlayer)
return Err(DracError(DracErrorCode::NotFound, "No active MPRIS players found"));
Result<Message, DracError> msgResult = Message::newMethodCall(
Result<Message> msgResult = Message::newMethodCall(
activePlayer->c_str(), "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"
);
@ -300,7 +300,7 @@ namespace os {
if (!msg.appendArgs("org.mpris.MediaPlayer2.Player", "Metadata"))
return Err(DracError(DracErrorCode::InternalError, "Failed to append arguments to Properties.Get message"));
Result<Message, DracError> replyResult = connection.sendWithReplyAndBlock(msg, 100);
Result<Message> replyResult = connection.sendWithReplyAndBlock(msg, 100);
if (!replyResult)
return Err(replyResult.error());
@ -374,17 +374,17 @@ namespace os {
return MediaInfo(std::move(title), std::move(artist));
}
fn GetWindowManager() -> Result<String, DracError> {
if (Result<String, DracError> waylandResult = GetWaylandCompositor())
fn GetWindowManager() -> Result<String> {
if (Result<String> waylandResult = GetWaylandCompositor())
return *waylandResult;
if (Result<String, DracError> x11Result = GetX11WindowManager())
if (Result<String> x11Result = GetX11WindowManager())
return *x11Result;
return Err(DracError(DracErrorCode::NotFound, "Could not detect window manager (Wayland/X11) or both failed"));
}
fn GetDesktopEnvironment() -> Result<String, DracError> {
fn GetDesktopEnvironment() -> Result<String> {
return GetEnv("XDG_CURRENT_DESKTOP")
.transform([](String xdgDesktop) -> String {
if (const usize colon = xdgDesktop.find(':'); colon != String::npos)
@ -392,11 +392,11 @@ namespace os {
return xdgDesktop;
})
.or_else([](const DracError&) -> Result<String, DracError> { return GetEnv("DESKTOP_SESSION"); });
.or_else([](const DracError&) -> Result<String> { return GetEnv("DESKTOP_SESSION"); });
}
fn GetShell() -> Result<String, DracError> {
if (const Result<String, DracError> shellPath = GetEnv("SHELL")) {
fn GetShell() -> Result<String> {
if (const Result<String> shellPath = GetEnv("SHELL")) {
// clang-format off
constexpr Array<Pair<StringView, StringView>, 5> shellMap {{
{ "bash", "Bash" },
@ -417,11 +417,11 @@ namespace os {
return Err(DracError(DracErrorCode::NotFound, "Could not find SHELL environment variable"));
}
fn GetHost() -> Result<String, DracError> {
fn GetHost() -> Result<String> {
constexpr CStr primaryPath = "/sys/class/dmi/id/product_family";
constexpr CStr fallbackPath = "/sys/class/dmi/id/product_name";
fn readFirstLine = [&](const String& path) -> Result<String, DracError> {
fn readFirstLine = [&](const String& path) -> Result<String> {
std::ifstream file(path);
String line;
@ -438,8 +438,8 @@ namespace os {
return line;
};
return readFirstLine(primaryPath).or_else([&](const DracError& primaryError) -> Result<String, DracError> {
return readFirstLine(fallbackPath).or_else([&](const DracError& fallbackError) -> Result<String, DracError> {
return readFirstLine(primaryPath).or_else([&](const DracError& primaryError) -> Result<String> {
return readFirstLine(fallbackPath).or_else([&](const DracError& fallbackError) -> Result<String> {
return Err(DracError(
DracErrorCode::InternalError,
std::format(
@ -454,7 +454,7 @@ namespace os {
});
}
fn GetKernelVersion() -> Result<String, DracError> {
fn GetKernelVersion() -> Result<String> {
utsname uts;
if (uname(&uts) == -1)
@ -466,7 +466,7 @@ namespace os {
return uts.release;
}
fn GetDiskUsage() -> Result<DiskSpace, DracError> {
fn GetDiskUsage() -> Result<DiskSpace> {
struct statvfs stat;
if (statvfs("/", &stat) == -1)
@ -482,18 +482,18 @@ namespace os {
namespace package {
using namespace std::string_literals;
fn GetDpkgCount() -> Result<u64, DracError> {
fn GetDpkgCount() -> Result<u64> {
return GetCountFromDirectory("Dpkg", fs::current_path().root_path() / "var" / "lib" / "dpkg" / "info", ".list"s);
}
fn GetMossCount() -> Result<u64, DracError> {
fn GetMossCount() -> Result<u64> {
const PackageManagerInfo mossInfo = {
.id = "moss",
.dbPath = "/.moss/db/install",
.countQuery = "SELECT COUNT(*) FROM meta",
};
Result<u64, DracError> countResult = GetCountFromDb(mossInfo);
Result<u64> countResult = GetCountFromDb(mossInfo);
if (countResult)
if (*countResult > 0)
@ -502,7 +502,7 @@ namespace package {
return countResult;
}
fn GetPacmanCount() -> Result<u64, DracError> {
fn GetPacmanCount() -> Result<u64> {
return GetCountFromDirectory("Pacman", fs::current_path().root_path() / "var" / "lib" / "pacman" / "local", true);
}
} // namespace package

View file

@ -15,7 +15,7 @@
using namespace util::types;
using util::error::DracError;
fn os::GetMemInfo() -> Result<u64, DracError> {
fn os::GetMemInfo() -> Result<u64> {
u64 mem = 0;
usize size = sizeof(mem);
@ -25,19 +25,19 @@ fn os::GetMemInfo() -> Result<u64, DracError> {
return mem;
}
fn os::GetNowPlaying() -> Result<MediaInfo, DracError> { return GetCurrentPlayingInfo(); }
fn os::GetNowPlaying() -> Result<MediaInfo> { return GetCurrentPlayingInfo(); }
fn os::GetOSVersion() -> Result<String, DracError> { return GetMacOSVersion(); }
fn os::GetOSVersion() -> Result<String> { return GetMacOSVersion(); }
fn os::GetDesktopEnvironment() -> Result<String, DracError> {
fn os::GetDesktopEnvironment() -> Result<String> {
return "Aqua"; // TODO: Implement
}
fn os::GetWindowManager() -> Result<String, DracError> {
fn os::GetWindowManager() -> Result<String> {
return "Yabai"; // TODO: Implement
}
fn os::GetKernelVersion() -> Result<String, DracError> {
fn os::GetKernelVersion() -> Result<String> {
Array<char, 256> kernelVersion {};
usize kernelVersionLen = sizeof(kernelVersion);
@ -47,7 +47,7 @@ fn os::GetKernelVersion() -> Result<String, DracError> {
return kernelVersion.data();
}
fn os::GetHost() -> Result<String, DracError> {
fn os::GetHost() -> Result<String> {
Array<char, 256> hwModel {};
usize hwModelLen = sizeof(hwModel);
@ -212,7 +212,7 @@ fn os::GetHost() -> Result<String, DracError> {
return String(iter->second);
}
fn os::GetDiskUsage() -> Result<DiskSpace, DracError> {
fn os::GetDiskUsage() -> Result<DiskSpace> {
struct statvfs vfs;
if (statvfs("/", &vfs) != 0)
@ -222,11 +222,11 @@ fn os::GetDiskUsage() -> Result<DiskSpace, DracError> {
.total_bytes = vfs.f_blocks * vfs.f_frsize };
}
fn os::GetPackageCount() -> Result<u64, DracError> {
fn os::GetPackageCount() -> Result<u64> {
return Err(DracError(DracErrorCode::NotSupported, "Package count is not supported on macOS")); // TODO: Implement
}
fn os::GetShell() -> Result<String, DracError> {
fn os::GetShell() -> Result<String> {
return "Fish"; // TODO: Implement
}

View file

@ -66,7 +66,7 @@ using MRMediaRemoteGetNowPlayingInfoFunction =
);
}
+ (Result<String, DracError>)macOSVersion {
+ (Result<String>)macOSVersion {
NSProcessInfo* processInfo = [NSProcessInfo processInfo];
NSOperatingSystemVersion osVersion = [processInfo operatingSystemVersion];
@ -91,8 +91,8 @@ using MRMediaRemoteGetNowPlayingInfoFunction =
extern "C++" {
// NOLINTBEGIN(misc-use-internal-linkage)
fn GetCurrentPlayingInfo() -> Result<MediaInfo, DracError> {
__block Result<MediaInfo, DracError> result;
fn GetCurrentPlayingInfo() -> Result<MediaInfo> {
__block Result<MediaInfo> result;
const dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
@ -124,7 +124,7 @@ extern "C++" {
return result;
}
fn GetMacOSVersion() -> Result<String, DracError> { return [Bridge macOSVersion]; }
fn GetMacOSVersion() -> Result<String> { return [Bridge macOSVersion]; }
// NOLINTEND(misc-use-internal-linkage)
}

View file

@ -23,21 +23,21 @@ namespace os {
* @return A Result containing the total RAM in bytes (u64) on success,
* or a DracError on failure.
*/
fn GetMemInfo() -> Result<u64, DracError>;
fn GetMemInfo() -> Result<u64>;
/**
* @brief Gets structured metadata about the currently playing media.
* @return A Result containing the media information (MediaInfo struct) on success,
* or a NowPlayingError (indicating player state or system error) on failure.
*/
fn GetNowPlaying() -> Result<MediaInfo, DracError>;
fn GetNowPlaying() -> Result<MediaInfo>;
/**
* @brief Gets the "pretty" name of the operating system.
* @details Examples: "Ubuntu 24.04.2 LTS", "Windows 11 Pro 24H2", "macOS 15 Sequoia".
* @return A Result containing the OS version String on success, or a DracError on failure.
*/
fn GetOSVersion() -> Result<String, DracError>;
fn GetOSVersion() -> Result<String>;
/**
* @brief Attempts to retrieve the desktop environment name.
@ -46,7 +46,7 @@ namespace os {
* @return A Result containing the DE name String on success,
* or a DracError on failure (e.g., permission error, API error).
*/
fn GetDesktopEnvironment() -> Result<String, DracError>;
fn GetDesktopEnvironment() -> Result<String>;
/**
* @brief Attempts to retrieve the window manager name.
@ -55,7 +55,7 @@ namespace os {
* @return A Result containing the detected WM name String on success,
* or a DracError on failure (e.g., permission error, API error).
*/
fn GetWindowManager() -> Result<String, DracError>;
fn GetWindowManager() -> Result<String>;
/**
* @brief Attempts to detect the current user shell name.
@ -64,7 +64,7 @@ namespace os {
* @return A Result containing the shell name String on success,
* or a DracError on failure (e.g., permission error, API error).
*/
fn GetShell() -> Result<String, DracError>;
fn GetShell() -> Result<String>;
/**
* @brief Gets a system identifier, often the hardware model or product family.
@ -73,7 +73,7 @@ namespace os {
* @return A Result containing the host/product identifier String on success,
* or a DracError on failure (e.g., permission reading DMI/registry, API error).
*/
fn GetHost() -> Result<String, DracError>;
fn GetHost() -> Result<String>;
/**
* @brief Gets the operating system's kernel version string.
@ -82,7 +82,7 @@ namespace os {
* @return A Result containing the kernel version String on success,
* or a DracError on failure.
*/
fn GetKernelVersion() -> Result<String, DracError>;
fn GetKernelVersion() -> Result<String>;
/**
* @brief Gets the disk usage for the primary/root filesystem.
@ -90,5 +90,5 @@ namespace os {
* @return A Result containing the DiskSpace struct (used/total bytes) on success,
* or a DracError on failure (e.g., filesystem not found, permission error).
*/
fn GetDiskUsage() -> Result<DiskSpace, DracError>;
fn GetDiskUsage() -> Result<DiskSpace>;
} // namespace os

View file

@ -50,7 +50,7 @@ namespace {
// NOLINTEND(readability-identifier-naming)
};
fn CountUniquePackages(const String& dbPath) -> Result<u64, DracError> {
fn CountUniquePackages(const String& dbPath) -> Result<u64> {
std::ifstream dbFile(dbPath);
if (!dbFile.is_open())
@ -68,7 +68,7 @@ namespace {
} // namespace
namespace os {
fn GetOSVersion() -> Result<String, DracError> {
fn GetOSVersion() -> Result<String> {
utsname uts;
if (uname(&uts) == -1)
@ -77,7 +77,7 @@ namespace os {
return uts.sysname;
}
fn GetMemInfo() -> Result<u64, DracError> {
fn GetMemInfo() -> Result<u64> {
CStr path = "/sys/kernel/memstat";
std::ifstream file(path);
@ -106,15 +106,15 @@ namespace os {
return (data.physical_allocated + data.physical_available) * PAGE_SIZE;
}
fn GetNowPlaying() -> Result<MediaInfo, DracError> {
fn GetNowPlaying() -> Result<MediaInfo> {
return Err(DracError(DracErrorCode::NotSupported, "Now playing is not supported on SerenityOS"));
}
fn GetWindowManager() -> Result<String, DracError> { return "WindowManager"; }
fn GetWindowManager() -> Result<String> { return "WindowManager"; }
fn GetDesktopEnvironment() -> Result<String, DracError> { return "SerenityOS Desktop"; }
fn GetDesktopEnvironment() -> Result<String> { return "SerenityOS Desktop"; }
fn GetShell() -> Result<String, DracError> {
fn GetShell() -> Result<String> {
uid_t userId = getuid();
passwd* pw = getpwuid(userId);
@ -134,7 +134,7 @@ namespace os {
return shell;
}
fn GetHost() -> Result<String, DracError> {
fn GetHost() -> Result<String> {
Array<char, HOST_NAME_MAX> hostname_buffer;
if (gethostname(hostname_buffer.data(), hostname_buffer.size()) != 0)
@ -143,7 +143,7 @@ namespace os {
return String(hostname_buffer.data());
}
fn GetKernelVersion() -> Result<String, DracError> {
fn GetKernelVersion() -> Result<String> {
utsname uts;
if (uname(&uts) == -1)
@ -152,7 +152,7 @@ namespace os {
return uts.release;
}
fn GetDiskUsage() -> Result<DiskSpace, DracError> {
fn GetDiskUsage() -> Result<DiskSpace> {
struct statvfs stat;
if (statvfs("/", &stat) == -1)
return Err(DracError::withErrno("statvfs call failed for '/'"));
@ -166,7 +166,7 @@ namespace os {
} // namespace os
namespace package {
fn GetSerenityCount() -> Result<u64, DracError> { return CountUniquePackages("/usr/Ports/installed.db"); }
fn GetSerenityCount() -> Result<u64> { return CountUniquePackages("/usr/Ports/installed.db"); }
} // namespace package
#endif // __serenity__

View file

@ -2,20 +2,21 @@
// clang-format off
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
#include <dwmapi.h>
#include <tlhelp32.h>
#include <ranges>
#include <tlhelp32.h>
#include <wincrypt.h>
#include <windows.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Management.Deployment.h>
#include <winrt/Windows.Media.Control.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.System.Profile.h>
#include <winrt/Windows.Management.Deployment.h>
#include <winrt/base.h>
#include <winrt/impl/Windows.Media.Control.2.h>
#include "src/core/package.hpp"
#include "src/util/error.hpp"
#include "src/util/helpers.hpp"
#include "src/util/logging.hpp"
@ -63,6 +64,7 @@ namespace {
String value((type == REG_SZ || type == REG_EXPAND_SZ) ? dataSize - 1 : dataSize, '\0');
// NOLINTNEXTLINE(*-pro-type-reinterpret-cast) - required here
if (RegQueryValueExA(key, valueName.c_str(), nullptr, nullptr, reinterpret_cast<LPBYTE>(value.data()), &dataSize) !=
ERROR_SUCCESS) {
RegCloseKey(key);
@ -81,7 +83,8 @@ namespace {
std::unordered_map<DWORD, ProcessData> processMap;
const HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// ReSharper disable once CppLocalVariableMayBeConst
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE) {
error_log("FindShellInProcessTree: Failed snapshot, error {}", GetLastError());
@ -92,9 +95,10 @@ namespace {
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnap, &pe32)) {
// NOLINTNEXTLINE(*-avoid-do-while)
do {
String fullName = pe32.szExeFile;
String baseName;
const String fullName = pe32.szExeFile;
String baseName;
const size_t lastSlash = fullName.find_last_of("\\/");
@ -107,11 +111,11 @@ namespace {
if (baseName.length() > 4 && baseName.ends_with(".exe"))
baseName.resize(baseName.length() - 4);
processMap[pe32.th32ProcessID] = ProcessData { pe32.th32ParentProcessID, std::move(baseName) };
processMap[pe32.th32ProcessID] =
ProcessData { .parentPid = pe32.th32ParentProcessID, .baseExeNameLower = std::move(baseName) };
} while (Process32Next(hSnap, &pe32));
} else {
} else
error_log("FindShellInProcessTree: Process32First failed, error {}", GetLastError());
}
CloseHandle(hSnap);
@ -165,7 +169,7 @@ namespace {
} // namespace
namespace os {
fn GetMemInfo() -> Result<u64, DracError> {
fn GetMemInfo() -> Result<u64> {
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
@ -178,7 +182,7 @@ namespace os {
));
}
fn GetNowPlaying() -> Result<MediaInfo, DracError> {
fn GetNowPlaying() -> Result<MediaInfo> {
using namespace winrt::Windows::Media::Control;
using namespace winrt::Windows::Foundation;
@ -200,7 +204,7 @@ namespace os {
} catch (const winrt::hresult_error& e) { return Err(DracError(e)); }
}
fn GetOSVersion() -> Result<String, DracError> {
fn GetOSVersion() -> Result<String> {
try {
const String regSubKey = R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)";
@ -230,13 +234,14 @@ namespace os {
} catch (const std::exception& e) { return Err(DracError(e)); }
}
fn GetHost() -> Result<String, DracError> {
fn GetHost() -> Result<String> {
return GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SYSTEM\HardwareConfig\Current)", "SystemFamily");
}
fn GetKernelVersion() -> Result<String, DracError> {
fn GetKernelVersion() -> Result<String> {
if (const HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll")) {
if (const auto rtlGetVersion = std::bit_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"))) {
// NOLINTNEXTLINE(*-pro-type-reinterpret-cast) - required here
if (const auto rtlGetVersion = reinterpret_cast<RtlGetVersionPtr>(GetProcAddress(ntdllHandle, "RtlGetVersion"))) {
RTL_OSVERSIONINFOW osInfo = {};
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
@ -250,7 +255,7 @@ namespace os {
return Err(DracError(DracErrorCode::NotFound, "Could not determine kernel version using RtlGetVersion"));
}
fn GetWindowManager() -> Result<String, DracError> {
fn GetWindowManager() -> Result<String> {
BOOL compositionEnabled = FALSE;
if (SUCCEEDED(DwmIsCompositionEnabled(&compositionEnabled)))
@ -259,7 +264,7 @@ namespace os {
return Err(DracError(DracErrorCode::NotFound, "Failed to get window manager (DwmIsCompositionEnabled failed"));
}
fn GetDesktopEnvironment() -> Result<String, DracError> {
fn GetDesktopEnvironment() -> Result<String> {
const String buildStr =
GetRegistryValue(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", "CurrentBuildNumber");
@ -301,18 +306,17 @@ namespace os {
} catch (...) { return Err(DracError(DracErrorCode::ParseError, "Failed to parse CurrentBuildNumber")); }
}
fn GetShell() -> Result<String, DracError> {
fn GetShell() -> Result<String> {
using util::helpers::GetEnv;
if (const Result<String, DracError> msystemResult = GetEnv("MSYSTEM"); msystemResult && !msystemResult->empty()) {
if (const Result<String> msystemResult = GetEnv("MSYSTEM"); msystemResult && !msystemResult->empty()) {
String shellPath;
if (const Result<String, DracError> shellResult = GetEnv("SHELL"); shellResult && !shellResult->empty()) {
if (const Result<String> shellResult = GetEnv("SHELL"); shellResult && !shellResult->empty())
shellPath = *shellResult;
} else if (const Result<String, DracError> loginShellResult = GetEnv("LOGINSHELL");
loginShellResult && !loginShellResult->empty()) {
else if (const Result<String> loginShellResult = GetEnv("LOGINSHELL");
loginShellResult && !loginShellResult->empty())
shellPath = *loginShellResult;
}
if (!shellPath.empty()) {
const usize lastSlash = shellPath.find_last_of("\\/");
@ -341,20 +345,51 @@ namespace os {
return Err(DracError(DracErrorCode::NotFound, "Shell not found"));
}
fn GetDiskUsage() -> Result<DiskSpace, DracError> {
fn GetDiskUsage() -> Result<DiskSpace> {
ULARGE_INTEGER freeBytes, totalBytes;
if (GetDiskFreeSpaceExW(L"C:\\", nullptr, &totalBytes, &freeBytes))
return DiskSpace { .used_bytes = totalBytes.QuadPart - freeBytes.QuadPart, .total_bytes = totalBytes.QuadPart };
return DiskSpace { .usedBytes = totalBytes.QuadPart - freeBytes.QuadPart, .totalBytes = totalBytes.QuadPart };
return Err(DracError(util::error::DracErrorCode::NotFound, "Failed to get disk usage"));
}
} // namespace os
fn GetPackageCount() -> Result<u64, DracError> {
namespace package {
using util::helpers::GetEnv;
fn CountChocolatey() -> Result<u64> {
const fs::path chocoPath = fs::path(GetEnv("ChocolateyInstall").value_or("C:\\ProgramData\\chocolatey")) / "lib";
if (!fs::exists(chocoPath) || !fs::is_directory(chocoPath))
return Err(
DracError(DracErrorCode::NotFound, std::format("Chocolatey directory not found: {}", chocoPath.string()))
);
return GetCountFromDirectory("Chocolatey", chocoPath);
}
fn CountScoop() -> Result<u64> {
fs::path scoopAppsPath;
if (const Result<String> scoopEnvPath = GetEnv("SCOOP"))
scoopAppsPath = fs::path(*scoopEnvPath) / "apps";
else if (const Result<String> userProfilePath = GetEnv("USERPROFILE"))
scoopAppsPath = fs::path(*userProfilePath) / "scoop" / "apps";
else
return Err(DracError(
DracErrorCode::NotFound,
"Could not determine Scoop installation directory (SCOOP and USERPROFILE environment variables not found)"
));
return GetCountFromDirectory("Scoop", scoopAppsPath, true);
}
fn CountWinGet() -> Result<u64> {
try {
return std::ranges::distance(winrt::Windows::Management::Deployment::PackageManager().FindPackagesForUser(L""));
} catch (const winrt::hresult_error& e) { return Err(DracError(e)); }
}
} // namespace os
} // namespace package
#endif

View file

@ -25,24 +25,33 @@ namespace util::cache {
* 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> {
inline fn GetCachePath(const String& cache_key) -> Result<fs::path> {
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);
const fs::path cacheDir = fs::temp_directory_path(errc) / "draconis++";
if (!fs::exists(cacheDir, errc)) {
if (errc)
return Err(DracError(DracErrorCode::IoError, "Failed to check existence of cache directory: " + errc.message())
);
fs::create_directories(cacheDir, errc);
if (errc)
return Err(DracError(DracErrorCode::IoError, "Failed to create cache directory: " + errc.message()));
}
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");
}
@ -53,18 +62,17 @@ namespace util::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);
fn ReadCache(const String& cache_key) -> Result<T> {
Result<fs::path> cachePathResult = GetCachePath(cache_key);
if (!cachePathResult)
return Err(cachePathResult.error());
const fs::path& cachePath = *cachePathResult;
if (std::error_code existsEc; !fs::exists(cachePath, existsEc) || existsEc) {
if (existsEc) {
// Log if there was an error checking existence, but still return NotFound
if (existsEc)
warn_log("Error checking existence of cache file '{}': {}", cachePath.string(), existsEc.message());
}
return Err(DracError(DracErrorCode::NotFound, "Cache file not found: " + cachePath.string()));
}
@ -72,17 +80,13 @@ namespace util::cache {
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
ifs.close();
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 {};
@ -98,14 +102,12 @@ namespace util::cache {
));
}
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())
@ -123,24 +125,20 @@ namespace util::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);
fn WriteCache(const String& cache_key, const T& data) -> Result<> {
Result<fs::path> 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());
tempPath += ".tmp";
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
DecayedT dataToSerialize = data;
if (glz::error_ctx glazeErr = glz::write_beve(dataToSerialize, binaryBuffer); glazeErr) {
return Err(DracError(
@ -154,7 +152,6 @@ namespace util::cache {
));
}
// Scope for ofstream to ensure it's closed before rename
{
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
if (!ofs.is_open())
@ -163,22 +160,18 @@ namespace util::cache {
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
fs::remove(tempPath, removeEc);
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 renameEc;
fs::rename(tempPath, cachePath, renameEc);
if (renameEc) {
// If rename failed, attempt to clean up the temp file
std::error_code removeEc;
fs::remove(tempPath, removeEc); // Ignore error on cleanup attempt
fs::remove(tempPath, removeEc);
return Err(DracError(
DracErrorCode::IoError,
std::format(
@ -190,26 +183,24 @@ namespace util::cache {
));
}
debug_log("Successfully wrote cache for key '{}'.", cache_key);
return {}; // Success
return {};
} catch (const std::ios_base::failure& e) {
std::error_code removeEc;
fs::remove(tempPath, removeEc); // Cleanup attempt
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); // Cleanup attempt
fs::remove(tempPath, removeEc);
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
fs::remove(tempPath, removeEc);
return Err(DracError(DracErrorCode::Other, "Unknown error writing cache file: " + tempPath.string()));
}
}
} // namespace util::cache

View file

@ -1,6 +1,6 @@
#pragma once
#include <format>
#include <expected> // std::{unexpected, expected}
#include <source_location> // std::source_location
#include <system_error> // std::error_code
@ -11,115 +11,146 @@
#include "src/util/types.hpp"
namespace util::error {
using types::u8, types::i32, types::String, types::StringView, types::Exception;
#include "include/matchit.h"
/**
* @enum DracErrorCode
* @brief Error codes for general OS-level operations.
*/
enum class DracErrorCode : u8 {
ApiUnavailable, ///< A required OS service/API is unavailable or failed unexpectedly at runtime.
InternalError, ///< An error occurred within the application's OS abstraction code logic.
InvalidArgument, ///< An invalid argument was passed to a function or method.
IoError, ///< General I/O error (filesystem, pipes, etc.).
NetworkError, ///< A network-related error occurred (e.g., DNS resolution, connection failure).
NotFound, ///< A required resource (file, registry key, device, API endpoint) was not found.
NotSupported, ///< The requested operation is not supported on this platform, version, or configuration.
Other, ///< A generic or unclassified error originating from the OS or an external library.
OutOfMemory, ///< The system ran out of memory or resources to complete the operation.
ParseError, ///< Failed to parse data obtained from the OS (e.g., file content, API output).
PermissionDenied, ///< Insufficient permissions to perform the operation.
PlatformSpecific, ///< An unmapped error specific to the underlying OS platform occurred (check message).
Timeout, ///< An operation timed out (e.g., waiting for IPC reply).
};
namespace util {
namespace error {
using types::u8, types::i32, types::String, types::StringView, types::Exception;
/**
* @struct DracError
* @brief Holds structured information about an OS-level error.
*
* Used as the error type in Result for many os:: functions.
*/
struct DracError {
// ReSharper disable CppDFANotInitializedField
String message; ///< A descriptive error message, potentially including platform details.
DracErrorCode code; ///< The general category of the error.
std::source_location location; ///< The source location where the error occurred (file, line, function).
// ReSharper restore CppDFANotInitializedField
/**
* @enum DracErrorCode
* @brief Error codes for general OS-level operations.
*/
enum class DracErrorCode : u8 {
ApiUnavailable, ///< A required OS service/API is unavailable or failed unexpectedly at runtime.
InternalError, ///< An error occurred within the application's OS abstraction code logic.
InvalidArgument, ///< An invalid argument was passed to a function or method.
IoError, ///< General I/O error (filesystem, pipes, etc.).
NetworkError, ///< A network-related error occurred (e.g., DNS resolution, connection failure).
NotFound, ///< A required resource (file, registry key, device, API endpoint) was not found.
NotSupported, ///< The requested operation is not supported on this platform, version, or configuration.
Other, ///< A generic or unclassified error originating from the OS or an external library.
OutOfMemory, ///< The system ran out of memory or resources to complete the operation.
ParseError, ///< Failed to parse data obtained from the OS (e.g., file content, API output).
PermissionDenied, ///< Insufficient permissions to perform the operation.
PlatformSpecific, ///< An unmapped error specific to the underlying OS platform occurred (check message).
Timeout, ///< An operation timed out (e.g., waiting for IPC reply).
};
DracError(const DracErrorCode errc, String msg, const std::source_location& loc = std::source_location::current())
: message(std::move(msg)), code(errc), location(loc) {}
/**
* @struct DracError
* @brief Holds structured information about an OS-level error.
*
* Used as the error type in Result for many os:: functions.
*/
struct DracError {
// ReSharper disable CppDFANotInitializedField
String message; ///< A descriptive error message, potentially including platform details.
DracErrorCode code; ///< The general category of the error.
std::source_location location; ///< The source location where the error occurred (file, line, function).
// ReSharper restore CppDFANotInitializedField
explicit DracError(const Exception& exc, const std::source_location& loc = std::source_location::current())
: message(exc.what()), code(DracErrorCode::InternalError), location(loc) {}
DracError(const DracErrorCode errc, String msg, const std::source_location& loc = std::source_location::current())
: message(std::move(msg)), code(errc), location(loc) {}
explicit DracError(const std::error_code& errc, const std::source_location& loc = std::source_location::current())
: message(errc.message()), location(loc) {
using enum DracErrorCode;
using enum std::errc;
explicit DracError(const Exception& exc, const std::source_location& loc = std::source_location::current())
: message(exc.what()), code(DracErrorCode::InternalError), location(loc) {}
switch (static_cast<std::errc>(errc.value())) {
case permission_denied: code = PermissionDenied; break;
case no_such_file_or_directory: code = NotFound; break;
case timed_out: code = Timeout; break;
case io_error: code = IoError; break;
case network_unreachable:
case network_down:
case connection_refused: code = NetworkError; break;
case not_supported: code = NotSupported; break;
default: code = errc.category() == std::generic_category() ? InternalError : PlatformSpecific; break;
explicit DracError(const std::error_code& errc, const std::source_location& loc = std::source_location::current())
: message(errc.message()), location(loc) {
using namespace matchit;
using enum DracErrorCode;
using enum std::errc;
code = match(errc)(
is | or_(file_too_large, io_error) = IoError,
is | invalid_argument = InvalidArgument,
is | not_enough_memory = OutOfMemory,
is | or_(address_family_not_supported, operation_not_supported, not_supported) = NotSupported,
is | or_(network_unreachable, network_down, connection_refused) = NetworkError,
is | or_(no_such_file_or_directory, not_a_directory, is_a_directory, file_exists) = NotFound,
is | permission_denied = PermissionDenied,
is | timed_out = Timeout,
is | _ = errc.category() == std::generic_category() ? InternalError : PlatformSpecific
);
}
}
#ifdef _WIN32
explicit DracError(const winrt::hresult_error& e) : message(winrt::to_string(e.message())) {
switch (e.code()) {
case E_ACCESSDENIED: code = DracErrorCode::PermissionDenied; break;
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
case HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_FOUND): code = DracErrorCode::NotFound; break;
case HRESULT_FROM_WIN32(ERROR_TIMEOUT):
case HRESULT_FROM_WIN32(ERROR_SEM_TIMEOUT): code = DracErrorCode::Timeout; break;
case HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED): code = DracErrorCode::NotSupported; break;
default: code = DracErrorCode::PlatformSpecific; break;
explicit DracError(const winrt::hresult_error& e) : message(winrt::to_string(e.message())) {
using namespace matchit;
using enum DracErrorCode;
fn fromWin32 = [](const types::u32 x) -> HRESULT { return HRESULT_FROM_WIN32(x); };
code = match(e.code())(
is | or_(E_ACCESSDENIED, fromWin32(ERROR_ACCESS_DENIED)) = PermissionDenied,
is | fromWin32(ERROR_FILE_NOT_FOUND) = NotFound,
is | fromWin32(ERROR_PATH_NOT_FOUND) = NotFound,
is | fromWin32(ERROR_SERVICE_NOT_FOUND) = NotFound,
is | fromWin32(ERROR_TIMEOUT) = Timeout,
is | fromWin32(ERROR_SEM_TIMEOUT) = Timeout,
is | fromWin32(ERROR_NOT_SUPPORTED) = NotSupported,
is | _ = PlatformSpecific
);
}
}
#else
DracError(const DracErrorCode code_hint, const int errno_val)
: message(std::system_category().message(errno_val)), code(code_hint) {
using enum DracErrorCode;
DracError(const DracErrorCode code_hint, const int errno_val)
: message(std::system_category().message(errno_val)), code(code_hint) {
using enum DracErrorCode;
switch (errno_val) {
case EACCES: code = PermissionDenied; break;
case ENOENT: code = NotFound; break;
case ETIMEDOUT: code = Timeout; break;
case ENOTSUP: code = NotSupported; break;
default: code = PlatformSpecific; break;
}
}
static auto withErrno(const String& context, const std::source_location& loc = std::source_location::current())
-> DracError {
const i32 errNo = errno;
const String msg = std::system_category().message(errNo);
const String fullMsg = std::format("{}: {}", context, msg);
const DracErrorCode code = [&errNo] {
switch (errNo) {
case EACCES:
case EPERM: return DracErrorCode::PermissionDenied;
case ENOENT: return DracErrorCode::NotFound;
case ETIMEDOUT: return DracErrorCode::Timeout;
case ENOTSUP: return DracErrorCode::NotSupported;
case EIO: return DracErrorCode::IoError;
case ECONNREFUSED:
case ENETDOWN:
case ENETUNREACH: return DracErrorCode::NetworkError;
default: return DracErrorCode::PlatformSpecific;
switch (errno_val) {
case EACCES: code = PermissionDenied; break;
case ENOENT: code = NotFound; break;
case ETIMEDOUT: code = Timeout; break;
case ENOTSUP: code = NotSupported; break;
default: code = PlatformSpecific; break;
}
}();
}
return DracError { code, fullMsg, loc };
}
static auto withErrno(const String& context, const std::source_location& loc = std::source_location::current())
-> DracError {
const i32 errNo = errno;
const String msg = std::system_category().message(errNo);
const String fullMsg = std::format("{}: {}", context, msg);
const DracErrorCode code = [&errNo] {
switch (errNo) {
case EACCES:
case EPERM: return DracErrorCode::PermissionDenied;
case ENOENT: return DracErrorCode::NotFound;
case ETIMEDOUT: return DracErrorCode::Timeout;
case ENOTSUP: return DracErrorCode::NotSupported;
case EIO: return DracErrorCode::IoError;
case ECONNREFUSED:
case ENETDOWN:
case ENETUNREACH: return DracErrorCode::NetworkError;
default: return DracErrorCode::PlatformSpecific;
}
}();
return DracError { code, fullMsg, loc };
}
#endif
};
} // namespace util::error
};
} // namespace error
namespace types {
/**
* @typedef Result
* @brief Alias for std::expected<Tp, Er>. Represents a value that can either be
* a success value of type Tp or an error value of type Er.
* @tparam Tp The type of the success value.
* @tparam Er The type of the error value.
*/
template <typename Tp = void, typename Er = error::DracError>
using Result = std::expected<Tp, Er>;
/**
* @typedef Err
* @brief Alias for std::unexpected<Er>. Used to construct a Result in an error state.
* @tparam Er The type of the error value.
*/
template <typename Er = error::DracError>
using Err = std::unexpected<Er>;
} // namespace types
} // namespace util

View file

@ -14,7 +14,7 @@ namespace util::helpers {
* @return A Result containing the value of the environment variable as a String,
* or an EnvError if an error occurred.
*/
[[nodiscard]] inline fn GetEnv(CStr name) -> Result<String, DracError> {
[[nodiscard]] inline fn GetEnv(CStr name) -> Result<String> {
#ifdef _WIN32
using types::i32, types::usize, types::UniquePointer;

View file

@ -5,10 +5,15 @@
#include <filesystem> // std::filesystem::path
#include <format> // std::format
#include <ftxui/screen/color.hpp> // ftxui::Color
#include <iostream> // std::cout
#include <mutex> // std::{mutex, lock_guard}
#include <utility> // std::forward
#ifdef __cpp_lib_print
#include <print> // std::print
#else
#include <iostream> // std::cout
#endif
#ifndef NDEBUG
#include <source_location> // std::source_location
#endif
@ -17,6 +22,8 @@
#include "src/util/error.hpp"
#include "src/util/types.hpp"
#include "ftxui/dom/elements.hpp"
namespace util::logging {
using types::usize, types::u8, types::i32, types::i64, types::CStr, types::String, types::StringView, types::Array,
types::Option, types::None, types::Mutex, types::LockGuard;
@ -118,13 +125,15 @@ namespace util::logging {
* @return FTXUI color code
*/
constexpr fn GetLevelColor(const LogLevel level) -> ftxui::Color::Palette16 {
switch (level) {
case LogLevel::Debug: return LogLevelConst::DEBUG_COLOR;
case LogLevel::Info: return LogLevelConst::INFO_COLOR;
case LogLevel::Warn: return LogLevelConst::WARN_COLOR;
case LogLevel::Error: return LogLevelConst::ERROR_COLOR;
default: std::unreachable();
}
using namespace matchit;
using enum LogLevel;
return match(level)(
is | Debug = LogLevelConst::DEBUG_COLOR,
is | Info = LogLevelConst::INFO_COLOR,
is | Warn = LogLevelConst::WARN_COLOR,
is | Error = LogLevelConst::ERROR_COLOR
);
}
/**
@ -133,13 +142,15 @@ namespace util::logging {
* @return String representation
*/
constexpr fn GetLevelString(const LogLevel level) -> StringView {
switch (level) {
case LogLevel::Debug: return LogLevelConst::DEBUG_STR;
case LogLevel::Info: return LogLevelConst::INFO_STR;
case LogLevel::Warn: return LogLevelConst::WARN_STR;
case LogLevel::Error: return LogLevelConst::ERROR_STR;
default: std::unreachable();
}
using namespace matchit;
using enum LogLevel;
return match(level)(
is | Debug = LogLevelConst::DEBUG_STR,
is | Info = LogLevelConst::INFO_STR,
is | Warn = LogLevelConst::WARN_STR,
is | Error = LogLevelConst::ERROR_STR
);
}
// ReSharper disable once CppDoxygenUnresolvedReference
@ -180,7 +191,8 @@ namespace util::logging {
if (localtime_r(&nowTt, &localTm) != nullptr) {
#endif
Array<char, 64> timeBuffer {};
auto formattedTime =
const usize formattedTime =
std::strftime(timeBuffer.data(), sizeof(timeBuffer), LogLevelConst::TIMESTAMP_FORMAT, &localTm);
if (formattedTime > 0) {
@ -188,11 +200,10 @@ namespace util::logging {
} else {
try {
timestamp = std::format("{:%X}", nowTp);
} catch (const std::format_error& fmt_err) { timestamp = "??:??:?? (fmt_err)"; }
} catch ([[maybe_unused]] const std::format_error& fmtErr) { timestamp = "??:??:??"; }
}
} else {
timestamp = "??:??:?? (conv_err)";
}
} else
timestamp = "??:??:??";
const String message = std::format(fmt, std::forward<Args>(args)...);
@ -203,16 +214,28 @@ namespace util::logging {
message
);
#ifdef __cpp_lib_print
std::print("{}", mainLogLine);
#else
std::cout << mainLogLine;
#endif
#ifndef NDEBUG
const String fileLine =
std::format(LogLevelConst::FILE_LINE_FORMAT, path(loc.file_name()).lexically_normal().string(), loc.line());
const String fullDebugLine = std::format("{}{}", LogLevelConst::DEBUG_LINE_PREFIX, fileLine);
#ifdef __cpp_lib_print
std::print("\n{}", Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR)));
#else
std::cout << '\n' << Italic(Colorize(fullDebugLine, LogLevelConst::DEBUG_INFO_COLOR));
#endif
#endif
#ifdef __cpp_lib_print
std::println("{}", LogLevelConst::RESET_CODE);
#else
std::cout << LogLevelConst::RESET_CODE << '\n';
#endif
}
template <typename ErrorType>

View file

@ -1,7 +1,6 @@
#pragma once
#include <array> // std::array (Array)
#include <expected> // std::expected (Result)
#include <future> // std::future (Future)
#include <map> // std::map (Map)
#include <memory> // std::shared_ptr and std::unique_ptr (SharedPointer, UniquePointer)
@ -12,6 +11,8 @@
#include <utility> // std::pair (Pair)
#include <vector> // std::vector (Vec)
#include "include/matchit.h"
namespace util::types {
using u8 = std::uint8_t; ///< 8-bit unsigned integer.
using u16 = std::uint16_t; ///< 16-bit unsigned integer.
@ -40,24 +41,6 @@ namespace util::types {
inline constexpr std::nullopt_t None = std::nullopt; ///< Represents an empty optional value.
/**
* @typedef Result
* @brief Alias for std::expected<Tp, Er>. Represents a value that can either be
* a success value of type Tp or an error value of type Er.
* @tparam Tp The type of the success value.
* @tparam Er The type of the error value.
*/
template <typename Tp, typename Er>
using Result = std::expected<Tp, Er>;
/**
* @typedef Err
* @brief Alias for std::unexpected<Er>. Used to construct a Result in an error state.
* @tparam Er The type of the error value.
*/
template <typename Er>
using Err = std::unexpected<Er>;
/**
* @typedef Option
* @brief Alias for std::optional<Tp>. Represents a value that may or may not be present.
@ -133,8 +116,8 @@ namespace util::types {
* Used as the success type for os::GetDiskUsage.
*/
struct DiskSpace {
u64 used_bytes; ///< Currently used disk space in bytes.
u64 total_bytes; ///< Total disk space in bytes.
u64 usedBytes; ///< Currently used disk space in bytes.
u64 totalBytes; ///< Total disk space in bytes.
};
/**

View file

@ -265,7 +265,7 @@ namespace dbus {
* @return Result containing a MessageGuard on success, or DraconisError on failure.
*/
static fn newMethodCall(const char* destination, const char* path, const char* interface, const char* method)
-> Result<Message, DracError> {
-> Result<Message> {
DBusMessage* rawMsg = dbus_message_new_method_call(destination, path, interface, method);
if (!rawMsg)
@ -331,7 +331,7 @@ namespace dbus {
* @return Result containing the reply MessageGuard on success, or DraconisError on failure.
*/
[[nodiscard]] fn sendWithReplyAndBlock(const Message& message, const i32 timeout_milliseconds = 1000) const
-> Result<Message, DracError> {
-> Result<Message> {
if (!m_conn || !message.get())
return Err(
DracError(DracErrorCode::InvalidArgument, "Invalid connection or message provided to sendWithReplyAndBlock")
@ -371,7 +371,7 @@ namespace dbus {
* @param bus_type The type of bus (DBUS_BUS_SESSION or DBUS_BUS_SYSTEM).
* @return Result containing a ConnectionGuard on success, or DraconisError on failure.
*/
static fn busGet(const DBusBusType bus_type) -> Result<Connection, DracError> {
static fn busGet(const DBusBusType bus_type) -> Result<Connection> {
Error err;
DBusConnection* rawConn = dbus_bus_get(bus_type, err.get());