Sese Framework  x.y.z
A cross-platform framework
载入中...
搜索中...
未找到
Format.h
浏览该文件的文档.
1// Copyright 2024 libsese
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
19
20
21#pragma once
22
23#include <sese/Config.h>
25#include <sese/text/Number.h>
28#include <sese/types/is_pair.h>
29#include <sese/Util.h>
30
31#include <cassert>
32#include <cmath>
33
34namespace sese::text {
35
36struct FmtCtx {
38 std::string_view pattern;
39 std::string_view::const_iterator pos;
40
41 explicit FmtCtx(std::string_view p);
42
43 bool parsing(std::string &args);
44};
45
50bool FormatOption_StringParse(FormatOption &opt, const std::string &opt_str);
51
56void FormatOption_StringFormat(FmtCtx &ctx, FormatOption &opt, const std::string &value);
57
62bool FormatOption_NumberParse(FormatOption &opt, const std::string &opt_str);
63
64template<typename T>
65SESE_ALWAYS_INLINE void FormatOption_NumberFormatAlgin(FmtCtx &ctx, FormatOption &opt, T number, int radix, bool upper_case) {
66 StringBuilder &builder = ctx.builder;
67 auto len = number2StringLength(number, radix);
68 if (opt.wide <= len) {
69 Number::toString(builder, number, radix, upper_case);
70 return;
71 }
72 auto diff = opt.wide - len;
73 switch (opt.align) {
74 case Align::LEFT:
75 Number::toString(builder, number, radix, upper_case);
76 builder << std::string(diff, opt.wide_char); // GCOVR_EXCL_LINE
77 break;
78 case Align::RIGHT:
79 builder << std::string(diff, opt.wide_char); // GCOVR_EXCL_LINE
80 Number::toString(builder, number, radix, upper_case);
81 break;
82 case Align::CENTER:
83 builder << std::string(diff / 2, opt.wide_char); // GCOVR_EXCL_LINE
84 Number::toString(builder, number, radix, upper_case);
85 builder << std::string((diff % 2 == 1 ? (diff / 2 + 1) : (diff / 2)), opt.wide_char); // GCOVR_EXCL_LINE
86 break;
87 }
88}
89
95template<typename T>
96void FormatOption_NumberFormat(FmtCtx &ctx, FormatOption &opt, T number) {
97 auto radix = opt.ext_type;
98 if (radix == 'X') {
99 FormatOption_NumberFormatAlgin(ctx, opt, number, 16, true);
100 } else if (radix == 'x') {
101 FormatOption_NumberFormatAlgin(ctx, opt, number, 16, false);
102 } else if (radix == 'o') {
103 FormatOption_NumberFormatAlgin(ctx, opt, number, 8, true);
104 } else if (radix == 'b') {
105 FormatOption_NumberFormatAlgin(ctx, opt, number, 2, true);
106 } else {
107 FormatOption_NumberFormatAlgin(ctx, opt, number, 10, true);
108 }
109}
110
116template<typename T>
118 if (opt.float_placeholder == 0) {
119 opt.float_placeholder = 1;
120 }
121 StringBuilder &builder = ctx.builder;
122 size_t len;
123 if (opt.ext_type == '%') {
124 number *= 100;
125 len = floating2StringLength(number, opt.float_placeholder);
126 len += 1;
127 } else {
128 len = floating2StringLength(number, opt.float_placeholder);
129 }
130 if (opt.wide <= len) {
131 Number::toString(builder, number, opt.float_placeholder);
132 if (opt.ext_type == '%') {
133 builder.append('%');
134 }
135 return;
136 }
137 auto diff = opt.wide - len;
138 switch (opt.align) {
139 case Align::LEFT:
140 Number::toString(builder, number, opt.float_placeholder);
141 if (opt.ext_type == '%') {
142 builder.append('%');
143 }
144 builder << std::string(diff, opt.wide_char); // GCOVR_EXCL_LINE
145 break;
146 case Align::RIGHT:
147 builder << std::string(diff, opt.wide_char); // GCOVR_EXCL_LINE
148 Number::toString(builder, number, opt.float_placeholder);
149 if (opt.ext_type == '%') {
150 builder.append('%');
151 }
152 break;
153 case Align::CENTER:
154 builder << std::string(diff / 2, opt.wide_char); // GCOVR_EXCL_LINE
155 Number::toString(builder, number, opt.float_placeholder);
156 if (opt.ext_type == '%') {
157 builder.append('%');
158 }
159 builder << std::string((diff % 2 == 1 ? (diff / 2 + 1) : (diff / 2)), opt.wide_char); // GCOVR_EXCL_LINE
160 break;
161 }
162}
163
164namespace overload {
165 template<typename VALUE, typename ENABLE = void>
166 struct Formatter {
167 bool parse(const std::string &) { return false; }
168
169 void format(FmtCtx &, const VALUE &) {}
170 };
171
172 template<>
173 struct Formatter<std::string> {
175
176 bool parse(const std::string &opt_str) { return FormatOption_StringParse(option, opt_str); }
177
178 void format(FmtCtx &ctx, const std::string &value) { FormatOption_StringFormat(ctx, option, value); }
179 };
180
181 template<>
182 struct Formatter<const char *> {
184
185 bool parse(const std::string &opt_str) {
186 return FormatOption_StringParse(option, opt_str);
187 }
188
189 void format(FmtCtx &ctx, const char *value) {
190 FormatOption_StringFormat(ctx, option, value);
191 }
192 };
193
194 template<typename VALUE>
195 struct Formatter<VALUE, std::enable_if_t<std::is_integral_v<VALUE>>> {
197
198 bool parse(const std::string &opt_str) {
199 return FormatOption_NumberParse(option, opt_str);
200 }
201
202 void format(FmtCtx &ctx, const VALUE &value) {
203 FormatOption_NumberFormat(ctx, option, value);
204 }
205 };
206
207 template<typename VALUE>
208 struct Formatter<VALUE, std::enable_if_t<std::is_floating_point_v<VALUE>>> {
210
211 bool parse(const std::string &opt_str) {
212 return FormatOption_NumberParse(option, opt_str);
213 }
214
215 void format(FmtCtx &ctx, const VALUE &value) {
216 if (std::isnan(value)) {
217 ctx.builder << "NaN";
218 } else {
219 FormatOption_FloatNumberFormat<VALUE>(ctx, option, value);
220 }
221 }
222 };
223
224 template<typename VALUE>
225 struct Formatter<VALUE, std::enable_if_t<is_iterable_v<VALUE>>> {
226 char begin_ch = '[';
227 char end_ch = ']';
228 char split_ch = ',';
229
230 bool parse(const std::string &args) {
231 if (args.size() == 3) {
232 begin_ch = args[0];
233 split_ch = args[1];
234 end_ch = args[2];
235 return true;
236 }
237 return false;
238 }
239
240 void format(FmtCtx &ctx, VALUE &value) {
241 StringBuilder &builder = ctx.builder;
242 builder << begin_ch;
243 bool first = true;
244 for (auto &&item: value) {
246 if (first) {
247 first = false;
248 } else {
249 builder << split_ch;
250 }
251 formatter.format(ctx, item);
252 }
253 builder << end_ch;
254 }
255 };
256
257} // namespace overload
258
259template<typename C, std::enable_if_t<!is_pair<typename C::value_type>::value, int> = 0>
260std::string for_each(const C &container) {
261 constexpr char SPLIT_CH = ',';
262 constexpr char BEGIN_CH = '[';
263 constexpr char END_CH = ']';
264 if (container.empty()) {
265 return "[]";
266 }
267 FmtCtx ctx("");
269 bool first = true;
270 ctx.builder << BEGIN_CH;
271 for (const auto &item: container) {
272 if (first) {
273 first = false;
274 } else {
275 ctx.builder << SPLIT_CH << ' ';
276 }
277 formatter.format(ctx, item);
278 }
279 ctx.builder << END_CH;
280 return ctx.builder.toString();
281}
282
283template<typename C, std::enable_if_t<is_pair<typename C::value_type>::value, int> = 0>
284std::string for_each(const C &container) {
285 constexpr char SPLIT_CH = ',';
286 constexpr char BEGIN_CH = '[';
287 constexpr char END_CH = ']';
288 constexpr char PAIR_BEGIN_CH = '{';
289 constexpr char PAIR_END_CH = '}';
290 if (container.empty()) {
291 return "[]";
292 }
293 FmtCtx ctx("");
294 auto key_formatter = overload::Formatter<std::decay_t<typename C::key_type>>();
295 auto value_formatter = overload::Formatter<std::decay_t<typename C::mapped_type>>();
296 bool first = true;
297 ctx.builder << BEGIN_CH;
298 for (auto &&[key, value]: container) {
299 if (first) {
300 first = false;
301 } else {
302 ctx.builder << SPLIT_CH << ' ';
303 }
304 ctx.builder << PAIR_BEGIN_CH;
305 key_formatter.format(ctx, key);
306 ctx.builder << SPLIT_CH << ' ';
307 value_formatter.format(ctx, value);
308 ctx.builder << PAIR_END_CH;
309 }
310 ctx.builder << END_CH;
311 return ctx.builder.toString();
312}
313
314constexpr size_t FormatParameterCounter(const char *pattern) {
315 size_t count = 0;
316 const char *p = pattern;
317 if (*p == '{')
318 count += 1;
319 p++;
320 while (*p != 0) {
321 if (*p == '{' && *(p - 1) != '\\') {
322 count += 1;
323 }
324 p++;
325 }
326 return count;
327}
328
329template<typename T>
330void Format(FmtCtx &ctx, T &&arg) {
331 std::string parsing_args;
332 auto status = ctx.parsing(parsing_args);
333 if (!status) {
334 return;
335 }
336 auto formatter = overload::Formatter<std::decay_t<T>>();
337 if (!parsing_args.empty()) {
338 if (formatter.parse(parsing_args)) {
339 formatter.format(ctx, std::forward<T>(arg));
340 } else {
341 ctx.builder << "!{parsing failed}";
342 }
343 ctx.parsing(parsing_args);
344 } else {
345 formatter.format(ctx, std::forward<T>(arg));
346 ctx.parsing(parsing_args);
347 }
348}
349
350template<typename T, typename... ARGS>
351void Format(FmtCtx &ctx, T &&arg, ARGS &&...args) {
352 std::string parsing_args;
353 auto status = ctx.parsing(parsing_args);
354 if (!status) {
355 return;
356 }
357 auto formatter = overload::Formatter<std::decay_t<T>>();
358 if (!parsing_args.empty()) {
359 if (formatter.parse(parsing_args)) {
360 formatter.format(ctx, std::forward<T>(arg));
361 } else {
362 ctx.builder << "!{parsing failed}";
363 }
364 Format(ctx, std::forward<ARGS>(args)...);
365 } else {
366 formatter.format(ctx, std::forward<T>(arg));
367 Format(ctx, std::forward<ARGS>(args)...);
368 }
369}
370
375template<typename... ARGS, std::enable_if_t<sizeof...(ARGS) == 0, int> = 0>
376std::string fmt(std::string_view pattern, ARGS &&...) {
377 if (FormatParameterCounter(pattern.data())) {
378 return "!{Mismatch in number of parameters}";
379 }
380 return {pattern.begin(), pattern.end()};
381}
382
388template<typename... ARGS, std::enable_if_t<sizeof...(ARGS) != 0, int> = 0>
389std::string fmt(std::string_view pattern, ARGS &&...args) {
390 auto param = FormatParameterCounter(pattern.data());
391 if (param != sizeof...(args)) {
392 return "!{Mismatch in number of parameters}";
393 }
394 FmtCtx ctx(pattern);
395 Format(ctx, std::forward<ARGS>(args)...);
396 return ctx.builder.toString();
397}
398
399} // namespace sese::text