// ReSharper disable CppDFAConstantParameter #pragma once // Fixes conflict in Windows with #ifdef _WIN32 #undef ERROR #endif #include #include #include #include #include #include #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(static_cast(emphA) | static_cast(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(emphA) & static_cast(emphB); } /** * @struct Style * @brief Represents a combination of text styles. * * Emphasis and color are both optional, allowing for flexible styling. */ struct Style { Option emph; ///< Optional emphasis style. Option 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(*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. */ constexpr fn operator|(const Emphasis emph, const Color fgColor)->Style { return { .emph = emph, .fg_col = fgColor }; } /** * @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 fn Print(const Style& style, std::format_string fmt, Args&&... args) -> void { if (const String styleCode = style.ansiCode(); styleCode.empty()) std::print(fmt, std::forward(args)...); else std::print("{}{}{}", styleCode, std::format(fmt, std::forward(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 fn Print(const Color& fgColor, std::format_string fmt, Args&&... args) -> void { Print({ .emph = None, .fg_col = fgColor }, fmt, std::forward(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 fn Print(const Emphasis emph, std::format_string fmt, Args&&... args) -> void { Print({ .emph = emph, .fg_col = None }, fmt, std::forward(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 fn Print(std::format_string fmt, Args&&... args) -> void { // Directly use std::print for unstyled output std::print(fmt, std::forward(args)...); } } /** * @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 fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_string fmt, Args&&... args) { using namespace std::chrono; using namespace term; #ifdef _WIN32 using enum term::Color; #else using enum Color; #endif 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}] ", zoned_time { current_zone(), std::chrono::floor(system_clock::now()) }); Print(Emphasis::Bold | color, "{} ", levelStr); Print(fmt, std::forward(args)...); #ifndef NDEBUG Print(BrightWhite, "\n{:>14} ", "╰──"); Print( Emphasis::Italic | BrightWhite, "{}:{}", std::filesystem::path(loc.file_name()).lexically_normal().string(), loc.line() ); #endif Print("\n"); } // Suppress unused macro warnings in Clang #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-macros" #endif #ifdef NDEBUG #define DEBUG_LOG(...) static_cast(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__) #endif /** * @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__) #ifdef __clang__ #pragma clang diagnostic pop #endif