#pragma once #ifdef _WIN32 #undef ERROR #endif #include #include #include #include #include #include #include "types.h" #define fn auto namespace term { enum class Emphasis : u8 { none = 0, bold = 1, italic = 2 }; constexpr fn operator|(Emphasis emphA, Emphasis emphB)->Emphasis { return static_cast(static_cast(emphA) | static_cast(emphB)); } enum class Color : u8 { black = 30, red = 31, green = 32, yellow = 33, blue = 34, magenta = 35, cyan = 36, white = 37, bright_black = 90, bright_red = 91, bright_green = 92, bright_yellow = 93, bright_blue = 94, bright_magenta = 95, bright_cyan = 96, bright_white = 97 }; struct FgColor { Color col; constexpr explicit FgColor(Color color) : col(color) {} [[nodiscard]] fn ansiCode() const -> String { return std::format("\033[{}m", static_cast(col)); } }; constexpr fn Fg(Color color) -> FgColor { return FgColor(color); } struct Style { Emphasis emph = Emphasis::none; FgColor fg_col = FgColor(static_cast(-1)); // Invalid color [[nodiscard]] fn ansiCode() const -> String { String result; if (emph != Emphasis::none) { if ((static_cast(emph) & static_cast(Emphasis::bold)) != 0) result += "\033[1m"; if ((static_cast(emph) & static_cast(Emphasis::italic)) != 0) result += "\033[3m"; } if (static_cast(fg_col.col) != -1) result += fg_col.ansiCode(); return result; } }; constexpr fn operator|(Emphasis emph, FgColor fgColor)->Style { return { .emph = emph, .fg_col = fgColor }; } constexpr fn operator|(FgColor fgColor, Emphasis emph)->Style { return { .emph = emph, .fg_col = fgColor }; } constexpr const char* reset = "\033[0m"; template fn Print(const Style& style, std::format_string fmt, Args&&... args) -> void { std::print("{}{}{}", style.ansiCode(), std::format(fmt, std::forward(args)...), reset); } template fn Print(const FgColor& fgColor, std::format_string fmt, Args&&... args) -> void { std::print("{}{}{}", fgColor.ansiCode(), std::format(fmt, std::forward(args)...), reset); } template fn Print(Emphasis emph, std::format_string fmt, Args&&... args) -> void { Print({ .emph = emph }, fmt, std::forward(args)...); } template fn Print(std::format_string fmt, Args&&... args) -> void { std::print(fmt, std::forward(args)...); } } namespace log_colors { using term::Color; constexpr Color debug = Color::cyan, info = Color::green, warn = Color::yellow, error = Color::red, timestamp = Color::bright_white, file_info = Color::bright_white; } enum class LogLevel : u8 { DEBUG, INFO, WARN, ERROR }; template fn LogImpl(const LogLevel level, const std::source_location& loc, std::format_string fmt, Args&&... args) -> void { using namespace std::chrono; const time_point>> now = std::chrono::floor(std::chrono::system_clock::now()); const auto [color, levelStr] = [&] { switch (level) { case LogLevel::DEBUG: return std::make_pair(log_colors::debug, "DEBUG"); case LogLevel::INFO: return std::make_pair(log_colors::info, "INFO "); case LogLevel::WARN: return std::make_pair(log_colors::warn, "WARN "); case LogLevel::ERROR: return std::make_pair(log_colors::error, "ERROR"); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcovered-switch-default" default: std::unreachable(); #pragma clang diagnostic pop } }(); const String filename = std::filesystem::path(loc.file_name()).lexically_normal().string(); using namespace term; Print(Fg(log_colors::timestamp), "[{:%H:%M:%S}] ", now); Print(Emphasis::bold | Fg(color), "{} ", levelStr); Print(fmt, std::forward(args)...); #ifndef NDEBUG Print(Fg(log_colors::file_info), "\n{:>14} ", "╰──"); Print(Emphasis::italic | Fg(log_colors::file_info), "{}:{}", filename, loc.line()); #endif Print("\n"); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-macros" #ifdef NDEBUG #define DEBUG_LOG(...) static_cast(0) #else #define DEBUG_LOG(...) LogImpl(LogLevel::DEBUG, std::source_location::current(), __VA_ARGS__) #endif #define INFO_LOG(...) LogImpl(LogLevel::INFO, std::source_location::current(), __VA_ARGS__) #define WARN_LOG(...) LogImpl(LogLevel::WARN, std::source_location::current(), __VA_ARGS__) #define ERROR_LOG(...) LogImpl(LogLevel::ERROR, std::source_location::current(), __VA_ARGS__) #pragma clang diagnostic pop