//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: Logging // // The logging facilities within parselink colorize output by default. This // is achieved by wrapping each formatted argument with an empty structure // and specializing the theme structure for the type. // // If no theme is specified for a type, the text will be rendered with the // default color. // // TODO(ksassenrath): // 1. Make configurable with a text file. // 2. Defer colorization for logging endpoints which do not support/desire // colored output. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #ifndef log_theme_8e9601cc066b20bf #define log_theme_8e9601cc066b20bf #include "level.h" #include "traits.h" #include #include #include #include namespace parselink { namespace logging { template struct format_arg{ using type = std::decay_t; constexpr format_arg(type const& t) : v(t) {} type const& v; }; template T> struct format_arg { using type = std::string_view; constexpr format_arg(T&& t) noexcept : v(t) {} type v; }; template struct format_arg { using type = void const*; constexpr format_arg(T const& t) noexcept : v(fmt::ptr(t)) {} void const* v; }; template struct theme {}; template struct static_theme { constexpr static auto style = fmt::fg(Color); }; template requires (!std::same_as) struct theme : static_theme {}; template <> struct theme : static_theme {}; template T> struct theme : static_theme {}; template struct theme : static_theme {}; template requires std::is_enum_v struct theme : static_theme {}; // Errors template <> struct theme : static_theme {}; template <> struct theme : static_theme {}; template <> struct theme : static_theme {}; template <> struct theme { static constexpr auto style(bool l) noexcept { return fmt::fg(l ? fmt::color::spring_green : fmt::color::indian_red); } }; template <> struct theme> { constexpr static fmt::color colors[] = { fmt::color::black, fmt::color::red, fmt::color::fire_brick, fmt::color::golden_rod, fmt::color::light_sky_blue, fmt::color::lime_green, fmt::color::pink, fmt::color::slate_gray }; static constexpr auto style(auto l) noexcept { return fmt::fg(*std::next(std::begin(colors), size_t(l.v))); } }; template concept has_static_theme = std::convertible_to>::style)>; template concept has_dynamic_theme = requires (T const& t) { { theme>::style(t) } -> std::convertible_to; }; template concept has_theme = has_static_theme || has_dynamic_theme; template requires (has_theme) struct theme> : theme {}; template constexpr auto get_theme(T const&) { return fmt::text_style{}; } template constexpr auto get_theme(T const&) { return theme::style; } template constexpr auto get_theme(T const& value) { return theme::style(value); } [[gnu::always_inline]] constexpr auto styled(fmt::text_style const& style, auto out) { if (style.has_foreground()) { auto foreground = fmt::detail::make_foreground_color(style.get_foreground()); out = std::copy(foreground.begin(), foreground.end(), out); } return out; } template struct theme> { static constexpr auto style(auto const& e) noexcept { return e ? get_theme(e.value()) : get_theme(e.error()); } }; template struct themed_arg : format_arg {}; template themed_arg(T&&) -> themed_arg; template fmt::format_args themed_args(Args&&... args) { return fmt::format_arg_store( themed_arg{std::forward(args)}...); } constexpr inline std::string_view reset_theme{"\x1b[0m"}; } // namespace logging } // namespace parselink // Colorize the text but forward the actual formatting itself to the original // formatter. template struct fmt::formatter> : fmt::formatter::type> { using type = std::remove_cvref_t; using themed_arg = parselink::logging::themed_arg; using format_arg = parselink::logging::format_arg; using resolved_type = typename format_arg::type; template auto format(themed_arg const& v, FormatContext& ctx) const { auto out = ctx.out(); out = parselink::logging::styled( parselink::logging::get_theme(v.v), out); out = fmt::formatter::format(v.v, ctx); if constexpr (parselink::logging::has_theme) { auto reset_color = parselink::logging::reset_theme; out = std::copy(reset_color.begin(), reset_color.end(), out); } return out; } }; #endif // log_theme_8e9601cc066b20bf