parselink-old/include/parselink/logging/formatters.h
2023-10-26 07:23:51 -07:00

155 lines
5.3 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
// {fmt} formatters for various types that are used throughout the codebase.
// Some are specific, while others depend on traits.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef logging_formatters_d22a64b1645a8134
#define logging_formatters_d22a64b1645a8134
#include "theme.h"
#include "traits.h"
#include <magic_enum.hpp>
#include <tl/expected.hpp>
// Simple wrapper for error strings to be formatted like any other string_view.
template <>
struct fmt::formatter<parselink::logging::error_str>
: fmt::formatter<std::string_view> {
template <typename FormatContext>
constexpr auto format(auto const& v, FormatContext& ctx) const {
return fmt::formatter<std::string_view>::format(v.v, ctx);
}
};
// Support enums. By default, enums print the enum type name as well as the
// enum name for the given value. E.g.
//
// enum Color { Red, Green, Blue };
// auto const color = Color::Green;
//
// Logging `color` will yield "Color::Green"
template <typename E>
requires std::is_enum_v<E>
struct fmt::formatter<E> : fmt::formatter<std::string_view> {
template <typename FormatContext>
auto format(E const v, FormatContext& ctx) const {
auto str = [v] {
return fmt::format("{}::{}", magic_enum::enum_type_name<E>(),
magic_enum::enum_name(v));
}();
return fmt::formatter<std::string_view>::format(str, ctx);
}
};
// Support enums that _only_ print the string "name" of the enum value. Using
// the same example above:
//
// enum Color { Red, Green, Blue };
// auto const color = Color::Green;
//
// Logging `enum_name_only{color}` will yield "Green"
template <typename E>
requires std::is_enum_v<E>
struct fmt::formatter<parselink::logging::enum_name_only<E>>
: fmt::formatter<std::string_view> {
using enum_name_only = parselink::logging::enum_name_only<E>;
template <typename FormatContext>
auto format(enum_name_only const v, FormatContext& ctx) const {
return fmt::formatter<std::string_view>::format(
magic_enum::enum_name(v.v), ctx);
}
};
template <>
struct fmt::formatter<std::error_code> : fmt::formatter<std::string_view> {
template <typename FormatContext>
auto format(auto const& v, FormatContext& ctx) const {
return fmt::formatter<std::string_view>::format(v.message(), ctx);
}
};
// Support conversion of typical standard error codes into a human-readable
// string.
template <>
struct fmt::formatter<std::errc> : fmt::formatter<std::error_code> {
template <typename FormatContext>
auto format(std::errc const& v, FormatContext& ctx) const {
return fmt::formatter<std::error_code>::format(
std::make_error_code(v), ctx);
}
};
// Support printing bytes as their hex representation.
template <>
struct fmt::formatter<std::byte> {
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(std::byte const v, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "0x{:0x}", std::uint8_t(v));
}
};
// Support printing raw/smart pointers without needing to wrap them in fmt::ptr
template <parselink::logging::detail::printable_pointer T>
struct fmt::formatter<T> : fmt::formatter<void const*> {
template <typename FormatContext>
auto format(T const& v, FormatContext& ctx) const {
return fmt::formatter<void const*>::format(fmt::ptr(v), ctx);
}
};
// TODO(ksassenrath): Re-enable when expected has been integrated
template <typename T, typename Err>
struct fmt::formatter<tl::expected<T, Err>> {
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(tl::expected<T, Err> const& v, FormatContext& ctx) const {
if (v) {
return fmt::format_to(ctx.out(), "{}",
parselink::logging::format_arg<T>{v.value()});
} else {
return fmt::format_to(ctx.out(), "{}",
parselink::logging::format_arg<Err>{v.error()});
}
}
};
// Support format_arg wrappers, which will be used to colorize output.
template <typename T>
struct fmt::formatter<parselink::logging::format_arg<T>>
: fmt::formatter<typename parselink::logging::format_arg<T>::type> {
using format_arg_type = parselink::logging::format_arg<T>;
using resolved_type = typename format_arg_type::type;
template <typename FormatContext>
auto format(format_arg_type const& v, FormatContext& ctx) const {
return fmt::formatter<resolved_type>::format(v.v, ctx);
}
};
#endif // logging_formatters_d22a64b1645a8134