-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathformat.hpp
103 lines (92 loc) · 3.42 KB
/
format.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#pragma once
#include <sc/fwd.hpp>
#include <sc/lazy_string_format.hpp>
#include <sc/string_constant.hpp>
#include <stdx/compiler.hpp>
#include <stdx/ct_conversions.hpp>
#include <stdx/tuple.hpp>
#include <stdx/tuple_algorithms.hpp>
#include <fmt/compile.h>
#include <fmt/format.h>
#include <algorithm>
#include <array>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>
namespace sc {
namespace detail {
template <typename T>
concept compile_time_field =
std::same_as<typename T::value_type,
std::remove_cvref_t<decltype(T::value)>>;
template <compile_time_field T> [[nodiscard]] CONSTEVAL auto field_value(T) {
if constexpr (std::is_enum_v<typename T::value_type>) {
return stdx::enum_as_string<T::value>();
} else {
return T::value;
}
}
template <typename T>
[[nodiscard]] CONSTEVAL auto field_value(sc::type_name<T>) {
return stdx::type_as_string<T>();
}
template <typename Fmt, typename Arg> 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<char, sz> buf{};
fmt::format_to(std::begin(buf), fmtstr, field_value(arg));
return buf;
}();
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return string_constant<char, str[Is]...>{};
}(std::make_index_sequence<std::size(str)>{});
}
template <typename Fmt> 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<int>(std::distance(std::begin(fmt), spec_end) + 1);
return std::pair{fmt.substr(int_<0>, int_<len>), fmt.substr(int_<len>)};
}
}
template <typename Str, typename Fmt, typename RuntimeTuple, typename Arg>
constexpr auto process_arg(stdx::tuple<Str, Fmt, RuntimeTuple> t, Arg arg) {
using namespace stdx::literals;
constexpr auto p = split_format_spec<Fmt>();
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 <typename Fmt, typename... Args>
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