guhg
This commit is contained in:
parent
24b6a72614
commit
1a2fba7fb8
29 changed files with 1676 additions and 1401 deletions
|
@ -1,347 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// Fixes conflict in Windows with <windows.h>
|
||||
#ifdef _WIN32
|
||||
#undef ERROR
|
||||
#endif // _WIN32
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <print>
|
||||
#include <source_location>
|
||||
#include <utility>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/// Macro alias for trailing return type functions.
|
||||
#define fn auto
|
||||
|
||||
/// Macro alias for std::nullopt, represents an empty optional value.
|
||||
#define None std::nullopt
|
||||
|
||||
/**
|
||||
* @namespace term
|
||||
* @brief Provides terminal-related utilities, including color and style formatting.
|
||||
*/
|
||||
namespace term {
|
||||
/**
|
||||
* @enum Emphasis
|
||||
* @brief Represents text emphasis styles.
|
||||
*
|
||||
* Enum values can be combined using bitwise OR to apply multiple styles at once.
|
||||
*/
|
||||
enum class Emphasis : u8 {
|
||||
Bold, ///< Bold text.
|
||||
Italic ///< Italic text.
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum Color
|
||||
* @brief Represents ANSI color codes for terminal output.
|
||||
*
|
||||
* Color codes can be used to format terminal output.
|
||||
*/
|
||||
enum class Color : u8 {
|
||||
Black = 30, ///< Black color.
|
||||
Red = 31, ///< Red color.
|
||||
Green = 32, ///< Green color.
|
||||
Yellow = 33, ///< Yellow color.
|
||||
Blue = 34, ///< Blue color.
|
||||
Magenta = 35, ///< Magenta color.
|
||||
Cyan = 36, ///< Cyan color.
|
||||
White = 37, ///< White color.
|
||||
BrightBlack = 90, ///< Bright black (gray) color.
|
||||
BrightRed = 91, ///< Bright red color.
|
||||
BrightGreen = 92, ///< Bright green color.
|
||||
BrightYellow = 93, ///< Bright yellow color.
|
||||
BrightBlue = 94, ///< Bright blue color.
|
||||
BrightMagenta = 95, ///< Bright magenta color.
|
||||
BrightCyan = 96, ///< Bright cyan color.
|
||||
BrightWhite = 97, ///< Bright white color.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Combines two emphasis styles using bitwise OR.
|
||||
* @param emphA The first emphasis style.
|
||||
* @param emphB The second emphasis style.
|
||||
* @return The combined emphasis style.
|
||||
*/
|
||||
constexpr fn operator|(Emphasis emphA, Emphasis emphB)->Emphasis {
|
||||
return static_cast<Emphasis>(static_cast<u8>(emphA) | static_cast<u8>(emphB));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if two emphasis styles are equal using bitwise AND.
|
||||
* @param emphA The first emphasis style.
|
||||
* @param emphB The second emphasis style.
|
||||
* @return The result of the bitwise AND operation.
|
||||
*/
|
||||
constexpr fn operator&(Emphasis emphA, Emphasis emphB)->u8 { return static_cast<u8>(emphA) & static_cast<u8>(emphB); }
|
||||
|
||||
/**
|
||||
* @struct Style
|
||||
* @brief Represents a combination of text styles.
|
||||
*
|
||||
* Emphasis and color are both optional, allowing for flexible styling.
|
||||
*/
|
||||
struct Style {
|
||||
Option<Emphasis> emph; ///< Optional emphasis style.
|
||||
Option<Color> fg_col; ///< Optional foreground color style.
|
||||
|
||||
/**
|
||||
* @brief Generates the ANSI escape code for the combined styles.
|
||||
* @return The ANSI escape code for the combined styles.
|
||||
*/
|
||||
[[nodiscard]] fn ansiCode() const -> String {
|
||||
String result;
|
||||
|
||||
if (emph) {
|
||||
if ((*emph & Emphasis::Bold) != 0)
|
||||
result += "\033[1m";
|
||||
if ((*emph & Emphasis::Italic) != 0)
|
||||
result += "\033[3m";
|
||||
}
|
||||
|
||||
if (fg_col)
|
||||
result += std::format("\033[{}m", static_cast<u8>(*fg_col));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Combines an emphasis style and a foreground color into a Style.
|
||||
* @param emph The emphasis style to apply.
|
||||
* @param fgColor The foreground color to apply.
|
||||
* @return The combined style.
|
||||
*/
|
||||
// ReSharper disable CppDFAConstantParameter
|
||||
constexpr fn operator|(const Emphasis emph, const Color fgColor)->Style {
|
||||
return { .emph = emph, .fg_col = fgColor };
|
||||
}
|
||||
// ReSharper restore CppDFAConstantParameter
|
||||
|
||||
/**
|
||||
* @brief Combines a foreground color and an emphasis style into a Style.
|
||||
* @param fgColor The foreground color to apply.
|
||||
* @param emph The emphasis style to apply.
|
||||
* @return The combined style.
|
||||
*/
|
||||
constexpr fn operator|(const Color fgColor, const Emphasis emph)->Style {
|
||||
return { .emph = emph, .fg_col = fgColor };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints formatted text with the specified style.
|
||||
* @tparam Args Parameter pack for format arguments.
|
||||
* @param style The Style object containing emphasis and/or color.
|
||||
* @param fmt The format string.
|
||||
* @param args The arguments for the format string.
|
||||
*/
|
||||
template <typename... Args>
|
||||
fn Print(const Style& style, std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||
if (const String styleCode = style.ansiCode(); styleCode.empty())
|
||||
std::print(fmt, std::forward<Args>(args)...);
|
||||
else
|
||||
std::print("{}{}{}", styleCode, std::format(fmt, std::forward<Args>(args)...), "\033[0m");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints formatted text with the specified foreground color.
|
||||
* @tparam Args Parameter pack for format arguments.
|
||||
* @param fgColor The foreground color to apply.
|
||||
* @param fmt The format string.
|
||||
* @param args The arguments for the format string.
|
||||
*/
|
||||
template <typename... Args>
|
||||
fn Print(const Color& fgColor, std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||
Print({ .emph = None, .fg_col = fgColor }, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints formatted text with the specified emphasis style.
|
||||
* @tparam Args Parameter pack for format arguments.
|
||||
* @param emph The emphasis style to apply.
|
||||
* @param fmt The format string.
|
||||
* @param args The arguments for the format string.
|
||||
*/
|
||||
template <typename... Args>
|
||||
fn Print(const Emphasis emph, std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||
Print({ .emph = emph, .fg_col = None }, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prints formatted text with no specific style (default terminal style).
|
||||
* @tparam Args Parameter pack for format arguments.
|
||||
* @param fmt The format string.
|
||||
* @param args The arguments for the format string.
|
||||
*/
|
||||
template <typename... Args>
|
||||
fn Print(std::format_string<Args...> fmt, Args&&... args) -> void {
|
||||
// Directly use std::print for unstyled output
|
||||
std::print(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
} // namespace term
|
||||
|
||||
/**
|
||||
* @enum LogLevel
|
||||
* @brief Represents different log levels.
|
||||
*/
|
||||
enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR };
|
||||
|
||||
/**
|
||||
* @brief Logs a message with the specified log level, source location, and format string.
|
||||
* @tparam Args Parameter pack for format arguments.
|
||||
* @param level The log level (DEBUG, INFO, WARN, ERROR).
|
||||
* @param loc The source location of the log message.
|
||||
* @param fmt The format string.
|
||||
* @param args The arguments for the format string.
|
||||
*/
|
||||
template <typename... Args>
|
||||
fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_string<Args...> fmt, Args&&... args) {
|
||||
using namespace std::chrono;
|
||||
using namespace term;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
using enum term::Color;
|
||||
#else
|
||||
using enum Color;
|
||||
#endif // _MSC_VER
|
||||
|
||||
const auto [color, levelStr] = [&] {
|
||||
switch (level) {
|
||||
case LogLevel::DEBUG: return std::make_pair(Cyan, "DEBUG");
|
||||
case LogLevel::INFO: return std::make_pair(Green, "INFO ");
|
||||
case LogLevel::WARN: return std::make_pair(Yellow, "WARN ");
|
||||
case LogLevel::ERROR: return std::make_pair(Red, "ERROR");
|
||||
default: std::unreachable();
|
||||
}
|
||||
}();
|
||||
|
||||
Print(BrightWhite, "[{:%X}] ", std::chrono::floor<seconds>(system_clock::now()));
|
||||
Print(Emphasis::Bold | color, "{} ", levelStr);
|
||||
Print(fmt, std::forward<Args>(args)...);
|
||||
|
||||
#ifndef NDEBUG
|
||||
Print(BrightWhite, "\n{:>14} ", "╰──");
|
||||
Print(
|
||||
Emphasis::Italic | BrightWhite,
|
||||
"{}:{}",
|
||||
std::filesystem::path(loc.file_name()).lexically_normal().string(),
|
||||
loc.line()
|
||||
);
|
||||
#endif // !NDEBUG
|
||||
|
||||
Print("\n");
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename ErrorType>
|
||||
fn LogAppError(const LogLevel level, const ErrorType& error_obj) {
|
||||
using DecayedErrorType = std::decay_t<ErrorType>;
|
||||
|
||||
std::source_location log_location = std::source_location::current();
|
||||
String error_message_part;
|
||||
LogLevel final_log_level = level;
|
||||
|
||||
if constexpr (std::is_same_v<DecayedErrorType, OsError>) {
|
||||
log_location = error_obj.location;
|
||||
error_message_part = error_obj.message;
|
||||
|
||||
} else if constexpr (std::is_same_v<DecayedErrorType, NowPlayingError>) {
|
||||
if (std::holds_alternative<OsError>(error_obj)) {
|
||||
const OsError& osErr = std::get<OsError>(error_obj);
|
||||
log_location = osErr.location;
|
||||
error_message_part = osErr.message;
|
||||
} else if (std::holds_alternative<NowPlayingCode>(error_obj)) {
|
||||
const NowPlayingCode npCode = std::get<NowPlayingCode>(error_obj);
|
||||
log_location = std::source_location::current();
|
||||
final_log_level = LogLevel::DEBUG;
|
||||
switch (npCode) {
|
||||
case NowPlayingCode::NoPlayers: error_message_part = "No media players found"; break;
|
||||
case NowPlayingCode::NoActivePlayer: error_message_part = "No active media player found"; break;
|
||||
default: error_message_part = "Unknown NowPlayingCode"; break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_location = std::source_location::current();
|
||||
if constexpr (std::is_base_of_v<std::exception, DecayedErrorType>)
|
||||
error_message_part = error_obj.what();
|
||||
else if constexpr (requires { error_obj.message; })
|
||||
error_message_part = error_obj.message;
|
||||
else
|
||||
error_message_part = "Unknown error type logged";
|
||||
}
|
||||
|
||||
LogImpl(final_log_level, log_location, "{}", error_message_part);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// Suppress unused macro warnings in Clang
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-macros"
|
||||
#endif // __clang__
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define DEBUG_LOG(...) static_cast<void>(0)
|
||||
#define DEBUG_LOG_LOC(...) static_cast<void>(0)
|
||||
#else
|
||||
/**
|
||||
* @def DEBUG_LOG
|
||||
* @brief Logs a message at the DEBUG level.
|
||||
* @details Only active in non-release builds (when NDEBUG is not defined).
|
||||
* Includes timestamp, level, message, and source location.
|
||||
* @param ... Format string and arguments for the log message.
|
||||
*/
|
||||
#define DEBUG_LOG(...) LogImpl(LogLevel::DEBUG, std::source_location::current(), __VA_ARGS__)
|
||||
/**
|
||||
* @def DEBUG_LOG_LOC(error_obj)
|
||||
* @brief Logs an application-specific error at the DEBUG level, using its stored location if available.
|
||||
* @details Only active in non-release builds (when NDEBUG is not defined).
|
||||
* @param error_obj The error object (e.g., OsError, NowPlayingError).
|
||||
*/
|
||||
#define DEBUG_LOG_LOC(error_obj) \
|
||||
do { \
|
||||
[&](const auto& err) { detail::LogAppError(LogLevel::DEBUG, err); }(error_obj); \
|
||||
} while (0)
|
||||
#endif // NDEBUG
|
||||
|
||||
/**
|
||||
* @def INFO_LOG(...)
|
||||
* @brief Logs a message at the INFO level.
|
||||
* @details Includes timestamp, level, message, and source location (in debug builds).
|
||||
* @param ... Format string and arguments for the log message.
|
||||
*/
|
||||
#define INFO_LOG(...) LogImpl(LogLevel::INFO, std::source_location::current(), __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @def WARN_LOG(...)
|
||||
* @brief Logs a message at the WARN level.
|
||||
* @details Includes timestamp, level, message, and source location (in debug builds).
|
||||
* @param ... Format string and arguments for the log message.
|
||||
*/
|
||||
#define WARN_LOG(...) LogImpl(LogLevel::WARN, std::source_location::current(), __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @def ERROR_LOG(...)
|
||||
* @brief Logs a message at the ERROR level.
|
||||
* @details Includes timestamp, level, message, and source location (in debug builds).
|
||||
* @param ... Format string and arguments for the log message.
|
||||
*/
|
||||
#define ERROR_LOG(...) LogImpl(LogLevel::ERROR, std::source_location::current(), __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @def ERROR_LOG_LOC(error_obj)
|
||||
* @brief Logs an application-specific error at the ERROR level, using its stored location if available.
|
||||
* @param error_obj The error object (e.g., OsError, NowPlayingError).
|
||||
*/
|
||||
#define ERROR_LOG_LOC(error_obj) \
|
||||
do { \
|
||||
[&](const auto& err) { detail::LogAppError(LogLevel::ERROR, err); }(error_obj); \
|
||||
} while (0)
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif // __clang__
|
383
src/util/types.h
383
src/util/types.h
|
@ -1,383 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array> // std::array alias (Array)
|
||||
#include <cstdlib> // std::getenv, std::free
|
||||
#include <expected> // std::expected alias (Result)
|
||||
#include <format> // std::format
|
||||
#include <map> // std::map alias (Map)
|
||||
#include <memory> // std::shared_ptr and std::unique_ptr aliases (SharedPointer, UniquePointer)
|
||||
#include <optional> // std::optional alias (Option)
|
||||
#include <source_location> // std::source_location
|
||||
#include <string> // std::string and std::string_view aliases (String, StringView)
|
||||
#include <system_error> // std::error_code and std::system_error
|
||||
#include <utility> // std::pair alias (Pair)
|
||||
#include <variant> // std::variant alias (NowPlayingError)
|
||||
#include <vector> // std::vector alias (Vec)
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winrt/base.h> // winrt::hresult_error
|
||||
#elifdef __linux__
|
||||
#include <dbus-cxx.h> // DBus::Error
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------//
|
||||
// Integer Type Aliases //
|
||||
// Provides concise names for standard fixed-width integer types. //
|
||||
//----------------------------------------------------------------//
|
||||
|
||||
using u8 = std::uint8_t; ///< 8-bit unsigned integer.
|
||||
using u16 = std::uint16_t; ///< 16-bit unsigned integer.
|
||||
using u32 = std::uint32_t; ///< 32-bit unsigned integer.
|
||||
using u64 = std::uint64_t; ///< 64-bit unsigned integer.
|
||||
|
||||
using i8 = std::int8_t; ///< 8-bit signed integer.
|
||||
using i16 = std::int16_t; ///< 16-bit signed integer.
|
||||
using i32 = std::int32_t; ///< 32-bit signed integer.
|
||||
using i64 = std::int64_t; ///< 64-bit signed integer.
|
||||
|
||||
//-----------------------------------------------------------//
|
||||
// Floating-Point Type Aliases //
|
||||
// Provides concise names for standard floating-point types. //
|
||||
//-----------------------------------------------------------//
|
||||
|
||||
using f32 = float; ///< 32-bit floating-point number.
|
||||
using f64 = double; ///< 64-bit floating-point number.
|
||||
|
||||
//-------------------------------------------------//
|
||||
// Size Type Aliases //
|
||||
// Provides concise names for standard size types. //
|
||||
//-------------------------------------------------//
|
||||
|
||||
using usize = std::size_t; ///< Unsigned size type (result of sizeof).
|
||||
using isize = std::ptrdiff_t; ///< Signed size type (result of pointer subtraction).
|
||||
|
||||
//---------------------------------------------------//
|
||||
// String Type Aliases //
|
||||
// Provides concise names for standard string types. //
|
||||
//---------------------------------------------------//
|
||||
|
||||
using String = std::string; ///< Owning, mutable string.
|
||||
using StringView = std::string_view; ///< Non-owning view of a string.
|
||||
using CStr = const char*; ///< Pointer to a null-terminated C-style string.
|
||||
|
||||
//----------------------------------------------------//
|
||||
// Standard Library Type Aliases //
|
||||
// Provides concise names for standard library types. //
|
||||
//----------------------------------------------------//
|
||||
|
||||
using Exception = std::exception; ///< Standard exception type.
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @tparam Tp The type of the potential value.
|
||||
*/
|
||||
template <typename Tp>
|
||||
using Option = std::optional<Tp>;
|
||||
|
||||
/**
|
||||
* @typedef Array
|
||||
* @brief Alias for std::array<Tp, sz>. Represents a fixed-size array.
|
||||
* @tparam Tp The element type.
|
||||
* @tparam sz The size of the array.
|
||||
*/
|
||||
template <typename Tp, usize sz>
|
||||
using Array = std::array<Tp, sz>;
|
||||
|
||||
/**
|
||||
* @typedef Vec
|
||||
* @brief Alias for std::vector<Tp>. Represents a dynamic-size array (vector).
|
||||
* @tparam Tp The element type.
|
||||
*/
|
||||
template <typename Tp>
|
||||
using Vec = std::vector<Tp>;
|
||||
|
||||
/**
|
||||
* @typedef Pair
|
||||
* @brief Alias for std::pair<T1, T2>. Represents a pair of values.
|
||||
* @tparam T1 The type of the first element.
|
||||
* @tparam T2 The type of the second element.
|
||||
*/
|
||||
template <typename T1, typename T2>
|
||||
using Pair = std::pair<T1, T2>;
|
||||
|
||||
/**
|
||||
* @typedef Map
|
||||
* @brief Alias for std::map<Key, Val>. Represents an ordered map (dictionary).
|
||||
* @tparam Key The key type.
|
||||
* @tparam Val The value type.
|
||||
*/
|
||||
template <typename Key, typename Val>
|
||||
using Map = std::map<Key, Val>;
|
||||
|
||||
/**
|
||||
* @typedef SharedPointer
|
||||
* @brief Alias for std::shared_ptr<Tp>. Manages shared ownership of a dynamically allocated object.
|
||||
* @tparam Tp The type of the managed object.
|
||||
*/
|
||||
template <typename Tp>
|
||||
using SharedPointer = std::shared_ptr<Tp>;
|
||||
|
||||
/**
|
||||
* @typedef UniquePointer
|
||||
* @brief Alias for std::unique_ptr<Tp, Dp>. Manages unique ownership of a dynamically allocated object.
|
||||
* @tparam Tp The type of the managed object.
|
||||
* @tparam Dp The deleter type (defaults to std::default_delete<Tp>).
|
||||
*/
|
||||
template <typename Tp, typename Dp = std::default_delete<Tp>>
|
||||
using UniquePointer = std::unique_ptr<Tp, Dp>;
|
||||
|
||||
//--------------------------------------------------------//
|
||||
// Application-Specific Type Aliases //
|
||||
// Provides concise names for application-specific types. //
|
||||
//--------------------------------------------------------//
|
||||
|
||||
/**
|
||||
* @enum NowPlayingCode
|
||||
* @brief Error codes specific to the Now Playing feature.
|
||||
*/
|
||||
enum class NowPlayingCode : u8 {
|
||||
NoPlayers, ///< No media players were found (e.g., no MPRIS services on Linux).
|
||||
NoActivePlayer, ///< Players were found, but none are currently active or playing.
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum OsErrorCode
|
||||
* @brief Error codes for general OS-level operations.
|
||||
*/
|
||||
enum class OsErrorCode : u8 {
|
||||
IoError, ///< General I/O error (filesystem, pipes, etc.).
|
||||
PermissionDenied, ///< Insufficient permissions to perform the operation.
|
||||
NotFound, ///< A required resource (file, registry key, device, API endpoint) was not found.
|
||||
ParseError, ///< Failed to parse data obtained from the OS (e.g., file content, API output).
|
||||
ApiUnavailable, ///< A required OS service/API is unavailable or failed unexpectedly at runtime.
|
||||
NotSupported, ///< The requested operation is not supported on this platform, version, or configuration.
|
||||
Timeout, ///< An operation timed out (e.g., waiting for IPC reply).
|
||||
BufferTooSmall, ///< Optional: Keep if using fixed C-style buffers, otherwise remove.
|
||||
InternalError, ///< An error occurred within the application's OS abstraction code logic.
|
||||
NetworkError, ///< A network-related error occurred (e.g., DNS resolution, connection failure).
|
||||
PlatformSpecific, ///< An unmapped error specific to the underlying OS platform occurred (check message).
|
||||
Other, ///< A generic or unclassified error originating from the OS or an external library.
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct OsError
|
||||
* @brief Holds structured information about an OS-level error.
|
||||
*
|
||||
* Used as the error type in Result for many os:: functions.
|
||||
*/
|
||||
struct OsError {
|
||||
// ReSharper disable CppDFANotInitializedField
|
||||
String message; ///< A descriptive error message, potentially including platform details.
|
||||
OsErrorCode code; ///< The general category of the error.
|
||||
std::source_location location; ///< The source location where the error occurred (file, line, function).
|
||||
// ReSharper restore CppDFANotInitializedField
|
||||
|
||||
OsError(const OsErrorCode errc, String msg, const std::source_location& loc = std::source_location::current())
|
||||
: message(std::move(msg)), code(errc), location(loc) {}
|
||||
|
||||
explicit OsError(const Exception& exc, const std::source_location& loc = std::source_location::current())
|
||||
: message(exc.what()), code(OsErrorCode::InternalError), location(loc) {}
|
||||
|
||||
explicit OsError(const std::error_code& errc, const std::source_location& loc = std::source_location::current())
|
||||
: message(errc.message()), location(loc) {
|
||||
using enum OsErrorCode;
|
||||
using enum std::errc;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
explicit OsError(const winrt::hresult_error& e) : message(winrt::to_string(e.message())) {
|
||||
switch (e.code()) {
|
||||
case E_ACCESSDENIED: code = OsErrorCode::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 = OsErrorCode::NotFound; break;
|
||||
case HRESULT_FROM_WIN32(ERROR_TIMEOUT):
|
||||
case HRESULT_FROM_WIN32(ERROR_SEM_TIMEOUT): code = OsErrorCode::Timeout; break;
|
||||
case HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED): code = OsErrorCode::NotSupported; break;
|
||||
default: code = OsErrorCode::PlatformSpecific; break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
OsError(const OsErrorCode code_hint, const int errno_val)
|
||||
: message(std::system_category().message(errno_val)), code(code_hint) {
|
||||
using enum OsErrorCode;
|
||||
|
||||
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())
|
||||
-> OsError {
|
||||
const i32 errNo = errno;
|
||||
const String msg = std::system_category().message(errNo);
|
||||
const String fullMsg = std::format("{}: {}", context, msg);
|
||||
|
||||
OsErrorCode code;
|
||||
switch (errNo) {
|
||||
case EACCES:
|
||||
case EPERM: code = OsErrorCode::PermissionDenied; break;
|
||||
case ENOENT: code = OsErrorCode::NotFound; break;
|
||||
case ETIMEDOUT: code = OsErrorCode::Timeout; break;
|
||||
case ENOTSUP: code = OsErrorCode::NotSupported; break;
|
||||
case EIO: code = OsErrorCode::IoError; break;
|
||||
case ECONNREFUSED:
|
||||
case ENETDOWN:
|
||||
case ENETUNREACH: code = OsErrorCode::NetworkError; break;
|
||||
default: code = OsErrorCode::PlatformSpecific; break;
|
||||
}
|
||||
|
||||
return OsError { code, fullMsg, loc };
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static auto fromDBus(const DBus::Error& err, const std::source_location& loc = std::source_location::current())
|
||||
-> OsError {
|
||||
String name = err.name();
|
||||
OsErrorCode codeHint = OsErrorCode::PlatformSpecific;
|
||||
String message;
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
if (name == "org.freedesktop.DBus.Error.ServiceUnknown"sv ||
|
||||
name == "org.freedesktop.DBus.Error.NameHasNoOwner"sv) {
|
||||
codeHint = OsErrorCode::NotFound;
|
||||
message = std::format("DBus service/name not found: {}", err.message());
|
||||
} else if (name == "org.freedesktop.DBus.Error.NoReply"sv || name == "org.freedesktop.DBus.Error.Timeout"sv) {
|
||||
codeHint = OsErrorCode::Timeout;
|
||||
message = std::format("DBus timeout/no reply: {}", err.message());
|
||||
} else if (name == "org.freedesktop.DBus.Error.AccessDenied"sv) {
|
||||
codeHint = OsErrorCode::PermissionDenied;
|
||||
message = std::format("DBus access denied: {}", err.message());
|
||||
} else {
|
||||
message = std::format("DBus error: {} - {}", name, err.message());
|
||||
}
|
||||
|
||||
return OsError { codeHint, message, loc };
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct DiskSpace
|
||||
* @brief Represents disk usage information.
|
||||
*
|
||||
* 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.
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct MediaInfo
|
||||
* @brief Holds structured metadata about currently playing media.
|
||||
*
|
||||
* Used as the success type for os::GetNowPlaying.
|
||||
* Using Option<> for fields that might not always be available.
|
||||
*/
|
||||
struct MediaInfo {
|
||||
Option<String> title; ///< Track title.
|
||||
Option<String> artist; ///< Track artist(s).
|
||||
Option<String> album; ///< Album name.
|
||||
Option<String> app_name; ///< Name of the media player application (e.g., "Spotify", "Firefox").
|
||||
|
||||
MediaInfo() = default;
|
||||
|
||||
MediaInfo(Option<String> title, Option<String> artist) : title(std::move(title)), artist(std::move(artist)) {}
|
||||
|
||||
MediaInfo(Option<String> title, Option<String> artist, Option<String> album, Option<String> app)
|
||||
: title(std::move(title)), artist(std::move(artist)), album(std::move(album)), app_name(std::move(app)) {}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------//
|
||||
// Potentially Update Existing Application-Specific Types //
|
||||
//--------------------------------------------------------//
|
||||
|
||||
/**
|
||||
* @typedef NowPlayingError (Updated Recommendation)
|
||||
* @brief Represents the possible errors returned by os::GetNowPlaying.
|
||||
*
|
||||
* It's a variant that can hold either a specific NowPlayingCode
|
||||
* (indicating player state like 'no active player') or a general OsError
|
||||
* (indicating an underlying system/API failure).
|
||||
*/
|
||||
using NowPlayingError = std::variant<NowPlayingCode, OsError>;
|
||||
|
||||
/**
|
||||
* @enum EnvError
|
||||
* @brief Error codes for environment variable retrieval.
|
||||
*/
|
||||
enum class EnvError : u8 {
|
||||
NotFound, ///< Environment variable not found.
|
||||
AccessError, ///< Access error when trying to retrieve the variable.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Safely retrieves an environment variable.
|
||||
* @param name The name of the environment variable to retrieve.
|
||||
* @return A Result containing the value of the environment variable as a String,
|
||||
* or an EnvError if an error occurred.
|
||||
*/
|
||||
[[nodiscard]] inline auto GetEnv(CStr name) -> Result<String, EnvError> {
|
||||
#ifdef _WIN32
|
||||
char* rawPtr = nullptr;
|
||||
usize bufferSize = 0;
|
||||
|
||||
// Use _dupenv_s to safely retrieve environment variables on Windows
|
||||
const i32 err = _dupenv_s(&rawPtr, &bufferSize, name);
|
||||
|
||||
const UniquePointer<char, decltype(&free)> ptrManager(rawPtr, free);
|
||||
|
||||
if (err != 0)
|
||||
return Err(EnvError::AccessError); // Error retrieving environment variable
|
||||
|
||||
if (!ptrManager)
|
||||
return Err(EnvError::NotFound); // Environment variable not found
|
||||
|
||||
return ptrManager.get();
|
||||
#else
|
||||
// Use std::getenv to retrieve environment variables on POSIX systems
|
||||
const CStr value = std::getenv(name);
|
||||
|
||||
if (!value)
|
||||
return Err(EnvError::NotFound); // Environment variable not found
|
||||
|
||||
return value;
|
||||
#endif
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue