#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace sc { namespace detail { template concept compile_time_field = std::same_as>; template [[nodiscard]] CONSTEVAL auto field_value(T) { if constexpr (std::is_enum_v) { return stdx::enum_as_string(); } else { return T::value; } } template [[nodiscard]] CONSTEVAL auto field_value(sc::type_name) { return stdx::type_as_string(); } template constexpr auto format1(Fmt, Arg arg) { constexpr auto str = [&] { constexpr auto fmtstr = FMT_COMPILE(Fmt::value); constexpr auto sz = fmt::formatted_size(fmtstr, field_value(arg)); std::array buf{}; fmt::format_to(std::begin(buf), fmtstr, field_value(arg)); return buf; }(); return [&](std::index_sequence) { return string_constant{}; }(std::make_index_sequence{}); } template constexpr auto split_format_spec() { constexpr Fmt fmt{}; constexpr auto spec_start = std::adjacent_find( std::begin(fmt), std::end(fmt), [](auto c1, auto c2) { return c1 == '{' and c2 != '{'; }); if constexpr (spec_start == std::end(fmt)) { return std::pair{fmt, ""_sc}; } else { constexpr auto spec_end = std::find_if(spec_start, std::end(fmt), [](auto c) { return c == '}'; }); constexpr auto len = static_cast(std::distance(std::begin(fmt), spec_end) + 1); return std::pair{fmt.substr(int_<0>, int_), fmt.substr(int_)}; } } template constexpr auto process_arg(stdx::tuple t, Arg arg) { using namespace stdx::literals; constexpr auto p = split_format_spec(); if constexpr (requires { field_value(arg); }) { return stdx::make_tuple(t[0_idx] + format1(p.first, arg), p.second, t[2_idx]); } else if constexpr (requires { arg.args; }) { return stdx::make_tuple(t[0_idx] + format1(p.first, arg.str), p.second, stdx::tuple_cat(t[2_idx], arg.args)); } else { return stdx::make_tuple( t[0_idx] + p.first, p.second, stdx::tuple_cat(t[2_idx], stdx::make_tuple(arg))); } } } // namespace detail template constexpr auto format(Fmt, Args... args) { using namespace stdx::literals; auto t = stdx::make_tuple(args...); auto r = t.fold_left(stdx::make_tuple(""_sc, Fmt{}, stdx::tuple{}), [](auto x, auto y) { return detail::process_arg(x, y); }); if constexpr (r[2_idx].size() == 0) { return r[0_idx] + r[1_idx]; } else { return lazy_string_format{r[0_idx] + r[1_idx], r[2_idx]}; } } } // namespace sc