format-inl.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  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/enum.h>
  12. #include <util/system/platform.h>
  13. #include <cctype>
  14. #include <optional>
  15. #include <span>
  16. namespace NYT {
  17. ////////////////////////////////////////////////////////////////////////////////
  18. static constexpr char GenericSpecSymbol = 'v';
  19. inline bool IsQuotationSpecSymbol(char symbol)
  20. {
  21. return symbol == 'Q' || symbol == 'q';
  22. }
  23. // TStringBuf
  24. inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBuf format)
  25. {
  26. if (!format) {
  27. builder->AppendString(value);
  28. return;
  29. }
  30. // Parse alignment.
  31. bool alignLeft = false;
  32. const char* current = format.begin();
  33. if (*current == '-') {
  34. alignLeft = true;
  35. ++current;
  36. }
  37. bool hasAlign = false;
  38. int alignSize = 0;
  39. while (*current >= '0' && *current <= '9') {
  40. hasAlign = true;
  41. alignSize = 10 * alignSize + (*current - '0');
  42. if (alignSize > 1000000) {
  43. builder->AppendString(TStringBuf("<alignment overflow>"));
  44. return;
  45. }
  46. ++current;
  47. }
  48. int padding = 0;
  49. bool padLeft = false;
  50. bool padRight = false;
  51. if (hasAlign) {
  52. padding = alignSize - value.size();
  53. if (padding < 0) {
  54. padding = 0;
  55. }
  56. padLeft = !alignLeft;
  57. padRight = alignLeft;
  58. }
  59. bool singleQuotes = false;
  60. bool doubleQuotes = false;
  61. bool escape = false;
  62. while (current < format.end()) {
  63. switch (*current++) {
  64. case 'q':
  65. singleQuotes = true;
  66. break;
  67. case 'Q':
  68. doubleQuotes = true;
  69. break;
  70. case 'h':
  71. escape = true;
  72. break;
  73. }
  74. }
  75. if (padLeft) {
  76. builder->AppendChar(' ', padding);
  77. }
  78. if (singleQuotes || doubleQuotes || escape) {
  79. for (const char* valueCurrent = value.begin(); valueCurrent < value.end(); ++valueCurrent) {
  80. char ch = *valueCurrent;
  81. if (ch == '\n') {
  82. builder->AppendString("\\n");
  83. } else if (ch == '\t') {
  84. builder->AppendString("\\t");
  85. } else if (ch == '\\') {
  86. builder->AppendString("\\\\");
  87. } else if (ch < PrintableASCIILow || ch > PrintableASCIIHigh) {
  88. builder->AppendString("\\x");
  89. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) >> 4]);
  90. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) & 0xf]);
  91. } else if ((singleQuotes && ch == '\'') || (doubleQuotes && ch == '\"')) {
  92. builder->AppendChar('\\');
  93. builder->AppendChar(ch);
  94. } else {
  95. builder->AppendChar(ch);
  96. }
  97. }
  98. } else {
  99. builder->AppendString(value);
  100. }
  101. if (padRight) {
  102. builder->AppendChar(' ', padding);
  103. }
  104. }
  105. // TString
  106. inline void FormatValue(TStringBuilderBase* builder, const TString& value, TStringBuf format)
  107. {
  108. FormatValue(builder, TStringBuf(value), format);
  109. }
  110. // const char*
  111. inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringBuf format)
  112. {
  113. FormatValue(builder, TStringBuf(value), format);
  114. }
  115. // char*
  116. inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf format)
  117. {
  118. FormatValue(builder, TStringBuf(value), format);
  119. }
  120. // char
  121. inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf format)
  122. {
  123. FormatValue(builder, TStringBuf(&value, 1), format);
  124. }
  125. // bool
  126. inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf format)
  127. {
  128. // Parse custom flags.
  129. bool lowercase = false;
  130. const char* current = format.begin();
  131. while (current != format.end()) {
  132. if (*current == 'l') {
  133. ++current;
  134. lowercase = true;
  135. } else if (IsQuotationSpecSymbol(*current)) {
  136. ++current;
  137. } else
  138. break;
  139. }
  140. auto str = lowercase
  141. ? (value ? TStringBuf("true") : TStringBuf("false"))
  142. : (value ? TStringBuf("True") : TStringBuf("False"));
  143. builder->AppendString(str);
  144. }
  145. // Fallback to ToString
  146. struct TToStringFallbackValueFormatterTag
  147. { };
  148. template <class TValue, class = void>
  149. struct TValueFormatter
  150. {
  151. static TToStringFallbackValueFormatterTag Do(TStringBuilderBase* builder, const TValue& value, TStringBuf format)
  152. {
  153. using ::ToString;
  154. FormatValue(builder, ToString(value), format);
  155. return {};
  156. }
  157. };
  158. // Enum
  159. template <class TEnum>
  160. struct TValueFormatter<TEnum, typename std::enable_if<TEnumTraits<TEnum>::IsEnum>::type>
  161. {
  162. static void Do(TStringBuilderBase* builder, TEnum value, TStringBuf format)
  163. {
  164. // Parse custom flags.
  165. bool lowercase = false;
  166. const char* current = format.begin();
  167. while (current != format.end()) {
  168. if (*current == 'l') {
  169. ++current;
  170. lowercase = true;
  171. } else if (IsQuotationSpecSymbol(*current)) {
  172. ++current;
  173. } else {
  174. break;
  175. }
  176. }
  177. FormatEnum(builder, value, lowercase);
  178. }
  179. };
  180. template <class TRange, class TFormatter>
  181. typename TFormattableView<TRange, TFormatter>::TBegin TFormattableView<TRange, TFormatter>::begin() const
  182. {
  183. return RangeBegin;
  184. }
  185. template <class TRange, class TFormatter>
  186. typename TFormattableView<TRange, TFormatter>::TEnd TFormattableView<TRange, TFormatter>::end() const
  187. {
  188. return RangeEnd;
  189. }
  190. template <class TRange, class TFormatter>
  191. TFormattableView<TRange, TFormatter> MakeFormattableView(
  192. const TRange& range,
  193. TFormatter&& formatter)
  194. {
  195. return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter)};
  196. }
  197. template <class TRange, class TFormatter>
  198. TFormattableView<TRange, TFormatter> MakeShrunkFormattableView(
  199. const TRange& range,
  200. TFormatter&& formatter,
  201. size_t limit)
  202. {
  203. return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter), limit};
  204. }
  205. template <class TRange, class TFormatter>
  206. void FormatRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  207. {
  208. builder->AppendChar('[');
  209. size_t index = 0;
  210. for (const auto& item : range) {
  211. if (index > 0) {
  212. builder->AppendString(DefaultJoinToStringDelimiter);
  213. }
  214. if (index == limit) {
  215. builder->AppendString(DefaultRangeEllipsisFormat);
  216. break;
  217. }
  218. formatter(builder, item);
  219. ++index;
  220. }
  221. builder->AppendChar(']');
  222. }
  223. template <class TRange, class TFormatter>
  224. void FormatKeyValueRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  225. {
  226. builder->AppendChar('{');
  227. size_t index = 0;
  228. for (const auto& item : range) {
  229. if (index > 0) {
  230. builder->AppendString(DefaultJoinToStringDelimiter);
  231. }
  232. if (index == limit) {
  233. builder->AppendString(DefaultRangeEllipsisFormat);
  234. break;
  235. }
  236. formatter(builder, item.first);
  237. builder->AppendString(DefaultKeyValueDelimiter);
  238. formatter(builder, item.second);
  239. ++index;
  240. }
  241. builder->AppendChar('}');
  242. }
  243. // TFormattableView
  244. template <class TRange, class TFormatter>
  245. struct TValueFormatter<TFormattableView<TRange, TFormatter>>
  246. {
  247. static void Do(TStringBuilderBase* builder, const TFormattableView<TRange, TFormatter>& range, TStringBuf /*format*/)
  248. {
  249. FormatRange(builder, range, range.Formatter, range.Limit);
  250. }
  251. };
  252. template <class TFormatter>
  253. TFormatterWrapper<TFormatter> MakeFormatterWrapper(
  254. TFormatter&& formatter)
  255. {
  256. return TFormatterWrapper<TFormatter>{
  257. .Formatter = std::move(formatter)
  258. };
  259. }
  260. // TFormatterWrapper
  261. template <class TFormatter>
  262. struct TValueFormatter<TFormatterWrapper<TFormatter>>
  263. {
  264. static void Do(TStringBuilderBase* builder, const TFormatterWrapper<TFormatter>& wrapper, TStringBuf /*format*/)
  265. {
  266. wrapper.Formatter(builder);
  267. }
  268. };
  269. // std::vector
  270. template <class T, class TAllocator>
  271. struct TValueFormatter<std::vector<T, TAllocator>>
  272. {
  273. static void Do(TStringBuilderBase* builder, const std::vector<T, TAllocator>& collection, TStringBuf /*format*/)
  274. {
  275. FormatRange(builder, collection, TDefaultFormatter());
  276. }
  277. };
  278. // std::span
  279. template <class T, size_t Extent>
  280. struct TValueFormatter<std::span<T, Extent>>
  281. {
  282. static void Do(TStringBuilderBase* builder, const std::span<T, Extent>& collection, TStringBuf /*format*/)
  283. {
  284. FormatRange(builder, collection, TDefaultFormatter());
  285. }
  286. };
  287. // TCompactVector
  288. template <class T, unsigned N>
  289. struct TValueFormatter<TCompactVector<T, N>>
  290. {
  291. static void Do(TStringBuilderBase* builder, const TCompactVector<T, N>& collection, TStringBuf /*format*/)
  292. {
  293. FormatRange(builder, collection, TDefaultFormatter());
  294. }
  295. };
  296. // std::set
  297. template <class T>
  298. struct TValueFormatter<std::set<T>>
  299. {
  300. static void Do(TStringBuilderBase* builder, const std::set<T>& collection, TStringBuf /*format*/)
  301. {
  302. FormatRange(builder, collection, TDefaultFormatter());
  303. }
  304. };
  305. // std::map
  306. template <class K, class V>
  307. struct TValueFormatter<std::map<K, V>>
  308. {
  309. static void Do(TStringBuilderBase* builder, const std::map<K, V>& collection, TStringBuf /*format*/)
  310. {
  311. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  312. }
  313. };
  314. // std::multimap
  315. template <class K, class V>
  316. struct TValueFormatter<std::multimap<K, V>>
  317. {
  318. static void Do(TStringBuilderBase* builder, const std::multimap<K, V>& collection, TStringBuf /*format*/)
  319. {
  320. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  321. }
  322. };
  323. // THashSet
  324. template <class T>
  325. struct TValueFormatter<THashSet<T>>
  326. {
  327. static void Do(TStringBuilderBase* builder, const THashSet<T>& collection, TStringBuf /*format*/)
  328. {
  329. FormatRange(builder, collection, TDefaultFormatter());
  330. }
  331. };
  332. // THashMultiSet
  333. template <class T>
  334. struct TValueFormatter<THashMultiSet<T>>
  335. {
  336. static void Do(TStringBuilderBase* builder, const THashMultiSet<T>& collection, TStringBuf /*format*/)
  337. {
  338. FormatRange(builder, collection, TDefaultFormatter());
  339. }
  340. };
  341. // THashMap
  342. template <class K, class V>
  343. struct TValueFormatter<THashMap<K, V>>
  344. {
  345. static void Do(TStringBuilderBase* builder, const THashMap<K, V>& collection, TStringBuf /*format*/)
  346. {
  347. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  348. }
  349. };
  350. // THashMultiMap
  351. template <class K, class V>
  352. struct TValueFormatter<THashMultiMap<K, V>>
  353. {
  354. static void Do(TStringBuilderBase* builder, const THashMultiMap<K, V>& collection, TStringBuf /*format*/)
  355. {
  356. FormatKeyValueRange(builder, collection, TDefaultFormatter());
  357. }
  358. };
  359. // TEnumIndexedArray
  360. template <class E, class T>
  361. struct TValueFormatter<TEnumIndexedArray<E, T>>
  362. {
  363. static void Do(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf format)
  364. {
  365. builder->AppendChar('{');
  366. bool firstItem = true;
  367. for (const auto& index : TEnumTraits<E>::GetDomainValues()) {
  368. if (!firstItem) {
  369. builder->AppendString(DefaultJoinToStringDelimiter);
  370. }
  371. FormatValue(builder, index, format);
  372. builder->AppendString(": ");
  373. FormatValue(builder, collection[index], format);
  374. firstItem = false;
  375. }
  376. builder->AppendChar('}');
  377. }
  378. };
  379. // std::pair
  380. template <class T1, class T2>
  381. struct TValueFormatter<std::pair<T1, T2>>
  382. {
  383. static void Do(TStringBuilderBase* builder, const std::pair<T1, T2>& value, TStringBuf format)
  384. {
  385. builder->AppendChar('{');
  386. FormatValue(builder, value.first, format);
  387. builder->AppendString(TStringBuf(", "));
  388. FormatValue(builder, value.second, format);
  389. builder->AppendChar('}');
  390. }
  391. };
  392. // std::optional
  393. inline void FormatValue(TStringBuilderBase* builder, std::nullopt_t, TStringBuf /*format*/)
  394. {
  395. builder->AppendString(TStringBuf("<null>"));
  396. }
  397. template <class T>
  398. struct TValueFormatter<std::optional<T>>
  399. {
  400. static void Do(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf format)
  401. {
  402. if (value) {
  403. FormatValue(builder, *value, format);
  404. } else {
  405. FormatValue(builder, std::nullopt, format);
  406. }
  407. }
  408. };
  409. template <class TValue>
  410. auto FormatValue(TStringBuilderBase* builder, const TValue& value, TStringBuf format) ->
  411. decltype(TValueFormatter<TValue>::Do(builder, value, format))
  412. {
  413. return TValueFormatter<TValue>::Do(builder, value, format);
  414. }
  415. namespace NDetail {
  416. template <class TValue>
  417. void FormatValueViaSprintf(
  418. TStringBuilderBase* builder,
  419. TValue value,
  420. TStringBuf format,
  421. TStringBuf genericSpec);
  422. template <class TValue>
  423. void FormatIntValue(
  424. TStringBuilderBase* builder,
  425. TValue value,
  426. TStringBuf format,
  427. TStringBuf genericSpec);
  428. void FormatPointerValue(
  429. TStringBuilderBase* builder,
  430. const void* value,
  431. TStringBuf format);
  432. } // namespace NDetail
  433. #define XX(valueType, castType, genericSpec) \
  434. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
  435. { \
  436. NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), format, genericSpec); \
  437. }
  438. XX(i8, i32, TStringBuf("d"))
  439. XX(ui8, ui32, TStringBuf("u"))
  440. XX(i16, i32, TStringBuf("d"))
  441. XX(ui16, ui32, TStringBuf("u"))
  442. XX(i32, i32, TStringBuf("d"))
  443. XX(ui32, ui32, TStringBuf("u"))
  444. #ifdef _win_
  445. XX(long long, i64, TStringBuf("lld"))
  446. XX(unsigned long long, ui64, TStringBuf("llu"))
  447. #else
  448. XX(long, i64, TStringBuf("ld"))
  449. XX(unsigned long, ui64, TStringBuf("lu"))
  450. #endif
  451. #undef XX
  452. #define XX(valueType, castType, genericSpec) \
  453. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
  454. { \
  455. NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), format, genericSpec); \
  456. }
  457. XX(double, double, TStringBuf("lf"))
  458. XX(float, float, TStringBuf("f"))
  459. #undef XX
  460. // Pointer
  461. template <class T>
  462. void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf format)
  463. {
  464. NYT::NDetail::FormatPointerValue(builder, static_cast<const void*>(value), format);
  465. }
  466. // TDuration (specialize for performance reasons)
  467. inline void FormatValue(TStringBuilderBase* builder, TDuration value, TStringBuf /*format*/)
  468. {
  469. builder->AppendFormat("%vus", value.MicroSeconds());
  470. }
  471. // TInstant (specialize for TFormatTraits)
  472. inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf format)
  473. {
  474. // TODO(babenko): optimize
  475. builder->AppendFormat("%v", ToString(value), format);
  476. }
  477. ////////////////////////////////////////////////////////////////////////////////
  478. namespace NDetail {
  479. template <class TArgFormatter>
  480. void FormatImpl(
  481. TStringBuilderBase* builder,
  482. TStringBuf format,
  483. const TArgFormatter& argFormatter)
  484. {
  485. size_t argIndex = 0;
  486. auto current = format.begin();
  487. while (true) {
  488. // Scan verbatim part until stop symbol.
  489. auto verbatimBegin = current;
  490. auto verbatimEnd = verbatimBegin;
  491. while (verbatimEnd != format.end() && *verbatimEnd != '%') {
  492. ++verbatimEnd;
  493. }
  494. // Copy verbatim part, if any.
  495. size_t verbatimSize = verbatimEnd - verbatimBegin;
  496. if (verbatimSize > 0) {
  497. builder->AppendString(TStringBuf(verbatimBegin, verbatimSize));
  498. }
  499. // Handle stop symbol.
  500. current = verbatimEnd;
  501. if (current == format.end()) {
  502. break;
  503. }
  504. YT_ASSERT(*current == '%');
  505. ++current;
  506. if (*current == '%') {
  507. // Verbatim %.
  508. builder->AppendChar('%');
  509. ++current;
  510. } else {
  511. // Scan format part until stop symbol.
  512. auto argFormatBegin = current;
  513. auto argFormatEnd = argFormatBegin;
  514. bool singleQuotes = false;
  515. bool doubleQuotes = false;
  516. while (
  517. argFormatEnd != format.end() &&
  518. *argFormatEnd != GenericSpecSymbol && // value in generic format
  519. *argFormatEnd != 'd' && // others are standard specifiers supported by printf
  520. *argFormatEnd != 'i' &&
  521. *argFormatEnd != 'u' &&
  522. *argFormatEnd != 'o' &&
  523. *argFormatEnd != 'x' &&
  524. *argFormatEnd != 'X' &&
  525. *argFormatEnd != 'f' &&
  526. *argFormatEnd != 'F' &&
  527. *argFormatEnd != 'e' &&
  528. *argFormatEnd != 'E' &&
  529. *argFormatEnd != 'g' &&
  530. *argFormatEnd != 'G' &&
  531. *argFormatEnd != 'a' &&
  532. *argFormatEnd != 'A' &&
  533. *argFormatEnd != 'c' &&
  534. *argFormatEnd != 's' &&
  535. *argFormatEnd != 'p' &&
  536. *argFormatEnd != 'n')
  537. {
  538. switch (*argFormatEnd) {
  539. case 'q':
  540. singleQuotes = true;
  541. break;
  542. case 'Q':
  543. doubleQuotes = true;
  544. break;
  545. case 'h':
  546. break;
  547. }
  548. ++argFormatEnd;
  549. }
  550. // Handle end of format string.
  551. if (argFormatEnd != format.end()) {
  552. ++argFormatEnd;
  553. }
  554. // 'n' means 'nothing'; skip the argument.
  555. if (*argFormatBegin != 'n') {
  556. // Format argument.
  557. TStringBuf argFormat(argFormatBegin, argFormatEnd);
  558. if (singleQuotes) {
  559. builder->AppendChar('\'');
  560. }
  561. if (doubleQuotes) {
  562. builder->AppendChar('"');
  563. }
  564. argFormatter(argIndex++, builder, argFormat);
  565. if (singleQuotes) {
  566. builder->AppendChar('\'');
  567. }
  568. if (doubleQuotes) {
  569. builder->AppendChar('"');
  570. }
  571. }
  572. current = argFormatEnd;
  573. }
  574. }
  575. }
  576. } // namespace NDetail
  577. ////////////////////////////////////////////////////////////////////////////////
  578. template <class... TArgs>
  579. TLazyMultiValueFormatter<TArgs...>::TLazyMultiValueFormatter(
  580. TStringBuf format,
  581. TArgs&&... args)
  582. : Format_(format)
  583. , Args_(std::forward<TArgs>(args)...)
  584. { }
  585. template <class... TArgs>
  586. void FormatValue(
  587. TStringBuilderBase* builder,
  588. const TLazyMultiValueFormatter<TArgs...>& value,
  589. TStringBuf /*format*/)
  590. {
  591. std::apply(
  592. [&] <class... TInnerArgs> (TInnerArgs&&... args) {
  593. builder->AppendFormat(value.Format_, std::forward<TInnerArgs>(args)...);
  594. },
  595. value.Args_);
  596. }
  597. template <class... TArgs>
  598. auto MakeLazyMultiValueFormatter(TStringBuf format, TArgs&&... args)
  599. {
  600. return TLazyMultiValueFormatter<TArgs...>(format, std::forward<TArgs>(args)...);
  601. }
  602. ////////////////////////////////////////////////////////////////////////////////
  603. template <class T>
  604. struct TFormatTraits
  605. {
  606. static constexpr bool HasCustomFormatValue = !std::is_same_v<
  607. decltype(FormatValue(
  608. static_cast<TStringBuilderBase*>(nullptr),
  609. *static_cast<const T*>(nullptr),
  610. TStringBuf())),
  611. TToStringFallbackValueFormatterTag>;
  612. };
  613. ////////////////////////////////////////////////////////////////////////////////
  614. template <size_t IndexBase, class... TArgs>
  615. struct TArgFormatterImpl;
  616. template <size_t IndexBase>
  617. struct TArgFormatterImpl<IndexBase>
  618. {
  619. void operator() (size_t /*index*/, TStringBuilderBase* builder, TStringBuf /*format*/) const
  620. {
  621. builder->AppendString(TStringBuf("<missing argument>"));
  622. }
  623. };
  624. template <size_t IndexBase, class THeadArg, class... TTailArgs>
  625. struct TArgFormatterImpl<IndexBase, THeadArg, TTailArgs...>
  626. {
  627. explicit TArgFormatterImpl(const THeadArg& headArg, const TTailArgs&... tailArgs)
  628. : HeadArg(headArg)
  629. , TailFormatter(tailArgs...)
  630. { }
  631. const THeadArg& HeadArg;
  632. TArgFormatterImpl<IndexBase + 1, TTailArgs...> TailFormatter;
  633. void operator() (size_t index, TStringBuilderBase* builder, TStringBuf format) const
  634. {
  635. YT_ASSERT(index >= IndexBase);
  636. if (index == IndexBase) {
  637. FormatValue(builder, HeadArg, format);
  638. } else {
  639. TailFormatter(index, builder, format);
  640. }
  641. }
  642. };
  643. ////////////////////////////////////////////////////////////////////////////////
  644. template <size_t Length, class... TArgs>
  645. void Format(
  646. TStringBuilderBase* builder,
  647. const char (&format)[Length],
  648. TArgs&&... args)
  649. {
  650. Format(builder, TStringBuf(format, Length - 1), std::forward<TArgs>(args)...);
  651. }
  652. template <class... TArgs>
  653. void Format(
  654. TStringBuilderBase* builder,
  655. TStringBuf format,
  656. TArgs&&... args)
  657. {
  658. TArgFormatterImpl<0, TArgs...> argFormatter(args...);
  659. NYT::NDetail::FormatImpl(builder, format, argFormatter);
  660. }
  661. template <size_t Length, class... TArgs>
  662. TString Format(
  663. const char (&format)[Length],
  664. TArgs&&... args)
  665. {
  666. TStringBuilder builder;
  667. Format(&builder, format, std::forward<TArgs>(args)...);
  668. return builder.Flush();
  669. }
  670. template <class... TArgs>
  671. TString Format(
  672. TStringBuf format,
  673. TArgs&&... args)
  674. {
  675. TStringBuilder builder;
  676. Format(&builder, format, std::forward<TArgs>(args)...);
  677. return builder.Flush();
  678. }
  679. ////////////////////////////////////////////////////////////////////////////////
  680. } // namespace NYT