123 lines
3.4 KiB
C++
123 lines
3.4 KiB
C++
|
|
#pragma once
|
|
|
|
#ifndef MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED
|
|
#define MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED 1
|
|
|
|
#include <format>
|
|
#include "../container/boxed_object.hpp"
|
|
|
|
namespace mijin
|
|
{
|
|
|
|
//
|
|
// public concepts
|
|
//
|
|
|
|
template<typename T>
|
|
concept parse_result_type = requires
|
|
{
|
|
typename T::parse_result_t;
|
|
};
|
|
|
|
template<typename T, typename TParseContext>
|
|
concept parseable_by_type = parse_result_type<T> &&
|
|
requires(const T& object, TParseContext& parseContext)
|
|
{
|
|
{ T::parseFormat(parseContext) } -> std::convertible_to<std::pair<typename TParseContext::iterator, typename T::parse_result_t>>;
|
|
};
|
|
|
|
template<typename T, typename TFmtContext>
|
|
concept simple_formattable_to_type = requires(const T& object, TFmtContext& formatContext)
|
|
{
|
|
{ object.format(formatContext) } -> std::convertible_to<typename TFmtContext::iterator>;
|
|
};
|
|
|
|
template<typename T, typename TFmtContext, typename TParseContext>
|
|
concept complex_formattable_to_type = parseable_by_type<T, TParseContext> &&
|
|
requires(const T& object, TParseContext& parseContext, TFmtContext& formatContext, T::parse_result_t& parseResult)
|
|
{
|
|
{ object.format(formatContext, parseResult) } -> std::convertible_to<typename TFmtContext::iterator>;
|
|
};
|
|
|
|
template<typename T, typename TFmtContext, typename TParseContext>
|
|
concept formattable_to_type = simple_formattable_to_type<T, TFmtContext> || complex_formattable_to_type<T, TFmtContext, TParseContext>;
|
|
|
|
template<typename T>
|
|
concept cformattable_type = formattable_to_type<T, std::format_context, std::format_parse_context>;
|
|
|
|
template<typename T>
|
|
concept wformattable_type = formattable_to_type<T, std::wformat_context, std::wformat_parse_context>;
|
|
|
|
template<typename T>
|
|
concept formattable_type = cformattable_type<T> && wformattable_type<T>;
|
|
|
|
template<typename T>
|
|
concept any_formattable_type = cformattable_type<T> || wformattable_type<T>;
|
|
|
|
//
|
|
// internal types
|
|
//
|
|
|
|
namespace impl
|
|
{
|
|
template<typename T>
|
|
struct ParseResult
|
|
{
|
|
};
|
|
|
|
template<parse_result_type T>
|
|
struct ParseResult<T>
|
|
{
|
|
BoxedObject<typename T::parse_result_t> value;
|
|
};
|
|
}
|
|
} // namespace mijin
|
|
|
|
template<mijin::any_formattable_type T>
|
|
struct std::formatter<T>
|
|
{
|
|
[[no_unique_address]] [[maybe_unused]] mijin::impl::ParseResult<T> parseResult;
|
|
|
|
template<typename TContext>
|
|
constexpr TContext::iterator parse(TContext& ctx)
|
|
{
|
|
if constexpr (mijin::parse_result_type<T>)
|
|
{
|
|
static_assert(mijin::parseable_by_type<T, TContext>, "Type does not support parsing by this context.");
|
|
auto [it, result] = T::parseFormat(ctx);
|
|
parseResult.value.construct(std::move(result));
|
|
return it;
|
|
}
|
|
else
|
|
{
|
|
auto it = ctx.begin();
|
|
auto end = ctx.end();
|
|
|
|
if (it != end && *it != '}')
|
|
{
|
|
throw std::format_error("invalid format");
|
|
}
|
|
|
|
return it;
|
|
}
|
|
}
|
|
|
|
template<typename TContext>
|
|
TContext::iterator format(const T& object, TContext& ctx) const
|
|
{
|
|
if constexpr (mijin::parse_result_type<T>)
|
|
{
|
|
auto it = object.format(ctx, std::move(*parseResult.value));
|
|
parseResult.destroy();
|
|
return it;
|
|
}
|
|
else
|
|
{
|
|
static_assert(mijin::simple_formattable_to_type<T, TContext>, "Type does not support formatting to this context.");
|
|
return object.format(ctx);
|
|
}
|
|
}
|
|
};
|
|
#endif // MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED
|