format-inl.h 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. #ifndef FORMAT_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include format.h"
  3. // For the sake of sane code completion.
  4. #include "format.h"
  5. #endif
  6. #include "enum.h"
  7. #include "string.h"
  8. #include <library/cpp/yt/assert/assert.h>
  9. #include <library/cpp/yt/small_containers/compact_vector.h>
  10. #include <library/cpp/yt/containers/enum_indexed_array.h>
  11. #include <library/cpp/yt/misc/concepts.h>
  12. #include <library/cpp/yt/misc/enum.h>
  13. #include <library/cpp/yt/misc/wrapper_traits.h>
  14. #include <util/generic/maybe.h>
  15. #include <util/system/platform.h>
  16. #include <cctype>
  17. #include <optional>
  18. #include <span>
  19. #if __cplusplus >= 202302L
  20. #include <filesystem>
  21. #endif
  22. namespace NYT {
  23. ////////////////////////////////////////////////////////////////////////////////
  24. // Helper functions for formatting.
  25. namespace NDetail {
  26. constexpr inline char IntroductorySymbol = '%';
  27. constexpr inline char GenericSpecSymbol = 'v';
  28. inline bool IsQuotationSpecSymbol(char symbol)
  29. {
  30. return symbol == 'Q' || symbol == 'q';
  31. }
  32. ////////////////////////////////////////////////////////////////////////////////
  33. template <class TValue>
  34. void FormatValueViaSprintf(
  35. TStringBuilderBase* builder,
  36. TValue value,
  37. TStringBuf spec,
  38. TStringBuf genericSpec);
  39. template <class TValue>
  40. void FormatIntValue(
  41. TStringBuilderBase* builder,
  42. TValue value,
  43. TStringBuf spec,
  44. TStringBuf genericSpec);
  45. void FormatPointerValue(
  46. TStringBuilderBase* builder,
  47. const void* value,
  48. TStringBuf spec);
  49. ////////////////////////////////////////////////////////////////////////////////
  50. // Helper concepts for matching the correct overload.
  51. // NB(arkady-e1ppa): We prefer to hardcode the known types
  52. // so that someone doesn't accidentally implement the
  53. // "SimpleRange" concept and have a non-trivial
  54. // formatting procedure at the same time.
  55. template <class R>
  56. concept CKnownRange =
  57. requires (R r) { [] <class... Ts> (std::vector<Ts...>) { } (r); } ||
  58. requires (R r) { [] <class T, size_t E> (std::span<T, E>) { } (r); } ||
  59. requires (R r) { [] <class T, size_t N> (TCompactVector<T, N>) { } (r); } ||
  60. requires (R r) { [] <class... Ts> (std::set<Ts...>) { } (r); } ||
  61. requires (R r) { [] <class... Ts> (THashSet<Ts...>) { } (r); } ||
  62. requires (R r) { [] <class... Ts> (THashMultiSet<Ts...>) { } (r); };
  63. ////////////////////////////////////////////////////////////////////////////////
  64. template <class R>
  65. concept CKnownKVRange =
  66. requires (R r) { [] <class... Ts> (std::map<Ts...>) { } (r); } ||
  67. requires (R r) { [] <class... Ts> (std::multimap<Ts...>) { } (r); } ||
  68. requires (R r) { [] <class... Ts> (THashMap<Ts...>) { } (r); } ||
  69. requires (R r) { [] <class... Ts> (THashMultiMap<Ts...>) { } (r); };
  70. } // namespace NDetail
  71. ////////////////////////////////////////////////////////////////////////////////
  72. template <class TRange, class TFormatter>
  73. void FormatRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  74. {
  75. builder->AppendChar('[');
  76. size_t index = 0;
  77. for (const auto& item : range) {
  78. if (index > 0) {
  79. builder->AppendString(DefaultJoinToStringDelimiter);
  80. }
  81. if (index == limit) {
  82. builder->AppendString(DefaultRangeEllipsisFormat);
  83. break;
  84. }
  85. formatter(builder, item);
  86. ++index;
  87. }
  88. builder->AppendChar(']');
  89. }
  90. ////////////////////////////////////////////////////////////////////////////////
  91. template <class TRange, class TFormatter>
  92. void FormatKeyValueRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  93. {
  94. builder->AppendChar('{');
  95. size_t index = 0;
  96. for (const auto& item : range) {
  97. if (index > 0) {
  98. builder->AppendString(DefaultJoinToStringDelimiter);
  99. }
  100. if (index == limit) {
  101. builder->AppendString(DefaultRangeEllipsisFormat);
  102. break;
  103. }
  104. formatter(builder, item.first);
  105. builder->AppendString(DefaultKeyValueDelimiter);
  106. formatter(builder, item.second);
  107. ++index;
  108. }
  109. builder->AppendChar('}');
  110. }
  111. ////////////////////////////////////////////////////////////////////////////////
  112. template <class R>
  113. concept CFormattableRange =
  114. NDetail::CKnownRange<R> &&
  115. CFormattable<typename R::value_type>;
  116. template <class R>
  117. concept CFormattableKVRange =
  118. NDetail::CKnownKVRange<R> &&
  119. CFormattable<typename R::key_type> &&
  120. CFormattable<typename R::value_type>;
  121. ////////////////////////////////////////////////////////////////////////////////
  122. template <class TRange, class TFormatter>
  123. typename TFormattableView<TRange, TFormatter>::TBegin TFormattableView<TRange, TFormatter>::begin() const
  124. {
  125. return RangeBegin;
  126. }
  127. template <class TRange, class TFormatter>
  128. typename TFormattableView<TRange, TFormatter>::TEnd TFormattableView<TRange, TFormatter>::end() const
  129. {
  130. return RangeEnd;
  131. }
  132. template <class TRange, class TFormatter>
  133. TFormattableView<TRange, TFormatter> MakeFormattableView(
  134. const TRange& range,
  135. TFormatter&& formatter)
  136. {
  137. return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter)};
  138. }
  139. template <class TRange, class TFormatter>
  140. TFormattableView<TRange, TFormatter> MakeShrunkFormattableView(
  141. const TRange& range,
  142. TFormatter&& formatter,
  143. size_t limit)
  144. {
  145. return TFormattableView<TRange, std::decay_t<TFormatter>>{
  146. range.begin(),
  147. range.end(),
  148. std::forward<TFormatter>(formatter),
  149. limit};
  150. }
  151. template <class TFormatter>
  152. TFormatterWrapper<TFormatter> MakeFormatterWrapper(
  153. TFormatter&& formatter)
  154. {
  155. return TFormatterWrapper<TFormatter>{
  156. .Formatter = std::move(formatter)
  157. };
  158. }
  159. template <class... TArgs>
  160. TLazyMultiValueFormatter<TArgs...>::TLazyMultiValueFormatter(
  161. TStringBuf format,
  162. TArgs&&... args)
  163. : Format_(format)
  164. , Args_(std::forward<TArgs>(args)...)
  165. { }
  166. template <class... TArgs>
  167. auto MakeLazyMultiValueFormatter(TStringBuf format, TArgs&&... args)
  168. {
  169. return TLazyMultiValueFormatter<TArgs...>(format, std::forward<TArgs>(args)...);
  170. }
  171. ////////////////////////////////////////////////////////////////////////////////
  172. // Non-container objects.
  173. #define XX(valueType, castType, genericSpec) \
  174. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf spec) \
  175. { \
  176. NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), spec, genericSpec); \
  177. }
  178. XX(i8, i32, TStringBuf("d"))
  179. XX(ui8, ui32, TStringBuf("u"))
  180. XX(i16, i32, TStringBuf("d"))
  181. XX(ui16, ui32, TStringBuf("u"))
  182. XX(i32, i32, TStringBuf("d"))
  183. XX(ui32, ui32, TStringBuf("u"))
  184. XX(long, i64, TStringBuf(PRIdLEAST64))
  185. XX(long long, i64, TStringBuf(PRIdLEAST64))
  186. XX(unsigned long, ui64, TStringBuf(PRIuLEAST64))
  187. XX(unsigned long long, ui64, TStringBuf(PRIuLEAST64))
  188. #undef XX
  189. #define XX(valueType, castType, genericSpec) \
  190. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf spec) \
  191. { \
  192. NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), spec, genericSpec); \
  193. }
  194. XX(double, double, TStringBuf("lf"))
  195. XX(float, float, TStringBuf("f"))
  196. #undef XX
  197. // Pointer
  198. template <class T>
  199. void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf spec)
  200. {
  201. NYT::NDetail::FormatPointerValue(builder, static_cast<const void*>(value), spec);
  202. }
  203. // TStringBuf
  204. inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBuf spec)
  205. {
  206. if (!spec) {
  207. builder->AppendString(value);
  208. return;
  209. }
  210. // Parse alignment.
  211. bool alignLeft = false;
  212. const char* current = spec.begin();
  213. if (*current == '-') {
  214. alignLeft = true;
  215. ++current;
  216. }
  217. bool hasAlign = false;
  218. int alignSize = 0;
  219. while (*current >= '0' && *current <= '9') {
  220. hasAlign = true;
  221. alignSize = 10 * alignSize + (*current - '0');
  222. if (alignSize > 1000000) {
  223. builder->AppendString(TStringBuf("<alignment overflow>"));
  224. return;
  225. }
  226. ++current;
  227. }
  228. int padding = 0;
  229. bool padLeft = false;
  230. bool padRight = false;
  231. if (hasAlign) {
  232. padding = alignSize - value.size();
  233. if (padding < 0) {
  234. padding = 0;
  235. }
  236. padLeft = !alignLeft;
  237. padRight = alignLeft;
  238. }
  239. bool singleQuotes = false;
  240. bool doubleQuotes = false;
  241. bool escape = false;
  242. while (current < spec.end()) {
  243. switch (*current++) {
  244. case 'q':
  245. singleQuotes = true;
  246. break;
  247. case 'Q':
  248. doubleQuotes = true;
  249. break;
  250. case 'h':
  251. escape = true;
  252. break;
  253. }
  254. }
  255. if (padLeft) {
  256. builder->AppendChar(' ', padding);
  257. }
  258. if (singleQuotes || doubleQuotes || escape) {
  259. for (const char* valueCurrent = value.begin(); valueCurrent < value.end(); ++valueCurrent) {
  260. char ch = *valueCurrent;
  261. if (ch == '\n') {
  262. builder->AppendString("\\n");
  263. } else if (ch == '\t') {
  264. builder->AppendString("\\t");
  265. } else if (ch == '\\') {
  266. builder->AppendString("\\\\");
  267. } else if (ch < PrintableASCIILow || ch > PrintableASCIIHigh) {
  268. builder->AppendString("\\x");
  269. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) >> 4]);
  270. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) & 0xf]);
  271. } else if ((singleQuotes && ch == '\'') || (doubleQuotes && ch == '\"')) {
  272. builder->AppendChar('\\');
  273. builder->AppendChar(ch);
  274. } else {
  275. builder->AppendChar(ch);
  276. }
  277. }
  278. } else {
  279. builder->AppendString(value);
  280. }
  281. if (padRight) {
  282. builder->AppendChar(' ', padding);
  283. }
  284. }
  285. // TString
  286. inline void FormatValue(TStringBuilderBase* builder, const TString& value, TStringBuf spec)
  287. {
  288. FormatValue(builder, TStringBuf(value), spec);
  289. }
  290. // const char*
  291. inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringBuf spec)
  292. {
  293. FormatValue(builder, TStringBuf(value), spec);
  294. }
  295. template <size_t N>
  296. inline void FormatValue(TStringBuilderBase* builder, const char (&value)[N], TStringBuf spec)
  297. {
  298. FormatValue(builder, TStringBuf(value), spec);
  299. }
  300. // char*
  301. inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf spec)
  302. {
  303. FormatValue(builder, TStringBuf(value), spec);
  304. }
  305. // std::string
  306. inline void FormatValue(TStringBuilderBase* builder, const std::string& value, TStringBuf spec)
  307. {
  308. FormatValue(builder, TStringBuf(value), spec);
  309. }
  310. // std::string_view
  311. inline void FormatValue(TStringBuilderBase* builder, const std::string_view& value, TStringBuf spec)
  312. {
  313. FormatValue(builder, TStringBuf(value), spec);
  314. }
  315. #if __cplusplus >= 202302L
  316. // std::filesystem::path
  317. inline void FormatValue(TStringBuilderBase* builder, const std::filesystem::path& value, TStringBuf spec)
  318. {
  319. FormatValue(builder, std::string(value), spec);
  320. }
  321. #endif
  322. // char
  323. inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf spec)
  324. {
  325. FormatValue(builder, TStringBuf(&value, 1), spec);
  326. }
  327. // bool
  328. inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf spec)
  329. {
  330. // Parse custom flags.
  331. bool lowercase = false;
  332. const char* current = spec.begin();
  333. while (current != spec.end()) {
  334. if (*current == 'l') {
  335. ++current;
  336. lowercase = true;
  337. } else if (NYT::NDetail::IsQuotationSpecSymbol(*current)) {
  338. ++current;
  339. } else
  340. break;
  341. }
  342. auto str = lowercase
  343. ? (value ? TStringBuf("true") : TStringBuf("false"))
  344. : (value ? TStringBuf("True") : TStringBuf("False"));
  345. builder->AppendString(str);
  346. }
  347. // TDuration
  348. inline void FormatValue(TStringBuilderBase* builder, TDuration value, TStringBuf /*spec*/)
  349. {
  350. builder->AppendFormat("%vus", value.MicroSeconds());
  351. }
  352. // TInstant
  353. inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf spec)
  354. {
  355. // TODO(babenko): Optimize.
  356. FormatValue(builder, NYT::ToStringIgnoringFormatValue(value), spec);
  357. }
  358. // Enum
  359. template <class TEnum>
  360. requires (TEnumTraits<TEnum>::IsEnum)
  361. void FormatValue(TStringBuilderBase* builder, TEnum value, TStringBuf spec)
  362. {
  363. // Parse custom flags.
  364. bool lowercase = false;
  365. const char* current = spec.begin();
  366. while (current != spec.end()) {
  367. if (*current == 'l') {
  368. ++current;
  369. lowercase = true;
  370. } else if (NYT::NDetail::IsQuotationSpecSymbol(*current)) {
  371. ++current;
  372. } else {
  373. break;
  374. }
  375. }
  376. FormatEnum(builder, value, lowercase);
  377. }
  378. template <class TArcadiaEnum>
  379. requires (std::is_enum_v<TArcadiaEnum> && !TEnumTraits<TArcadiaEnum>::IsEnum)
  380. void FormatValue(TStringBuilderBase* builder, TArcadiaEnum value, TStringBuf /*spec*/)
  381. {
  382. // NB(arkady-e1ppa): This can catch normal enums which
  383. // just want to be serialized as numbers.
  384. // Unfortunately, we have no way of determining that other than
  385. // marking every relevant arcadia enum in the code by trait
  386. // or writing their complete trait and placing such trait in
  387. // every single file where it is formatted.
  388. // We gotta figure something out but until that
  389. // we will just have to make a string for such enums.
  390. // If only arcadia enums provided compile-time check
  391. // if enum is serializable :(((((.
  392. builder->AppendString(NYT::ToStringIgnoringFormatValue(value));
  393. }
  394. // Container objects.
  395. // NB(arkady-e1ppa): In order to support container combinations
  396. // we forward-declare them before defining.
  397. // TMaybe
  398. template <class T, class TPolicy>
  399. void FormatValue(TStringBuilderBase* builder, const TMaybe<T, TPolicy>& value, TStringBuf spec);
  400. // std::optional
  401. template <CFormattable T>
  402. void FormatValue(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf spec);
  403. // std::pair
  404. template <CFormattable A, CFormattable B>
  405. void FormatValue(TStringBuilderBase* builder, const std::pair<A, B>& value, TStringBuf spec);
  406. // std::tuple
  407. template <CFormattable... Ts>
  408. void FormatValue(TStringBuilderBase* builder, const std::tuple<Ts...>& value, TStringBuf spec);
  409. // TEnumIndexedArray
  410. template <class E, CFormattable T>
  411. void FormatValue(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf spec);
  412. // One-valued ranges
  413. template <CFormattableRange TRange>
  414. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf spec);
  415. // Two-valued ranges
  416. template <CFormattableKVRange TRange>
  417. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf spec);
  418. // FormattableView
  419. template <class TRange, class TFormatter>
  420. void FormatValue(
  421. TStringBuilderBase* builder,
  422. const TFormattableView<TRange, TFormatter>& formattableView,
  423. TStringBuf spec);
  424. // TFormatterWrapper
  425. template <class TFormatter>
  426. void FormatValue(
  427. TStringBuilderBase* builder,
  428. const TFormatterWrapper<TFormatter>& wrapper,
  429. TStringBuf spec);
  430. // TLazyMultiValueFormatter
  431. template <class... TArgs>
  432. void FormatValue(
  433. TStringBuilderBase* builder,
  434. const TLazyMultiValueFormatter<TArgs...>& value,
  435. TStringBuf /*spec*/);
  436. // TMaybe
  437. template <class T, class TPolicy>
  438. void FormatValue(TStringBuilderBase* builder, const TMaybe<T, TPolicy>& value, TStringBuf spec)
  439. {
  440. FormatValue(builder, NYT::ToStringIgnoringFormatValue(value), spec);
  441. }
  442. // std::optional: nullopt
  443. inline void FormatValue(TStringBuilderBase* builder, std::nullopt_t, TStringBuf /*spec*/)
  444. {
  445. builder->AppendString(TStringBuf("<null>"));
  446. }
  447. // std::optional: generic T
  448. template <CFormattable T>
  449. void FormatValue(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf spec)
  450. {
  451. if (value.has_value()) {
  452. FormatValue(builder, *value, spec);
  453. } else {
  454. FormatValue(builder, std::nullopt, spec);
  455. }
  456. }
  457. // std::pair
  458. template <CFormattable A, CFormattable B>
  459. void FormatValue(TStringBuilderBase* builder, const std::pair<A, B>& value, TStringBuf spec)
  460. {
  461. builder->AppendChar('{');
  462. FormatValue(builder, value.first, spec);
  463. builder->AppendString(TStringBuf(", "));
  464. FormatValue(builder, value.second, spec);
  465. builder->AppendChar('}');
  466. }
  467. // std::tuple
  468. template <CFormattable... Ts>
  469. void FormatValue(TStringBuilderBase* builder, const std::tuple<Ts...>& value, TStringBuf spec)
  470. {
  471. builder->AppendChar('{');
  472. [&] <size_t... Idx> (std::index_sequence<Idx...>) {
  473. ([&] {
  474. FormatValue(builder, std::get<Idx>(value), spec);
  475. if constexpr (Idx != sizeof...(Ts)) {
  476. builder->AppendString(TStringBuf(", "));
  477. }
  478. } (), ...);
  479. } (std::index_sequence_for<Ts...>());
  480. builder->AppendChar('}');
  481. }
  482. // TEnumIndexedArray
  483. template <class E, CFormattable T>
  484. void FormatValue(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf spec)
  485. {
  486. builder->AppendChar('{');
  487. bool firstItem = true;
  488. for (const auto& index : TEnumTraits<E>::GetDomainValues()) {
  489. if (!firstItem) {
  490. builder->AppendString(DefaultJoinToStringDelimiter);
  491. }
  492. FormatValue(builder, index, spec);
  493. builder->AppendString(": ");
  494. FormatValue(builder, collection[index], spec);
  495. firstItem = false;
  496. }
  497. builder->AppendChar('}');
  498. }
  499. // One-valued ranges
  500. template <CFormattableRange TRange>
  501. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf /*spec*/)
  502. {
  503. NYT::FormatRange(builder, collection, TDefaultFormatter());
  504. }
  505. // Two-valued ranges
  506. template <CFormattableKVRange TRange>
  507. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf /*spec*/)
  508. {
  509. NYT::FormatKeyValueRange(builder, collection, TDefaultFormatter());
  510. }
  511. // FormattableView
  512. template <class TRange, class TFormatter>
  513. void FormatValue(
  514. TStringBuilderBase* builder,
  515. const TFormattableView<TRange, TFormatter>& formattableView,
  516. TStringBuf /*spec*/)
  517. {
  518. NYT::FormatRange(builder, formattableView, formattableView.Formatter, formattableView.Limit);
  519. }
  520. // TFormatterWrapper
  521. template <class TFormatter>
  522. void FormatValue(
  523. TStringBuilderBase* builder,
  524. const TFormatterWrapper<TFormatter>& wrapper,
  525. TStringBuf /*spec*/)
  526. {
  527. wrapper.Formatter(builder);
  528. }
  529. // TLazyMultiValueFormatter
  530. template <class... TArgs>
  531. void FormatValue(
  532. TStringBuilderBase* builder,
  533. const TLazyMultiValueFormatter<TArgs...>& value,
  534. TStringBuf /*spec*/)
  535. {
  536. std::apply(
  537. [&] <class... TInnerArgs> (TInnerArgs&&... args) {
  538. builder->AppendFormat(value.Format_, std::forward<TInnerArgs>(args)...);
  539. },
  540. value.Args_);
  541. }
  542. ////////////////////////////////////////////////////////////////////////////////
  543. namespace NDetail {
  544. template <size_t HeadPos, class... TArgs>
  545. class TValueFormatter;
  546. template <size_t HeadPos>
  547. class TValueFormatter<HeadPos>
  548. {
  549. public:
  550. void operator() (size_t /*index*/, TStringBuilderBase* builder, TStringBuf /*spec*/) const
  551. {
  552. builder->AppendString(TStringBuf("<missing argument>"));
  553. }
  554. };
  555. template <size_t HeadPos, class THead, class... TTail>
  556. class TValueFormatter<HeadPos, THead, TTail...>
  557. {
  558. public:
  559. explicit TValueFormatter(const THead& head, const TTail&... tail) noexcept
  560. : Head_(head)
  561. , TailFormatter_(tail...)
  562. { }
  563. void operator() (size_t index, TStringBuilderBase* builder, TStringBuf spec) const
  564. {
  565. YT_ASSERT(index >= HeadPos);
  566. if (index == HeadPos) {
  567. FormatValue(builder, Head_, spec);
  568. } else {
  569. TailFormatter_(index, builder, spec);
  570. }
  571. }
  572. private:
  573. const THead& Head_;
  574. TValueFormatter<HeadPos + 1, TTail...> TailFormatter_;
  575. };
  576. ////////////////////////////////////////////////////////////////////////////////
  577. template <class TRangeValue>
  578. class TRangeFormatter
  579. {
  580. public:
  581. template <class... TArgs>
  582. requires std::constructible_from<std::span<const TRangeValue>, TArgs...>
  583. explicit TRangeFormatter(TArgs&&... args) noexcept
  584. : Span_(std::forward<TArgs>(args)...)
  585. { }
  586. void operator() (size_t index, TStringBuilderBase* builder, TStringBuf spec) const
  587. {
  588. if (index >= Span_.size()) {
  589. builder->AppendString(TStringBuf("<missing argument>"));
  590. } else {
  591. FormatValue(builder, *(Span_.begin() + index), spec);
  592. }
  593. }
  594. private:
  595. std::span<const TRangeValue> Span_;
  596. };
  597. ////////////////////////////////////////////////////////////////////////////////
  598. template <class T>
  599. concept CFormatter = CInvocable<T, void(size_t, TStringBuilderBase*, TStringBuf)>;
  600. ////////////////////////////////////////////////////////////////////////////////
  601. template <CFormatter TFormatter>
  602. void RunFormatter(
  603. TStringBuilderBase* builder,
  604. TStringBuf format,
  605. const TFormatter& formatter)
  606. {
  607. size_t argIndex = 0;
  608. auto current = std::begin(format);
  609. auto end = std::end(format);
  610. while (true) {
  611. // Scan verbatim part until stop symbol.
  612. auto verbatimBegin = current;
  613. auto verbatimEnd = std::find(current, end, IntroductorySymbol);
  614. // Copy verbatim part, if any.
  615. size_t verbatimSize = verbatimEnd - verbatimBegin;
  616. if (verbatimSize > 0) {
  617. builder->AppendString(TStringBuf(verbatimBegin, verbatimSize));
  618. }
  619. // Handle stop symbol.
  620. current = verbatimEnd;
  621. if (current == end) {
  622. break;
  623. }
  624. YT_ASSERT(*current == IntroductorySymbol);
  625. ++current;
  626. if (*current == IntroductorySymbol) {
  627. // Verbatim %.
  628. builder->AppendChar(IntroductorySymbol);
  629. ++current;
  630. continue;
  631. }
  632. // Scan format part until stop symbol.
  633. auto argFormatBegin = current;
  634. auto argFormatEnd = argFormatBegin;
  635. bool singleQuotes = false;
  636. bool doubleQuotes = false;
  637. while (
  638. argFormatEnd != end &&
  639. *argFormatEnd != GenericSpecSymbol && // value in generic format
  640. *argFormatEnd != 'd' && // others are standard specifiers supported by printf
  641. *argFormatEnd != 'i' &&
  642. *argFormatEnd != 'u' &&
  643. *argFormatEnd != 'o' &&
  644. *argFormatEnd != 'x' &&
  645. *argFormatEnd != 'X' &&
  646. *argFormatEnd != 'f' &&
  647. *argFormatEnd != 'F' &&
  648. *argFormatEnd != 'e' &&
  649. *argFormatEnd != 'E' &&
  650. *argFormatEnd != 'g' &&
  651. *argFormatEnd != 'G' &&
  652. *argFormatEnd != 'a' &&
  653. *argFormatEnd != 'A' &&
  654. *argFormatEnd != 'c' &&
  655. *argFormatEnd != 's' &&
  656. *argFormatEnd != 'p' &&
  657. *argFormatEnd != 'n')
  658. {
  659. switch (*argFormatEnd) {
  660. case 'q':
  661. singleQuotes = true;
  662. break;
  663. case 'Q':
  664. doubleQuotes = true;
  665. break;
  666. case 'h':
  667. break;
  668. }
  669. ++argFormatEnd;
  670. }
  671. // Handle end of format string.
  672. if (argFormatEnd != end) {
  673. ++argFormatEnd;
  674. }
  675. // 'n' means 'nothing'; skip the argument.
  676. if (*argFormatBegin != 'n') {
  677. // Format argument.
  678. TStringBuf argFormat(argFormatBegin, argFormatEnd);
  679. if (singleQuotes) {
  680. builder->AppendChar('\'');
  681. }
  682. if (doubleQuotes) {
  683. builder->AppendChar('"');
  684. }
  685. formatter(argIndex++, builder, argFormat);
  686. if (singleQuotes) {
  687. builder->AppendChar('\'');
  688. }
  689. if (doubleQuotes) {
  690. builder->AppendChar('"');
  691. }
  692. }
  693. current = argFormatEnd;
  694. }
  695. }
  696. } // namespace NDetail
  697. ////////////////////////////////////////////////////////////////////////////////
  698. template <class... TArgs>
  699. void Format(TStringBuilderBase* builder, TFormatString<TArgs...> format, TArgs&&... args)
  700. {
  701. // NB(arkady-e1ppa): "if constexpr" is done in order to prevent
  702. // compiler from emitting "No matching function to call"
  703. // when arguments are not formattable.
  704. // Compiler would crash in TFormatString ctor
  705. // anyway (e.g. program would not compile) but
  706. // for some reason it does look ahead and emits
  707. // a second error.
  708. if constexpr ((CFormattable<TArgs> && ...)) {
  709. NYT::NDetail::TValueFormatter<0, TArgs...> formatter(args...);
  710. NYT::NDetail::RunFormatter(builder, format.Get(), formatter);
  711. }
  712. }
  713. template <class... TArgs>
  714. TString Format(TFormatString<TArgs...> format, TArgs&&... args)
  715. {
  716. TStringBuilder builder;
  717. Format(&builder, format, std::forward<TArgs>(args)...);
  718. return builder.Flush();
  719. }
  720. ////////////////////////////////////////////////////////////////////////////////
  721. template <size_t Length, class TVector>
  722. void FormatVector(
  723. TStringBuilderBase* builder,
  724. const char (&format)[Length],
  725. const TVector& vec)
  726. {
  727. NYT::NDetail::TRangeFormatter<typename TVector::value_type> formatter(vec);
  728. NYT::NDetail::RunFormatter(builder, format, formatter);
  729. }
  730. template <class TVector>
  731. void FormatVector(
  732. TStringBuilderBase* builder,
  733. TStringBuf format,
  734. const TVector& vec)
  735. {
  736. NYT::NDetail::TRangeFormatter<typename TVector::value_type> formatter(vec);
  737. NYT::NDetail::RunFormatter(builder, format, formatter);
  738. }
  739. template <size_t Length, class TVector>
  740. TString FormatVector(
  741. const char (&format)[Length],
  742. const TVector& vec)
  743. {
  744. TStringBuilder builder;
  745. FormatVector(&builder, format, vec);
  746. return builder.Flush();
  747. }
  748. template <class TVector>
  749. TString FormatVector(
  750. TStringBuf format,
  751. const TVector& vec)
  752. {
  753. TStringBuilder builder;
  754. FormatVector(&builder, format, vec);
  755. return builder.Flush();
  756. }
  757. ////////////////////////////////////////////////////////////////////////////////
  758. } // namespace NYT