temp stuff

This commit is contained in:
Mars 2025-02-03 19:22:02 -05:00
parent 5b63fe4cce
commit 95374d942d
Signed by: pupbrained
GPG key ID: 0FF5B8826803F895
7 changed files with 199 additions and 323 deletions

View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1738012240, "lastModified": 1738611518,
"narHash": "sha256-4wmhkSSdgkVR02zG7nP4MTUpA2oih7E+9AWu4zEqP+k=", "narHash": "sha256-gOP/qsGtUCTkazx3qQ/tn6xaDERRgOtF2eRe1gmIU5s=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "0542e87760a8f611f089dcf38862f783c8c8f890", "rev": "eb3431789cef743af9dace58eb2ba7b33a332b56",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -58,11 +58,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1737483750, "lastModified": 1738070913,
"narHash": "sha256-5An1wq5U8sNycOBBg3nsDDgpwBmR9liOpDGlhliA6Xo=", "narHash": "sha256-j6jC12vCFsTGDmY2u1H12lMr62fnclNjuCtAdF1a4Nk=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "f2cc121df15418d028a59c9737d38e3a90fbaf8f", "rev": "bebf27d00f7d10ba75332a0541ac43676985dea3",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -65,6 +65,7 @@
tomlplusplus tomlplusplus
yyjson yyjson
reflect-cpp reflect-cpp
sqlitecpp
ftxui ftxui
] ]
++ linuxPkgs ++ linuxPkgs
@ -75,7 +76,6 @@
systemdLibs systemdLibs
sdbus-cpp sdbus-cpp
valgrind valgrind
linuxKernel.packages.linux_zen.perf.out
xorg.libX11 xorg.libX11
wayland wayland
]); ]);

View file

@ -92,6 +92,7 @@ if host_machine.system() == 'darwin'
deps += dependency('SystemConfiguration') deps += dependency('SystemConfiguration')
deps += dependency('iconv') deps += dependency('iconv')
elif host_machine.system() == 'linux' or host_machine.system() == 'freebsd' elif host_machine.system() == 'linux' or host_machine.system() == 'freebsd'
deps += dependency('SQLiteCpp')
deps += dependency('sdbus-c++') deps += dependency('sdbus-c++')
deps += dependency('x11') deps += dependency('x11')
deps += dependency('wayland-client') deps += dependency('wayland-client')

View file

