CommonLib
Loading...
Searching...
No Matches
String.inl
Go to the documentation of this file.
1#include <algorithm>
2#include <charconv>
3#include <iomanip>
4#include <regex>
5
6namespace hedgedev::csl::ut::string
7{
8 template <expr::AnyString TLeft, expr::AnyString TRight>
9 inline bool Compare(const TLeft& in_rLeft, const TRight& in_rRight, bool in_isCaseSensitive)
10 {
11 const auto strings = PrecedentConvert(in_rLeft, in_rRight);
12
13 const auto& left = strings[0];
14 const auto& right = strings[1];
15
16 if (left.length() != right.length())
17 return false;
18
19 if (!in_isCaseSensitive)
20 return ToLower(left) == ToLower(right);
21
22 return left == right;
23 }
24
25 template <expr::AnyString TString, expr::AnyString TSubString>
26 inline bool Contains(const TString& in_rStr, const TSubString& in_rSubStr, bool in_isCaseSensitive)
27 {
28 using TStringView = expr::PrecedentInferredStringView<TString, TSubString>;
29
30 const auto strings = PrecedentConvert(in_rStr, in_rSubStr);
31
32 const auto& rStr = strings[0];
33 const auto& rSubStr = strings[1];
34
35 if (rStr.length() < rSubStr.length())
36 return false;
37
38 if (!in_isCaseSensitive)
39 {
40 using TChar = expr::GetCharType_t<TStringView>;
41
42 const auto it = std::search(rStr.begin(), rStr.end(), rSubStr.begin(), rSubStr.end(),
43 //
44 [](TChar l, TChar r)
45 {
46 return std::tolower(l) == std::tolower(r);
47 }
48 );
49
50 return it != rStr.end();
51 }
52
53 return rStr.find(rSubStr) != TStringView::npos;
54 }
55
56 template <expr::AnyString TDst, expr::AnyString TSrc>
58 {
59 if constexpr (std::is_same_v<TDst, TSrc>)
60 {
61 return in_rStr;
62 }
63 else
64 {
65 TDst result{};
66
68
69 return result;
70 }
71 }
72
73 template <expr::AnyString TString, typename TChar>
75 {
77
78 for (const auto& c : expr::InferredStringView(in_rStr))
79 {
80 if (c == in_searchChar)
82
83 result << c;
84 }
85
86 return result.str();
87 }
88
89 template <expr::AnyString TString>
91 {
94
96
98 size_t length = sizeof(TChar);
99
100 if constexpr (std::is_same_v<TChar, char>)
101 {
103 }
104 else if constexpr (std::is_same_v<TChar, wchar_t>)
105 {
107 }
108 else
109 {
110 static_assert(false, "Unsupported string type.");
111 }
112
113 const auto upBuffer = std::make_unique<TChar[]>(length);
114
115 if (length > 0)
116 {
117 if constexpr (std::is_same_v<TChar, char>)
118 {
120 }
121 else if constexpr (std::is_same_v<TChar, wchar_t>)
122 {
124 }
125 }
126
127 va_end(args);
128
130 }
131
132 template <expr::AnyString TString>
134 {
135 size_t result{};
136
139
140 while (std::getline(stream, line))
142
143 return result;
144 }
145
146 template <expr::AnyString TString, expr::AnyString TUrl, typename TOut>
147 inline TOut Hyperlink(const TString& in_rStr, const TUrl& in_rUrl)
148 {
150
151 const auto& rStr = strings[0];
152 const auto& rUrl = strings[1];
153
154 if (rUrl.empty())
155 return TOut(rStr);
156
158
159 result << expr::CreateInferredString<TOut>("<a href=\"")
160 << rUrl << expr::CreateInferredString<TOut>("\">")
161 << rStr << expr::CreateInferredString<TOut>("</a>");
162
163 return result.str();
164 }
165
168 {
170 static_assert(false, "The delimiter must have the same underlying character type as the collection.");
171
173
174 if (!in_rStrings.empty())
175 {
176 const auto length = in_rStrings.size();
177
178 for (size_t i = 0; i < length; i++)
179 {
180 const auto& rStr = in_rStrings[i];
181
182 result << rStr;
183
184 if (i == length - 1)
185 continue;
186
188 }
189 }
190
191 return result.str();
192 }
193
194 template <expr::AnyString TDelimiter, expr::AnyString... TArgs, typename TOut>
195 inline TOut Join(const TDelimiter& in_rDelimiter, const TArgs&... in_rArgs)
196 {
198
199 return Join(strings[0], std::vector(strings.begin() + 1, strings.end()));
200 }
201
202 template <expr::AnyString TString, expr::AnyString TPadString, typename TOut>
204 {
206
208
209 const auto& rStr = strings[0];
210 const auto& rPadStr = strings[1];
211
212 result << rPadStr << rStr << rPadStr;
213
214 return result.str();
215 }
216
217 template <typename TOut, expr::AnyString TString>
218 inline TOut Parse(const TString& in_rStr)
219 {
220 TOut result{};
221
223
224 return result;
225 }
226
227 template <expr::AnyString... TArgs, typename TOut>
228 inline std::array<TOut, sizeof...(TArgs)> PrecedentConvert(const TArgs&... in_rArgs)
229 {
230 std::array<TOut, sizeof...(TArgs)> result{};
231
232 const auto args = std::make_tuple(in_rArgs...);
233 constexpr auto precedence = expr::GetStringTypePrecedence<TArgs...>();
234
235 [&] <size_t... Index>(std::index_sequence<Index...>)
236 {
237 const auto convert = [&] <expr::AnyString T>(const size_t in_index, const T& in_rStr)
238 {
239 if constexpr (expr::IsAllSame<TArgs...>)
240 {
241 if constexpr (expr::CString<T>)
242 {
243 // Create inferred string from C string.
245 }
246 else
247 {
248 // Copy original string.
250 }
251 }
252 else
253 {
254 if constexpr (std::is_same_v<T, TOut>)
255 {
256 // Copy original string.
258 }
259 else
260 {
261 // Convert string to precedent type.
263 }
264 }
265 };
266
267 (convert(Index, std::get<Index>(args)), ...);
268 }
269 (std::make_index_sequence<sizeof...(TArgs)>{});
270
271 return result;
272 }
273
274 template <expr::AnyString TString>
281
282 template <expr::AnyString TString, expr::AnyString TDelimiter, typename TOut>
284 {
285 std::vector<TOut> result{};
286
288
290
291 const auto& rStr = strings[0];
292 const auto& rDelimiter = strings[1];
293
294 size_t start{};
295 const auto delimiterLength = rDelimiter.length();
296
297 while (true)
298 {
299 const auto pos = rStr.find(rDelimiter, start);
300 const auto token = rStr.substr(start, pos == TStringView::npos ? rStr.size() - start : pos - start);
301
303
304 if (pos == TStringView::npos)
305 break;
306
308 }
309
310 return result;
311 }
312
313 template <expr::AnyString TString>
315 {
317
318 if (!result.empty())
319 {
321 [](expr::GetCharType_t<TString> c) { return std::tolower(c); });
322 }
323
324 return result;
325 }
326
327 template <expr::AnyString TString>
329 {
331
332 if (!result.empty())
333 {
335 [](expr::GetCharType_t<TString> c) { return std::toupper(c); });
336 }
337
338 return result;
339 }
340
341 template <expr::AnyString TString, typename TChar>
343 {
344 const auto str = expr::InferredStringView<TString>(in_rStr);
345
346 const auto it = std::find_if(str.begin(), str.end(),
347 //
348 [&](TChar c) -> bool
349 {
351 return std::find(in_trimChars->begin(), in_trimChars->end(), c) == in_trimChars->end();
352
353 return !std::isspace(c);
354 }
355 );
356
358 }
359
360 template <expr::AnyString TString, typename TChar>
362 {
363 const auto str = expr::InferredStringView<TString>(in_rStr);
364
365 const auto it = std::find_if_not(str.rbegin(), str.rend(),
366 //
367 [&](TChar c) -> bool
368 {
370 return std::find(in_trimChars->begin(), in_trimChars->end(), c) != in_trimChars->end();
371
372 return std::isspace(c);
373 }
374 )
375 .base();
376
378 }
379
380 template <expr::AnyString TString, typename TChar>
385
386 template <expr::AnyString TString>
388 {
390
391 const auto ellipsis = expr::CreateInferredString<TOut>("...");
392
393 // Truncate ellipsis to max length.
395 return TOut(ellipsis.substr(0, in_maxLength));
396
397 const auto str = expr::InferredStringView<TString>(in_rStr);
398
399 if (str.length() <= in_maxLength)
400 return TOut(str);
401
402 TOut result{};
403 auto length = in_maxLength;
404
405 if (in_ellipsis)
407
408 if (in_truncateEnd)
409 {
410 result = TOut(str.substr(0, length));
411
412 if (in_ellipsis)
413 result += ellipsis;
414 }
415 else
416 {
417 if (in_ellipsis)
418 result += ellipsis;
419
421 }
422
423 return result;
424 }
425
426 template <expr::BasicString TString, typename TValue>
428 {
430
432
433 if (in_prefix)
435
436 result << std::uppercase << std::hex;
437
438 if (in_maxLength)
439 result << std::setw(in_maxLength) << std::setfill(TChar('0'));
440
441 result << in_value;
442
443 return result.str();
444 }
445
446 template <expr::AnyString TDst, expr::AnyString TSrc>
447 inline bool TryConvert(const TSrc& in_rSrc, TDst& out_rDst)
448 {
451
452 const auto view = expr::InferredStringView<TSrc>(in_rSrc);
453 const auto size = view.size();
454
455 if (!size)
456 {
457 out_rDst = TDst();
458 return true;
459 }
460
461 if constexpr (std::is_same_v<TSrc, TDst>)
462 {
464 return true;
465 }
466 else if constexpr (std::is_same_v<TSrcChar, TDstChar>)
467 {
468 out_rDst = TDst(view);
469 return true;
470 }
471
472 if constexpr (std::is_same_v<TDst, std::string>)
473 {
474 if constexpr (std::is_same_v<TSrcChar, wchar_t>)
475 {
476 auto upBuffer = std::make_unique<char[]>(size + sizeof(char));
477#ifdef WIN32
478 char defaultChar = '?';
480 if (chars < size)
481 return false;
482#else
483 size_t chars{};
484 if (wcstombs_s(&chars, (char*)upBuffer.get(), size + sizeof(char), view.data(), size) != 0)
485 return false;
486#endif
487 out_rDst = std::string((char*)upBuffer.get(), size);
488 }
489 }
490 else if constexpr (std::is_same_v<TDst, std::wstring>)
491 {
492 if constexpr (std::is_same_v<TSrcChar, char>)
493 {
494 auto upBuffer = std::make_unique<wchar_t[]>(size + sizeof(wchar_t));
495#ifdef WIN32
497 if (chars < size)
498 return false;
499#else
500 size_t chars{};
501 if (mbstowcs_s(&chars, (wchar_t*)upBuffer.get(), size + sizeof(wchar_t), view.data(), size) != 0)
502 return false;
503#endif
504 out_rDst = std::wstring((wchar_t*)upBuffer.get(), size);
505 }
506 }
507 else
508 {
509 static_assert(false, "Unsupported destination string type.");
510 }
511
512 return true;
513 }
514
515 template <typename TOut, expr::AnyString TString>
516 inline bool TryParse(const TString& in_rStr, TOut& out_rValue)
517 {
518 const auto str = Trim(in_rStr);
519
520 if constexpr (std::is_same_v<TOut, bool>)
521 {
524
525 return true;
526 }
527 else if constexpr (std::is_same_v<TOut, float> || std::is_same_v<TOut, double>)
528 {
529 TOut result{};
531
533
534 return true;
535 }
536 else if constexpr (std::is_integral_v<TOut> || std::is_enum_v<TOut>)
537 {
538 out_rValue = static_cast<TOut>(std::atoll(str.data()));
539 return true;
540 }
541
542 return false;
543 }
544
545 template <expr::AnyString TString>
547 {
549
550 const auto str = expr::InferredStringView<TString>(in_rStr);
551
553
554 static constexpr auto s_kLineBreak = TChar('\n');
555 static constexpr auto s_kCarriageReturn = TChar('\r');
556
557 static constexpr TChar s_kWordChars[] =
558 {
559 '.', ',', ';', '!', '?', '\"', '\\', '/'
560 };
561
562 const TChar* pos = str.data();
563 const TChar* end = str.data() + str.size();
564
565 const TChar* lastLineStart = pos;
566 const TChar* lastWordStart{};
567
568 bool isInsideWord = true;
569
570 while (pos < end)
571 {
572 auto c = *pos;
573 auto nextPos = pos + 1;
574
575 if (std::iscntrl(c))
576 {
577 if (c == s_kLineBreak)
578 {
580
581 pos = nextPos;
582
584 lastWordStart = nullptr;
585
586 isInsideWord = true;
587
588 continue;
589 }
590
591 if (c == s_kCarriageReturn)
592 {
593 pos = nextPos;
594 continue;
595 }
596 }
597
598 isInsideWord = !std::isspace(c) &&
600
601 if (!isInsideWord)
603
605 {
607 {
608 // Wrap to last word boundary.
611
613 lastWordStart = nullptr;
614 }
615 else
616 {
617 // Wrap immediately.
620
622 }
623 }
624
625 pos = nextPos;
626 }
627
628 // Handle remaining characters.
629 if (lastLineStart < end)
631
632 return result;
633 }
634}
Definition String.h:8
bool Contains(const TString &in_rStr, const TSubString &in_rSubStr, bool in_isCaseSensitive=true)
Definition String.inl:26
bool Compare(const TLeft &in_rLeft, const TRight &in_rRight, bool in_isCaseSensitive=true)
Definition String.inl:9