parselink-old/include/parselink/logging/theme.h
Kurt Sassenrath 2a4c819f4f Fix approach to include paths.
This accommodates bazel's best practices a bit better.
2023-10-04 21:23:36 -07:00

213 lines
5.9 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// 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 <parselink/util/expected.h> */
#include <fmt/color.h>
#include <system_error>
#include <type_traits>
namespace parselink {
namespace logging {
template <typename T>
struct format_arg{
using type = std::decay_t<T>;
constexpr format_arg(type const& t) : v(t) {}
type const& v;
};
template <std::convertible_to<std::string_view> T>
struct format_arg<T> {
using type = std::string_view;
constexpr format_arg(T&& t) noexcept : v(t) {}
type v;
};
template <detail::smart_pointer T>
struct format_arg<T> {
using type = void const*;
constexpr format_arg(T const& t) noexcept : v(fmt::ptr(t)) {}
void const* v;
};
template <typename>
struct theme {};
template <fmt::color Color>
struct static_theme {
constexpr static auto style = fmt::fg(Color);
};
template <std::integral T>
struct theme<T> : static_theme<fmt::color::light_sky_blue> {};
template <std::convertible_to<std::string_view> T>
struct theme<T> : static_theme<fmt::color::pale_green> {};
template <detail::printable_pointer T>
struct theme<T> : static_theme<fmt::color::pale_violet_red> {};
template <typename E>
requires std::is_enum_v<E>
struct theme<E> : static_theme<fmt::color::gray> {};
// Errors
template <>
struct theme<std::error_code> : static_theme<fmt::color::fire_brick> {};
template <>
struct theme<std::errc> : static_theme<fmt::color::fire_brick> {};
template <>
struct theme<error_str> : static_theme<fmt::color::fire_brick> {};
template <>
struct theme<enum_name_only<logging::level>> {
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 <typename T>
concept has_static_theme =
std::convertible_to<fmt::text_style,
decltype(theme<std::remove_cvref_t<T>>::style)>;
template <typename T>
concept has_dynamic_theme = requires (T const& t) {
{ theme<std::remove_cvref_t<T>>::style(t) }
-> std::convertible_to<fmt::text_style>;
};
template <typename T>
concept has_theme = has_static_theme<T> || has_dynamic_theme<T>;
static_assert(has_static_theme<std::errc>);
template <typename T>
constexpr auto get_theme(T const&) {
return fmt::text_style{};
}
template <has_static_theme T>
constexpr auto get_theme(T const&) {
return theme<T>::style;
}
template <has_dynamic_theme T>
constexpr auto get_theme(T const& value) {
return theme<T>::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<char>(style.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
}
return out;
}
#if 0
// TODO(ksassenrath): Enable when expected is supported
template <typename T, typename Err>
struct theme<expected<T, Err>> {
static constexpr auto style(auto const& e) noexcept {
if (e.has_value()) {
return get_theme(e.value());
} else {
return get_theme(e.error());
}
}
};
#endif
template <typename T>
struct themed_arg : format_arg<T> {};
template <typename T>
themed_arg(T&&) -> themed_arg<T>;
template <typename... Args>
fmt::format_args themed_args(Args&&... args) {
return fmt::format_arg_store<fmt::format_context>(
themed_arg<Args>{std::forward<Args>(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 <typename T>
struct fmt::formatter<parselink::logging::themed_arg<T>>
: fmt::formatter<typename parselink::logging::format_arg<T>::type> {
using type = std::remove_cvref_t<T>;
using themed_arg = parselink::logging::themed_arg<T>;
using format_arg = parselink::logging::format_arg<T>;
using resolved_type = typename format_arg::type;
template <typename FormatContext>
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<resolved_type>::format(v.v, ctx);
if constexpr (parselink::logging::has_theme<type>) {
auto reset_color = parselink::logging::reset_theme;
out = std::copy(reset_color.begin(), reset_color.end(), out);
}
return out;
}
};
#endif // log_theme_8e9601cc066b20bf