Skip to content

Commit c28fe3f

Browse files
authored
Merge pull request #91 from OpenVicProject/add/fmt-formatters
Add `fmt::formatter` for LineObject
2 parents 1f785f0 + 359240c commit c28fe3f

File tree

2 files changed

+131
-1
lines changed

2 files changed

+131
-1
lines changed

include/openvic-dataloader/csv/LineObject.hpp

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
#include <openvic-dataloader/detail/Constexpr.hpp>
1717

18+
#include <fmt/base.h>
19+
#include <fmt/format.h>
20+
1821
namespace ovdl::csv {
1922
/// LineObject should be able to recognize the differences between:
2023
/// Input -> Indexes == ""
@@ -168,4 +171,101 @@ namespace ovdl::csv {
168171
}
169172
return stream;
170173
}
171-
}
174+
}
175+
176+
// Supports s<char> for designating a separator, including s{{ and s}} to escape brackets
177+
// Also supports s{} for dynamic separator which supports multi-character separators
178+
template<>
179+
struct fmt::formatter<ovdl::csv::LineObject> : formatter<string_view> {
180+
struct {
181+
arg_id_kind kind = arg_id_kind::none;
182+
detail::arg_ref<char> ref;
183+
std::string value = ";";
184+
} sep_spec {};
185+
186+
constexpr format_parse_context::iterator parse(format_parse_context& ctx) {
187+
auto it = ctx.begin(), end = ctx.end();
188+
if (it == end || *ctx.begin() == '}') {
189+
return it;
190+
}
191+
192+
if (*it == 's') {
193+
++it;
194+
195+
if (*it == '{') {
196+
++it;
197+
198+
if (*it == '{') {
199+
++it;
200+
sep_spec.value = { it, it + 1 };
201+
} else if (*it == '}') {
202+
++it;
203+
sep_spec.ref = ctx.next_arg_id();
204+
sep_spec.kind = arg_id_kind::index;
205+
} else {
206+
it = detail::parse_arg_id(it, end, detail::dynamic_spec_handler<char> { ctx, sep_spec.ref, sep_spec.kind });
207+
}
208+
} else if (*it == '}') {
209+
++it;
210+
211+
if (*it != '}') {
212+
report_error("invalid format string");
213+
return it;
214+
}
215+
} else {
216+
sep_spec.value = { it, it + 1 };
217+
++it;
218+
}
219+
}
220+
221+
return it;
222+
}
223+
224+
struct separator_spec_getter {
225+
template<typename T, FMT_ENABLE_IF(detail::is_std_string_like<T>::value)>
226+
constexpr auto operator()(T value) -> string_view {
227+
return value;
228+
}
229+
230+
constexpr auto operator()(const char* value) -> string_view {
231+
return value;
232+
}
233+
234+
template<typename T, FMT_ENABLE_IF(!detail::is_std_string_like<T>::value)>
235+
constexpr auto operator()(T) -> string_view {
236+
report_error("separator is not string-like");
237+
return {};
238+
}
239+
};
240+
241+
format_context::iterator format(ovdl::csv::LineObject line, format_context& ctx) const {
242+
string_view separator = sep_spec.value;
243+
if (sep_spec.kind != arg_id_kind::none) {
244+
auto arg = sep_spec.kind == arg_id_kind::index ? ctx.arg(sep_spec.ref.index) : ctx.arg(sep_spec.ref.name);
245+
if (!arg) {
246+
report_error("argument not found");
247+
}
248+
separator = arg.visit(separator_spec_getter {});
249+
}
250+
251+
auto out = ctx.out();
252+
ovdl::csv::LineObject::position_type sep_index = 0;
253+
for (const auto& [pos, val] : line) {
254+
while (sep_index < pos) {
255+
out = detail::write<char>(out, separator);
256+
ctx.advance_to(out);
257+
sep_index++;
258+
}
259+
if (std::any_of(val.begin(), val.end(), [&](char c) { return (separator.size() == 1 && c == separator[0]) || std::isspace(c); })) {
260+
out = detail::write<char>(out, '"');
261+
ctx.advance_to(out);
262+
out = formatter<string_view>::format(val, ctx);
263+
out = detail::write<char>(out, '"');
264+
ctx.advance_to(out);
265+
} else {
266+
out = formatter<string_view>::format(val, ctx);
267+
}
268+
}
269+
return out;
270+
}
271+
};

