12#include "expr/string_expr.h"
15namespace hedgedev::csl::ut::
string
17 template <expr::any_string T_left, expr::any_string T_right>
18 inline bool compare(
const T_left& in_left,
const T_right& in_right,
bool in_case_sensitive)
20 const auto strings =
encoding::precedent_convert(in_left, in_right);
22 const auto& left = strings[0];
23 const auto& right = strings[1];
25 if (left.length() != right.length())
28 if (!in_case_sensitive)
29 return lower(left) == lower(right);
34 template <expr::any_string T_str, expr::any_string T_substr>
35 inline bool contains(
const T_str& in_str,
const T_substr& in_substr,
bool in_case_sensitive)
37 const auto strings =
encoding::precedent_convert(in_str, in_substr);
39 const auto& str = strings[0];
40 const auto& substr = strings[1];
42 if (str.length() < substr.length())
45 using pi_string_t = expr::pi_string_t<T_str, T_substr>;
47 if (!in_case_sensitive)
49 using pi_string_char_t = expr::get_char_type_t<pi_string_t>;
51 const auto it = std::search(str.begin(), str.end(), substr.begin(), substr.end(),
53 [](pi_string_char_t in_left, pi_string_char_t in_right) ->
bool
55 return std::tolower(in_left) == std::tolower(in_right);
59 return it != str.end();
62 return str.find(substr) != pi_string_t::npos;
65 template <expr::any_string T_str,
typename T_str_char>
68 std::basic_stringstream<T_str_char> result{};
70 for (
const auto& c : expr::inferred_string_view_t(in_str))
72 if (c == in_search_char)
73 result << in_escape_char;
81 template <expr::any_string T>
85 va_start(args, in_str);
87 using str_char_t = expr::get_char_type_t<T>;
89 const auto str_sv = expr::inferred_string_view_t<T>(in_str);
91 size_t length =
sizeof(str_char_t);
93 if constexpr (std::is_same_v<str_char_t,
char>)
95 length += _vscprintf(str_sv.data(), args);
97 else if constexpr (std::is_same_v<str_char_t,
wchar_t>)
99 length += _vscwprintf(str_sv.data(), args);
103 static_assert(
false,
"Unsupported string type.");
106 const auto buffer = std::make_unique<str_char_t[]>(length);
110 if constexpr (std::is_same_v<str_char_t,
char>)
112 _vsnprintf_s(buffer.get(), length, _TRUNCATE, str_sv.data(), args);
114 else if constexpr (std::is_same_v<str_char_t,
wchar_t>)
116 _vsnwprintf_s(buffer.get(), length, _TRUNCATE, str_sv.data(), args);
122 return expr::inferred_string_t<T>(buffer.get(), length);
125 template <expr::any_string T>
130 std::basic_stringstream<expr::get_char_type_t<T>> stream(in_str);
131 expr::inferred_string_t<T> line{};
133 while (std::getline(stream, line))
134 result = std::max(result, line.length());
139 template <expr::any_string T_str, expr::any_string T_url,
typename T_result>
140 inline T_result
hyperlink(
const T_str& in_str,
const T_url& in_url)
142 const auto strings =
encoding::precedent_convert(in_str, in_url);
144 const auto& str = strings[0];
145 const auto& url = strings[1];
148 return T_result(str);
150 std::basic_stringstream<expr::get_char_type_t<T_result>> result{};
152 result << expr::create_inferred_string<T_result>(
"<a href=\"")
153 << url << expr::create_inferred_string<T_result>(
"\">")
154 << str << expr::create_inferred_string<T_result>(
"</a>");
159 template <expr::any_string T>
162 if constexpr (std::is_pointer_v<std::remove_cvref_t<T>>)
168 return expr::inferred_string_view_t<T>(in_str).empty();
171 template <expr::any_string T>
174 if (is_null_or_empty(in_str))
177 const auto str_sv = expr::inferred_string_view_t<T>(in_str);
179 return std::all_of(str_sv.begin(), str_sv.end(), [](expr::get_char_type_t<T> in_char) ->
bool
181 return std::isspace(in_char);
185 template <expr::any_string T_delimiter, expr::any_string T_strings>
188 if constexpr (!
expr::is_same_underlying_char_type_v<T_delimiter, T_strings>)
189 static_assert(
false,
"The delimiter must have the same underlying character type as the collection.");
191 std::basic_stringstream<expr::get_char_type_t<T_strings>> result{};
193 if (!in_strings.empty())
195 const auto length = in_strings.size();
197 for (size_t i = 0; i < length; i++)
199 const auto& str = in_strings[i];
206 result << in_delimiter;
213 template <expr::any_string T_delimiter, expr::any_string... T_args,
typename T_result>
214 inline T_result
join(
const T_delimiter& in_delimiter,
const T_args&... in_args)
216 const auto strings = encoding::precedent_convert(in_delimiter, in_args...);
218 return join(strings[0], std::vector(strings.begin() + 1, strings.end()));
221 template <expr::any_string T_str, expr::any_string T_pad_str,
typename T_result>
222 inline T_result
pad(
const T_str& in_str,
const T_pad_str& in_pad_str)
224 std::basic_stringstream<expr::get_char_type_t<T_result>> result{};
226 const auto strings =
encoding::precedent_convert(in_str, in_pad_str);
228 const auto& str = strings[0];
229 const auto& pad_str = strings[1];
231 result << pad_str << str << pad_str;
236 template <
typename T_result, expr::any_string T_str>
237 inline T_result
parse(
const T_str& in_str)
241 try_parse(in_str, result);
246 template <expr::any_string T_str>
249 const auto xml_regex = expr::create_inferred_string<expr::inferred_string_t<T_str>>(
"<[^>]*>");
251 return std::regex_replace(expr::inferred_string_view_t<T_str>(in_str).data(),
252 std::basic_regex<expr::get_char_type_t<T_str>>(xml_regex),
253 expr::inferred_string_t<T_str>());
256 template <expr::any_string T_str, expr::any_string T_delimiter,
typename T_result>
259 std::vector<T_result> result{};
261 using pi_string_view_t = expr::pi_string_view_t<T_str, T_delimiter>;
263 const auto strings =
encoding::precedent_convert(in_str, in_delimiter);
265 const auto& str = strings[0];
266 const auto& delimiter = strings[1];
269 const auto delimiter_length = delimiter.length();
273 const auto pos = str.find(delimiter, start);
274 const auto token = str.substr(start, pos == pi_string_view_t::npos ? str.size() - start : pos - start);
276 result.emplace_back(trim(token));
278 if (pos == pi_string_view_t::npos)
281 start = pos + delimiter_length;
287 template <expr::any_string T>
290 auto result = expr::inferred_string_t<T>(in_str);
294 std::transform(result.begin(), result.end(), result.begin(),
296 [](expr::get_char_type_t<T> in_char) ->
bool
298 return std::tolower(in_char);
306 template <expr::any_string T>
309 auto result = expr::inferred_string_t<T>(in_str);
313 std::transform(result.begin(), result.end(), result.begin(),
315 [](expr::get_char_type_t<T> in_char) ->
bool
317 return std::toupper(in_char);
325 template <expr::any_string T_str,
typename T_trim_chars>
328 const auto str_sv = expr::inferred_string_view_t<T_str>(in_str);
330 const auto it = std::find_if(str_sv.begin(), str_sv.end(),
332 [&](T_trim_chars in_char) ->
bool
334 if (in_trim_chars.has_value())
335 return std::find(in_trim_chars->begin(), in_trim_chars->end(), in_char) == in_trim_chars->end();
337 return !std::isspace(in_char);
341 return expr::inferred_string_t<T_str>(str_sv.substr(std::distance(str_sv.begin(), it)));
344 template <expr::any_string T_str,
typename T_trim_chars>
347 const auto str_sv = expr::inferred_string_view_t<T_str>(in_str);
349 const auto it = std::find_if_not(str_sv.rbegin(), str_sv.rend(),
351 [&](T_trim_chars in_char) ->
bool
353 if (in_trim_chars.has_value())
354 return std::find(in_trim_chars->begin(), in_trim_chars->end(), in_char) != in_trim_chars->end();
356 return std::isspace(in_char);
361 return expr::inferred_string_t<T_str>(str_sv.substr(0, std::distance(str_sv.begin(), it)));
364 template <expr::any_string T_str,
typename T_trim_chars>
365 inline expr::inferred_string_t<T_str> trim(
const T_str& in_str,
const std::optional<std::vector<T_trim_chars>> in_trim_chars)
367 return trim_end(trim_start(in_str, in_trim_chars), in_trim_chars);
370 template <expr::any_string T>
373 using inferred_string_t = expr::inferred_string_t<T>;
375 inferred_string_t result{};
377 const auto ellipsis = expr::create_inferred_string<inferred_string_t>(
"...");
379 if (in_ellipsis && in_max_length <= ellipsis.length())
382 result = ellipsis.substr(0, in_max_length);
386 const auto str_sv = expr::inferred_string_view_t<T>(in_str);
388 if (str_sv.length() <= in_max_length)
394 auto length = in_max_length;
397 length -= ellipsis.length();
401 result = inferred_string_t(str_sv.substr(0, length));
411 result += inferred_string_t(str_sv.substr(str_sv.length() - length, length));
419 template <expr::basic_string T_result,
typename T_value>
422 using string_char_t = expr::get_char_type_t<T_result>;
424 std::basic_stringstream<string_char_t> result{};
427 result <<
expr::create_inferred_string<T_result>(
"0x");
429 result << std::uppercase << std::hex;
432 result << std::setw(in_max_length) << std::setfill(string_char_t(
'0'));
434 if constexpr (std::is_pointer_v<T_value>)
436 result << uintptr_t(in_value);
446 template <
typename T_result, expr::any_string T_str>
447 inline bool try_parse(
const T_str& in_str, T_result& out_result)
449 if (is_null_or_whitespace(in_str))
452 if constexpr (std::is_same_v<T_str, T_result>)
457 else if constexpr (std::is_same_v<T_result, std::filesystem::path>)
459 out_result = std::filesystem::path(in_str);
462 else if constexpr (
expr::any_string<T_result>)
466 if (!try_convert<T_result>(in_str, result))
474 const auto str_trim = trim(in_str);
476 if constexpr (std::is_same_v<T_result,
bool>)
478 const auto str_trim_lower = lower(str_trim);
480 out_result = str_trim_lower == expr::create_inferred_string<expr::inferred_string_t<T_str>>(
"true") ||
481 str_trim_lower == expr::create_inferred_string<expr::inferred_string_t<T_str>>(
"1");
485 else if constexpr (std::is_integral_v<T_result> || std::is_enum_v<T_result> || std::is_same_v<T_result,
float> || std::is_same_v<T_result,
double>)
487 typename std::conditional_t<std::is_enum_v<T_result>, std::underlying_type<T_result>, std::type_identity<T_result>>::type result{};
490 if (!try_convert<std::string>(str_trim, chars))
493 if (std::from_chars(chars.data(), chars.data() + chars.size(), result).ec != std::errc{})
496 out_result = T_result(result);
504 template <expr::any_string T_str>
507 expr::inferred_string_t<T_str> result{};
509 const auto str_sv = expr::inferred_string_view_t<T_str>(in_str);
511 using string_char_t = expr::get_char_type_t<T_str>;
513 static constexpr auto line_break = string_char_t(
'\n');
514 static constexpr auto carriage_return = string_char_t(
'\r');
516 static constexpr string_char_t word_chars[] =
518 '.',
',',
';',
'!',
'?',
'\"',
'\\',
'/'
521 const string_char_t* pos = str_sv.data();
522 const string_char_t* end = str_sv.data() + str_sv.size();
524 const string_char_t* last_line_start = pos;
525 const string_char_t* last_word_start{};
527 bool is_inside_word =
true;
532 auto next_pos = pos + 1;
538 result.append(last_line_start, next_pos);
542 last_line_start = next_pos;
543 last_word_start =
nullptr;
545 is_inside_word =
true;
550 if (c == carriage_return)
557 is_inside_word = !std::isspace(c) &&
558 std::find(std::begin(word_chars), std::end(word_chars), c) != std::end(word_chars);
561 last_word_start = pos;
563 if (size_t(pos - last_line_start) >= in_max_width)
565 if (last_word_start && last_word_start > last_line_start)
568 result.append(last_line_start, last_word_start);
569 result.push_back(line_break);
571 last_line_start = last_word_start;
572 last_word_start =
nullptr;
577 result.append(last_line_start, pos);
578 result.push_back(line_break);
580 last_line_start = pos;
588 if (last_line_start < end)
589 result.append(last_line_start, end);
expr::inferred_string_t< T_str > trim_start(const T_str &in_str, std::optional< std::vector< T_trim_chars > > in_trim_chars={})
Definition string.inl:326
expr::inferred_string_t< T > format(const T in_str,...)
Definition string.inl:82
expr::inferred_string_t< T > lower(const T &in_str)
Definition string.inl:288
bool is_null_or_empty(const T &in_str)
Definition string.inl:160
T_result hyperlink(const T_str &in_str, const T_url &in_url)
Definition string.inl:140
expr::inferred_string_t< T_str > remove_xml_tags(const T_str &in_str)
Definition string.inl:247
bool is_null_or_whitespace(const T &in_str)
Definition string.inl:172
expr::inferred_string_t< T_str > trim_end(const T_str &in_str, std::optional< std::vector< T_trim_chars > > in_trim_chars={})
Definition string.inl:345
expr::inferred_string_t< T_str > wrap(const T_str &in_str, size_t in_max_width)
Definition string.inl:505
bool compare(const T_left &in_left, const T_right &in_right, bool in_case_sensitive=true)
Definition string.inl:18
T_result join(const T_delimiter &in_delimiter, const T_args &... in_args)
Definition string.inl:214
std::vector< T_result > split(const T_str &in_str, const T_delimiter &in_delimiter)
Definition string.inl:257
expr::inferred_string_t< T > upper(const T &in_str)
Definition string.inl:307
bool try_parse(const T_str &in_str, T_result &out_result)
Definition string.inl:447
expr::inferred_string_t< T_str > escape(const T_str &in_str, T_str_char in_search_char, T_str_char in_escape_char=T_str_char('\\'))
Definition string.inl:66
bool contains(const T_str &in_str, const T_substr &in_substr, bool in_case_sensitive=true)
Definition string.inl:35
T_result pad(const T_str &in_str, const T_pad_str &in_pad_str)
Definition string.inl:222
T_result hex(T_value in_value, size_t in_max_length=sizeof(size_t) *2, bool in_prefix=true)
Definition string.inl:420
T_result parse(const T_str &in_str)
Definition string.inl:237
size_t get_width(const T &in_str)
Definition string.inl:126
expr::inferred_string_t< T > truncate(const T &in_str, size_t in_max_length, bool in_truncate_end=true, bool in_ellipsis=true)
Definition string.inl:371
expr::inferred_string_t< T_strings > join(const T_delimiter &in_delimiter, const std::vector< T_strings > &in_strings)
Definition string.inl:186