1515
1616#include < openvic-dataloader/detail/Constexpr.hpp>
1717
18+ #include < fmt/base.h>
19+ #include < fmt/format.h>
20+
1821namespace 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+ };
0 commit comments