228 lines
6.2 KiB
C++
228 lines
6.2 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 <span>
|
|
#include <system_error>
|
|
#include <type_traits>
|
|
|
|
#include <fmt/color.h>
|
|
#include <tl/expected.hpp>
|
|
|
|
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>
|
|
requires(!std::same_as<T, bool>)
|
|
struct theme<T> : static_theme<fmt::color::light_sky_blue> {};
|
|
|
|
template <>
|
|
struct theme<std::byte> : 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<bool> {
|
|
static constexpr auto style(bool l) noexcept {
|
|
return fmt::fg(l ? fmt::color::spring_green : fmt::color::indian_red);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct theme<enum_name_only<logging::level>> {
|
|
// clang-format off
|
|
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 };
|
|
|
|
// clang-format on
|
|
|
|
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>;
|
|
|
|
template <typename T>
|
|
requires(has_theme<T>)
|
|
struct theme<std::span<T>> : theme<T> {};
|
|
|
|
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;
|
|
}
|
|
|
|
template <typename T, typename Err>
|
|
struct theme<tl::expected<T, Err>> {
|
|
static constexpr auto style(auto const& e) noexcept {
|
|
return e ? get_theme(e.value()) : get_theme(e.error());
|
|
}
|
|
};
|
|
|
|
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
|