@ -1,5 +1,6 @@
#include <chrono> #include <chrono>
#include <curl/curl.h> #include <curl/curl.h>
#include <expected>
#include <filesystem> #include <filesystem>
#include <fmt/core.h> #include <fmt/core.h>
#include <rfl/json.hpp> #include <rfl/json.hpp>
@ -7,9 +8,12 @@
#include "config.h" #include "config.h"
using rfl::Error;
using rfl::Result;
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace std::string_literals;
// Alias for cleaner error handling
template <typename T>
using Result = std::expected<T, std::string>;
namespace { namespace {
// Common function to get cache path // Common function to get cache path
@ -18,7 +22,7 @@ namespace {
fs::path cachePath = fs::temp_directory_path(errc); fs::path cachePath = fs::temp_directory_path(errc);
if (errc) if (errc)
return Error("Failed to get temp directory: " + errc.message()); return std::unexpected("Failed to get temp directory: "s + errc.message());
cachePath /= "weather_cache.json"; cachePath /= "weather_cache.json";
return cachePath; return cachePath;
@ -28,91 +32,94 @@ namespace {
fn ReadCacheFromFile() -> Result<WeatherOutput> { fn ReadCacheFromFile() -> Result<WeatherOutput> {
Result<fs::path> cachePath = GetCachePath(); Result<fs::path> cachePath = GetCachePath();
if (!cachePath) if (!cachePath)
return Error(cachePath.error()->what()); return std::unexpected(cachePath.error());
std::ifstream ifs(*cachePath, std::ios::binary); std::ifstream ifs(*cachePath, std::ios::binary);
if (!ifs.is_open()) if (!ifs.is_open())
return Error("Cache file not found: " + cachePath.value().string()); return std::unexpected("Cache file not found: "s + cachePath->string());
DEBUG_LOG("Reading from cache file..."); DEBUG_LOG("Reading from cache file...");
std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
Result<WeatherOutput> result = rfl::json::read<WeatherOutput>(content); rfl::Result<WeatherOutput> result = rfl::json::read<WeatherOutput>(content);
if (!result)
return std::unexpected(result.error()->what());
DEBUG_LOG("Successfully read from cache file."); DEBUG_LOG("Successfully read from cache file.");
return result; return *result;
} }
// Function to write cache to file // Function to write cache to file
fn WriteCacheToFile(const WeatherOutput& data) -> Result<u8> { fn WriteCacheToFile(const WeatherOutput& data) -> Result<void> {
Result<fs::path> cachePath = GetCachePath(); Result<fs::path> cachePath = GetCachePath();
if (!cachePath) if (!cachePath)
return Error(cachePath.error()->what()); return std::unexpected(cachePath.error());
DEBUG_LOG("Writing to cache file..."); DEBUG_LOG("Writing to cache file...");
// Write to temporary file first
fs::path tempPath = *cachePath; fs::path tempPath = *cachePath;
tempPath += ".tmp"; tempPath += ".tmp";
{ {
std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc); std::ofstream ofs(tempPath, std::ios::binary | std::ios::trunc);
if (!ofs.is_open()) if (!ofs.is_open())
return Error("Failed to open temp file: " + tempPath.string()); return std::unexpected("Failed to open temp file: "s + tempPath.string());
auto json = rfl::json::write(data); std::string json = rfl::json::write(data);
ofs << json; ofs << json;
if (!ofs) if (!ofs)
return Error("Failed to write to temp file"); return std::unexpected("Failed to write to temp file");
} // File stream closes here }
// Atomic replace
std::error_code errc; std::error_code errc;
fs::rename(tempPath, *cachePath, errc); fs::rename(tempPath, *cachePath, errc);
if (errc) { if (errc) {
fs::remove(tempPath, errc); fs::remove(tempPath, errc);
return Error("Failed to replace cache file: " + errc.message()); return std::unexpected("Failed to replace cache file: "s + errc.message());
} }
DEBUG_LOG("Successfully wrote to cache file."); DEBUG_LOG("Successfully wrote to cache file.");
return 0; return {};
} }
fn WriteCallback(void* contents, const usize size, const usize nmemb, string* str) -> usize { fn WriteCallback(void* contents, size_t size, size_t nmemb, std::string* str) -> size_t {
const usize totalSize = size * nmemb; const size_t totalSize = size * nmemb;
str->append(static_cast<char*>(contents), totalSize); str->append(static_cast<char*>(contents), totalSize);
return totalSize; return totalSize;
} }
// Function to make API request // Function to make API request
fn MakeApiRequest(const string& url) -> Result<WeatherOutput> { fn MakeApiRequest(const std::string& url) -> Result<WeatherOutput> {
DEBUG_LOG("Making API request to URL: {}", url); DEBUG_LOG("Making API request to URL: {}", url);
CURL* curl = curl_easy_init(); CURL* curl = curl_easy_init();
string responseBuffer; std::string responseBuffer;
if (!curl)
return std::unexpected("Failed to initialize cURL");
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
const CURLcode res = curl_easy_perform(curl); const CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
if (res != CURLE_OK) if (res != CURLE_OK)
return Error(fmt::format("Failed to perform cURL request: {}", curl_easy_strerror(res))); return std::unexpected(fmt::format("cURL error: {}", curl_easy_strerror(res)));
DEBUG_LOG("Received response from API. Response size: {}", responseBuffer.size()); DEBUG_LOG("API response size: {}", responseBuffer.size());
DEBUG_LOG("Response: {}", responseBuffer);
WeatherOutput output = rfl::json::read<WeatherOutput>(responseBuffer).value(); rfl::Result<WeatherOutput> output = rfl::json::read<WeatherOutput>(responseBuffer);
if (!output)
return std::unexpected(output.error()->what());
return output; // Return an empty result for now return *output;
}
return Error("Failed to initialize cURL.");
} }
} }
@ -120,60 +127,48 @@ namespace {
fn Weather::getWeatherInfo() const -> WeatherOutput { fn Weather::getWeatherInfo() const -> WeatherOutput {
using namespace std::chrono; using namespace std::chrono;
// Check if cache is valid
if (Result<WeatherOutput> data = ReadCacheFromFile()) { if (Result<WeatherOutput> data = ReadCacheFromFile()) {
WeatherOutput dataVal = *data; const WeatherOutput& dataVal = *data;
const duration<double> cacheAge = system_clock::now() - system_clock::time_point(seconds(dataVal.dt));
if (system_clock::now() - system_clock::time_point(seconds(dataVal.dt)) < minutes(10)) {
DEBUG_LOG("Cache is valid. Returning cached data.");
if (cacheAge < 10min) {
DEBUG_LOG("Using valid cache");
return dataVal; return dataVal;
} }
DEBUG_LOG("Cache expired");
DEBUG_LOG("Cache is expired.");
} else { } else {
DEBUG_LOG("No valid cache found."); DEBUG_LOG("Cache error: {}", data.error());
} }
WeatherOutput result; fn handleApiResult = [](const Result<WeatherOutput>& result) -> WeatherOutput {
if (!result)
ERROR_LOG("API request failed: {}", result.error());
if (holds_alternative<string>(location)) { // Fix for second warning: Check the write result
const string city = get<string>(location); if (Result<void> writeResult = WriteCacheToFile(*result); !writeResult)
ERROR_LOG("Failed to write cache: {}", writeResult.error());
const char* loc = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length())); return *result;
};
DEBUG_LOG("City: {}", loc); if (std::holds_alternative<std::string>(location)) {
const auto& city = std::get<std::string>(location);
char* escaped = curl_easy_escape(nullptr, city.c_str(), static_cast<int>(city.length()));
const string apiUrl = fmt::format( DEBUG_LOG("Requesting city: {}", escaped);
"https://api.openweathermap.org/data/2.5/" const std::string apiUrl =
"weather?q={}&appid={}&units={}", fmt::format("https://api.openweathermap.org/data/2.5/weather?q={}&appid={}&units={}", escaped, api_key, units);
loc,
api_key, curl_free(escaped);
units return handleApiResult(MakeApiRequest(apiUrl));
}
const auto& [lat, lon] = std::get<Coords>(location);
DEBUG_LOG("Requesting coordinates: lat={:.3f}, lon={:.3f}", lat, lon);
const std::string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/weather?lat={:.3f}&lon={:.3f}&appid={}&units={}", lat, lon, api_key, units
); );
result = MakeApiRequest(apiUrl).value(); return handleApiResult(MakeApiRequest(apiUrl));
} else {
const auto [lat, lon] = get<Coords>(location);
DEBUG_LOG("Coordinates: lat = {:.3f}, lon = {:.3f}", lat, lon);
const string apiUrl = fmt::format(
"https://api.openweathermap.org/data/2.5/"
"weather?lat={:.3f}&lon={:.3f}&appid={}&units={}",
lat,
lon,
api_key,
units
);
result = MakeApiRequest(apiUrl).value();
}
// Update the cache with the new data
WriteCacheToFile(result);
DEBUG_LOG("Returning new data.");
return result;
} }

