diff --git a/source/mijin/util/formattable.hpp b/source/mijin/util/formattable.hpp new file mode 100644 index 0000000..7b1f914 --- /dev/null +++ b/source/mijin/util/formattable.hpp @@ -0,0 +1,122 @@ + +#pragma once + +#ifndef MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED +#define MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED 1 + +#include +#include "../container/boxed_object.hpp" + +namespace mijin +{ + +// +// public concepts +// + +template +concept parse_result_type = requires +{ + typename T::parse_result_t; +}; + +template +concept parseable_by_type = parse_result_type && + requires(const T& object, TParseContext& parseContext) +{ + { T::parseFormat(parseContext) } -> std::convertible_to>; +}; + +template +concept simple_formattable_to_type = requires(const T& object, TFmtContext& formatContext) +{ + { object.format(formatContext) } -> std::convertible_to; +}; + +template +concept complex_formattable_to_type = parseable_by_type && + requires(const T& object, TParseContext& parseContext, TFmtContext& formatContext, T::parse_result_t& parseResult) +{ + { object.format(formatContext, parseResult) } -> std::convertible_to; +}; + +template +concept formattable_to_type = simple_formattable_to_type || complex_formattable_to_type; + +template +concept cformattable_type = formattable_to_type; + +template +concept wformattable_type = formattable_to_type; + +template +concept formattable_type = cformattable_type && wformattable_type; + +template +concept any_formattable_type = cformattable_type || wformattable_type; + +// +// internal types +// + +namespace impl +{ +template +struct ParseResult +{ +}; + +template +struct ParseResult +{ + BoxedObject value; +}; +} +} // namespace mijin + +template +struct std::formatter +{ + [[no_unique_address]] [[maybe_unused]] mijin::impl::ParseResult parseResult; + + template + constexpr TContext::iterator parse(TContext& ctx) + { + if constexpr (mijin::parse_result_type) + { + static_assert(mijin::parseable_by_type, "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 + TContext::iterator format(const T& object, TContext& ctx) const + { + if constexpr (mijin::parse_result_type) + { + auto it = object.format(ctx, std::move(*parseResult.value)); + parseResult.destroy(); + return it; + } + else + { + static_assert(mijin::simple_formattable_to_type, "Type does not support formatting to this context."); + return object.format(ctx); + } + } +}; +#endif // MIJIN_UTIL_FORMATTABLE_HPP_INCLUDED