tests/src/csv/LineObject.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ TEST_CASE("LineObject", "[line-object]") {
2828

2929
CHECK(ss.str() == ""sv);
3030
}
31+
32+
SECTION("fmt to_string") {
33+
CHECK(fmt::to_string(line) == ""sv);
34+
}
3135
}
3236

3337
SECTION("no prefix") {
@@ -50,6 +54,10 @@ TEST_CASE("LineObject", "[line-object]") {
5054

5155
CHECK(ss.str() == "a;b;c"sv);
5256
}
57+
58+
SECTION("fmt to_string") {
59+
CHECK(fmt::to_string(line) == "a;b;c"sv);
60+
}
5361
}
5462

5563
SECTION("no suffix") {
@@ -88,6 +96,10 @@ TEST_CASE("LineObject", "[line-object]") {
8896

8997
CHECK(ss.str() == ";a;b;c"sv);
9098
}
99+
100+
SECTION("fmt to_string") {
101+
CHECK(fmt::to_string(line) == ";a;b;c"sv);
102+
}
91103
}
92104

93105
SECTION("prefix and suffix with spaces") {
@@ -112,6 +124,10 @@ TEST_CASE("LineObject", "[line-object]") {
112124

113125
CHECK(ss.str() == ";\"a b\";\"c d\";\"e f\""sv);
114126
}
127+
128+
SECTION("fmt to_string") {
129+
CHECK(fmt::to_string(line) == ";\"a b\";\"c d\";\"e f\""sv);
130+
}
115131
}
116132

117133
SECTION("prefix and suffix with separators") {
@@ -136,6 +152,10 @@ TEST_CASE("LineObject", "[line-object]") {
136152

137153
CHECK(ss.str() == ";\"a;b\";\"c;d\";\"e;f\""sv);
138154
}
155+
156+
SECTION("fmt to_string") {
157+
CHECK(fmt::to_string(line) == ";\"a;b\";\"c;d\";\"e;f\""sv);
158+
}
139159
}
140160

141161
SECTION("prefix and suffix with custom char separator") {
@@ -151,6 +171,8 @@ TEST_CASE("LineObject", "[line-object]") {
151171
ss << line.use_sep("|") << std::flush;
152172

153173
CHECK(ss.str() == "|a;b|c;d|e;f"sv);
174+
175+
CHECK(fmt::format("{:s|}", line) == "|a;b|c;d|e;f"sv);
154176
}
155177

156178
SECTION("prefix and suffix with custom char separator and containing the separator") {
@@ -175,6 +197,10 @@ TEST_CASE("LineObject", "[line-object]") {
175197

176198
CHECK(ss.str() == "|\"a|b\"|\"c|d\"|\"e|f\""sv);
177199
}
200+
201+
SECTION("fmt format") {
202+
CHECK(fmt::format("{:s|}", line) == "|\"a|b\"|\"c|d\"|\"e|f\""sv);
203+
}
178204
}
179205

180206
SECTION("prefix and suffix with custom string_view separator") {
@@ -192,5 +218,9 @@ TEST_CASE("LineObject", "[line-object]") {
192218

193219
CHECK(ss.str() == "heya;bheyc;dheye;f"sv);
194220
}
221+
222+
SECTION("fmt format") {
223+
CHECK(fmt::format("{:s{}}", line, "hey") == "heya;bheyc;dheye;f"sv);
224+
}
195225
}
196226
}

0 commit comments

Comments
 (0)