View file

@ -10,6 +10,8 @@
#include "config/config.h" #include "config/config.h"
#include "os/os.h" #include "os/os.h"
constexpr const bool SHOW_ICONS = true;
struct BytesToGiB { struct BytesToGiB {
u64 value; u64 value;
}; };
@ -83,14 +85,19 @@ namespace {
const bool nowPlayingEnabled = config.now_playing.get().enabled; const bool nowPlayingEnabled = config.now_playing.get().enabled;
const std::string& nowPlaying = nowPlayingEnabled ? GetNowPlaying() : ""; const std::string& nowPlaying = nowPlayingEnabled ? GetNowPlaying() : "";
// Icon constants (using Nerd Font v3) const char *calendarIcon = "", *hostIcon = "", *kernelIcon = "", *osIcon = "", *memoryIcon = "", *weatherIcon = "",
constexpr const char* calendarIcon = ""; *musicIcon = "";
constexpr const char* hostIcon = " 󰌢 ";
constexpr const char* kernelIcon = ""; if (SHOW_ICONS) {
constexpr const char* osIcon = ""; calendarIcon = "";
constexpr const char* memoryIcon = ""; hostIcon = " 󰌢 ";
constexpr const char* weatherIcon = ""; kernelIcon = "";
constexpr const char* musicIcon = ""; osIcon = "";
memoryIcon = "";
weatherIcon = "";
musicIcon = "";
}
const Color::Palette16 labelColor = Color::Yellow; const Color::Palette16 labelColor = Color::Yellow;
const Color::Palette16 valueColor = Color::White; const Color::Palette16 valueColor = Color::White;
const Color::Palette16 borderColor = Color::GrayLight; const Color::Palette16 borderColor = Color::GrayLight;
@ -111,7 +118,6 @@ namespace {
return hbox({ return hbox({
text(icon) | color(iconColor), text(icon) | color(iconColor),
text(label) | color(labelColor), text(label) | color(labelColor),
text(" "),
filler(), filler(),
text(value) | color(valueColor), text(value) | color(valueColor),
text(" "), text(" "),

View file

@ -1,5 +1,6 @@
#ifdef __linux__ #ifdef __linux__
#include <SQLiteCpp/SQLiteCpp.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <algorithm> #include <algorithm>
@ -9,6 +10,7 @@
#include <fstream> #include <fstream>
#include <ranges> #include <ranges>
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>
#include <sqlite3.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <vector> #include <vector>
@ -51,9 +53,8 @@ namespace {
// Find the end of the numeric part // Find the end of the numeric part
const size_t end = view.find_first_not_of("0123456789"); const size_t end = view.find_first_not_of("0123456789");
if (end != std::string_view::npos) { if (end != std::string_view::npos)
view = view.substr(0, end); view = view.substr(0, end);
}
// Get pointers via iterators // Get pointers via iterators
const char* startPtr = &*view.begin(); // Safe iterator-to-pointer conversion const char* startPtr = &*view.begin(); // Safe iterator-to-pointer conversion
@ -346,14 +347,81 @@ namespace {
std::ifstream cmdline("/proc/self/environ"); std::ifstream cmdline("/proc/self/environ");
std::string envVars((std::istreambuf_iterator<char>(cmdline)), std::istreambuf_iterator<char>()); std::string envVars((std::istreambuf_iterator<char>(cmdline)), std::istreambuf_iterator<char>());
for (const auto& [process, deName] : processChecks) { for (const auto& [process, deName] : processChecks)
if (envVars.find(process) != std::string::npos) if (envVars.find(process) != std::string::npos)
return deName; return deName;
}
return "Unknown"; return "Unknown";
} }
fn CountNix() noexcept -> std::optional<size_t> {
constexpr std::string_view dbPath = "/nix/var/nix/db/db.sqlite";
constexpr std::string_view querySql = "SELECT COUNT(*) FROM ValidPaths WHERE sigs IS NOT NULL;";
sqlite3* sqlDB = nullptr;
sqlite3_stmt* stmt = nullptr;
size_t count = 0;
// 1. Direct URI construction without string concatenation
const std::string uri =
fmt::format("file:{}{}immutable=1", dbPath, (dbPath.find('?') != std::string_view::npos) ? "&" : "?");
// 2. Open database with optimized flags
if (sqlite3_open_v2(uri.c_str(), &sqlDB, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI | SQLITE_OPEN_NOMUTEX, nullptr) !=
SQLITE_OK) {
return std::nullopt;
}
// 3. Configure database for maximum read performance
sqlite3_exec(sqlDB, "PRAGMA journal_mode=OFF; PRAGMA mmap_size=268435456;", nullptr, nullptr, nullptr);
// 4. Single-step prepared statement execution
if (sqlite3_prepare_v3(sqlDB, querySql.data(), querySql.size(), SQLITE_PREPARE_PERSISTENT, &stmt, nullptr) ==
SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
}
sqlite3_finalize(stmt);
}
sqlite3_close(sqlDB);
return count ? std::optional { count } : std::nullopt;
}
fn CountNixWithCache() noexcept -> std::optional<size_t> {
constexpr std::string_view dbPath = "/nix/var/nix/db/db.sqlite";
constexpr std::string_view cachePath = "/tmp/nix_pkg_count.cache";
// 1. Check cache validity atomically
try {
const auto dbMtime = std::filesystem::last_write_time(dbPath);
const auto cacheMtime = std::filesystem::last_write_time(cachePath);
if (std::filesystem::exists(cachePath) && dbMtime <= cacheMtime) {
// Read cached value (atomic read)
std::ifstream cache(cachePath.data(), std::ios::binary);
size_t count = 0;
cache.read(std::bit_cast<char*>(&count), sizeof(count));
return cache ? std::optional(count) : std::nullopt;
}
} catch (...) {} // Ignore errors, fall through to rebuild cache
// 2. Compute fresh value
const auto count = CountNix(); // Original optimized function
// 3. Update cache atomically (write+rename pattern)
if (count) {
constexpr std::string_view tmpPath = "/tmp/nix_pkg_count.tmp";
{
std::ofstream tmp(tmpPath.data(), std::ios::binary | std::ios::trunc);
tmp.write(std::bit_cast<const char*>(&*count), sizeof(*count));
} // RAII close
std::filesystem::rename(tmpPath, cachePath);
}
return count;
}
} }
fn GetOSVersion() -> std::string { fn GetOSVersion() -> std::string {
@ -414,10 +482,33 @@ fn GetNowPlaying() -> string {
const std::map<std::basic_string<char>, sdbus::Variant>& metadata = const std::map<std::basic_string<char>, sdbus::Variant>& metadata =
metadataVariant.get<std::map<std::string, sdbus::Variant>>(); metadataVariant.get<std::map<std::string, sdbus::Variant>>();
auto iter = metadata.find("xesam:title"); std::string title;
auto titleIter = metadata.find("xesam:title");
if (titleIter != metadata.end() && titleIter->second.containsValueOfType<std::string>()) {
title = titleIter->second.get<std::string>();
}
if (iter != metadata.end() && iter->second.containsValueOfType<std::string>()) std::string artist;
return iter->second.get<std::string>(); auto artistIter = metadata.find("xesam:artist");
if (artistIter != metadata.end() && artistIter->second.containsValueOfType<std::vector<std::string>>()) {
auto artists = artistIter->second.get<std::vector<std::string>>();
if (!artists.empty()) {
artist = artists[0];
}
}
std::string result;
if (!artist.empty() && !title.empty()) {
result = artist + " - " + title;
} else if (!title.empty()) {
result = title;
} else if (!artist.empty()) {
result = artist;
} else {
result = "";
}
return result;
} }
} catch (const sdbus::Error& e) { } catch (const sdbus::Error& e) {
if (e.getName() != "com.github.altdesktop.playerctld.NoActivePlayer") { if (e.getName() != "com.github.altdesktop.playerctld.NoActivePlayer") {
@ -430,7 +521,6 @@ fn GetNowPlaying() -> string {
return ""; return "";
} }
fn GetWindowManager() -> string { fn GetWindowManager() -> string {
// Check environment variables first // Check environment variables first
const char* xdgSessionType = std::getenv("XDG_SESSION_TYPE"); const char* xdgSessionType = std::getenv("XDG_SESSION_TYPE");
@ -505,6 +595,8 @@ fn GetKernelVersion() -> string {
return ""; return "";
} }
DEBUG_LOG("{}", CountNixWithCache().value_or(0));
return static_cast<const char*>(uts.release); return static_cast<const char*>(uts.release);
} }

View file

@ -1,218 +0,0 @@
#pragma once
#include <stdexcept>
#include <utility>
#include <variant>
#include "macros.h"
#include "types.h"
/**
* @class Error
* @brief Represents an error with a message.
*
* This class is used to encapsulate error messages that can be returned from functions.
*/
class Error {
public:
/**
* @brief Constructs an Error with a message.
* @param message The error message.
*/
explicit Error(string message) : m_Message(std::move(message)) {}
/**
* @brief Retrieves the error message.
* @return A constant reference to the error message string.
*/
[[nodiscard]] fn message() const -> const string& { return m_Message; }
private:
string m_Message; ///< The error message.
};
// Primary template for Result with a default type of void
/**
* @class Result
* @brief Represents a result that can either be a value or an error.
*
* This is the primary template for Result, which defaults to handling void results.
*/
template <typename T = void>
class Result;
// Specialization for Result<void>
/**
* @class Result<void>
* @brief Specialization of Result for handling void results.
*
* This class is used when a function either succeeds with no value or fails with an error.
*/
template <>
class Result<void> {
public:
/**
* @brief Constructs a successful Result.
*/
Result() : m_Result(std::monostate {}) {}
/**
* @brief Constructs an error Result.
* @param error The error object.
*/
Result(const Error& error) : m_Result(error) {}
/**
* @brief Constructs an error Result.
* @param error An rvalue reference to the error object.
*/
Result(Error&& error) : m_Result(std::move(error)) {}
/**
* @brief Checks if the Result is successful.
* @return True if the Result is successful, otherwise false.
*/
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<std::monostate>(m_Result); }
/**
* @brief Checks if the Result contains an error.
* @return True if the Result contains an error, otherwise false.
*/
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
/**
* @brief Throws an exception if the Result contains an error.
*
* This function should be called only if the Result is successful.
*/
void value() const {
if (isErr()) {
throw std::logic_error("Attempted to access value of an error Result");
}
}
/**
* @brief Retrieves the error object.
* @return A constant reference to the Error object.
* @throws std::logic_error if the Result is successful.
*/
[[nodiscard]] fn error() const -> const Error& {
if (isOk()) {
throw std::logic_error("Attempted to access error of an ok Result");
}
return std::get<Error>(m_Result);
}
private:
std::variant<std::monostate, Error>
m_Result; ///< The underlying result, which can be either void or an Error.
};
// Primary template for Result
/**
* @class Result
* @brief Represents a result that can either be a value of type T or an error.
*
* This template class is used to handle results that can either be a successful value or an error.
* @tparam T The type of the successful value.
*/
template <typename T>
class Result {
public:
/**
* @brief Constructs a successful Result with a value.
* @param value The value of the Result.
*/
Result(const T& value) : m_Result(value) {}
/**
* @brief Constructs a successful Result with a value.
* @param value An rvalue reference to the value.
*/
Result(T&& value) : m_Result(std::move(value)) {}
/**
* @brief Constructs an error Result.
* @param error The error object.
*/
Result(const Error& error) : m_Result(error) {}
/**
* @brief Constructs an error Result.
* @param error An rvalue reference to the error object.
*/
Result(Error&& error) : m_Result(std::move(error)) {}
/**
* @brief Checks if the Result is successful.
* @return True if the Result is successful, otherwise false.
*/
[[nodiscard]] fn isOk() const -> bool { return std::holds_alternative<T>(m_Result); }
/**
* @brief Checks if the Result contains an error.
* @return True if the Result contains an error, otherwise false.
*/
[[nodiscard]] fn isErr() const -> bool { return std::holds_alternative<Error>(m_Result); }
/**
* @brief Retrieves the value.
* @return A constant reference to the value.
* @throws std::logic_error if the Result contains an error.
*/
fn value() const -> const T& {
if (isErr()) {
throw std::logic_error("Attempted to access value of an error Result");
}
return std::get<T>(m_Result);
}
/**
* @brief Retrieves the error object.
* @return A constant reference to the Error object.
* @throws std::logic_error if the Result is successful.
*/
[[nodiscard]] fn error() const -> const Error& {
if (isOk()) {
throw std::logic_error("Attempted to access error of an ok Result");
}
return std::get<Error>(m_Result);
}
/**
* @brief Retrieves the value or returns a default value.
* @param defaultValue The default value to return if the Result contains an error.
* @return The value if the Result is successful, otherwise the default value.
*/
fn valueOr(const T& defaultValue) const -> T {
return isOk() ? std::get<T>(m_Result) : defaultValue;
}
private:
std::variant<T, Error>
m_Result; ///< The underlying result, which can be either a value of type T or an Error.
};
/**
* @brief Helper function to create a successful Result.
*
* This function deduces the type of the value and creates a successful Result.
* @tparam T The type of the value.
* @param value The value to be stored in the Result.
* @return A Result object containing the value.
*/
template <typename T>
fn Ok(T&& value) {
return Result<std::decay_t<T>>(std::forward<T>(value));
}
/**
* @brief Helper function to create a successful Result<void>.
*
* This function creates a successful Result that does not contain a value.
* @return A Result<void> object indicating success.
*/
inline fn Ok() -> Result<void> { return {}; }