1#ifndef ACTIONENGINE_REDIS_CHUNK_STORE_OPS_UNINDENT_H_ 
    2#define ACTIONENGINE_REDIS_CHUNK_STORE_OPS_UNINDENT_H_ 
   14namespace multiline_raw_string {
 
   15template <
class char_type>
 
   16using string_view = std::basic_string_view<char_type>;
 
   20template <
class char_type>
 
   21constexpr string_view<char_type> space_chars =
 
   22    std::declval<string_view<char_type>>();
 
   24constexpr inline string_view<char> space_chars<char> = 
" \f\n\r\t\v";
 
   26constexpr inline string_view<wchar_t> space_chars<wchar_t> = L
" \f\n\r\t\v";
 
   28constexpr inline string_view<char8_t> space_chars<char8_t> = u8
" \f\n\r\t\v";
 
   30constexpr inline string_view<char16_t> space_chars<char16_t> = u
" \f\n\r\t\v";
 
   32constexpr inline string_view<char32_t> space_chars<char32_t> = U
" \f\n\r\t\v";
 
   35template <
class char_type>
 
   36constexpr string_view<char_type> potential_line_endings[] =
 
   37    std::declval<string_view<char_type>[]>();
 
   40constexpr inline string_view<char> potential_line_endings<char>[] = {
 
   43constexpr inline string_view<wchar_t> potential_line_endings<wchar_t>[] = {
 
   44    L
"\r\n", L
"\r", L
"\n"};
 
   46constexpr inline string_view<char8_t> potential_line_endings<char8_t>[] = {
 
   47    u8
"\r\n", u8
"\r", u8
"\n"};
 
   49constexpr inline string_view<char16_t> potential_line_endings<char16_t>[] = {
 
   50    u
"\r\n", u
"\r", u
"\n"};
 
   52constexpr inline string_view<char32_t> potential_line_endings<char32_t>[] = {
 
   53    U
"\r\n", U
"\r", U
"\n"};
 
   56template <
class char_type>
 
   57constexpr char_type null_char = std::declval<char_type>();
 
   59constexpr inline char null_char<char> = 
'\0';
 
   61constexpr inline wchar_t null_char<wchar_t> = L
'\0';
 
   63constexpr inline char8_t null_char<char8_t> = 
static_cast<char8_t>(
'\0');
 
   65constexpr inline char16_t null_char<char16_t> = u
'\0';
 
   67constexpr inline char32_t null_char<char32_t> = U
'\0';
 
   71template <
class char_type>
 
   72consteval string_view<char_type> detect_line_ending(
 
   73    string_view<char_type> str) {
 
   74  return *std::ranges::max_element(potential_line_endings<char_type>, {},
 
   75                                   [str](string_view<char_type> line_ending) {
 
   78                                         std::views::split(str, line_ending);
 
   79                                     return std::ranges::distance(view);
 
   85template <
class char_type>
 
   86consteval string_view<char_type> get_leading_space_sequence(
 
   87    string_view<char_type> line) {
 
   88  return line.substr(0, line.find_first_not_of(space_chars<char_type>));
 
   94template <
class char_type>
 
   95consteval bool is_line_empty(string_view<char_type> line) {
 
   96  return get_leading_space_sequence(line).size() == line.size();
 
  102template <
class char_type>
 
  103consteval std::vector<string_view<char_type>> split_lines(
 
  104    string_view<char_type> str, string_view<char_type> line_ending) {
 
  105  std::vector<string_view<char_type>> lines;
 
  107  for (
auto line : std::views::split(str, line_ending)) {
 
  108    lines.emplace_back(line.begin(), line.end());
 
  112  if (lines.size() > 1 && is_line_empty(lines[0])) {
 
  113    lines.erase(lines.begin());
 
  115  if (lines.size() > 1 && is_line_empty(lines[lines.size() - 1])) {
 
  116    lines.erase(lines.end() - 1);
 
  125template <
class char_type>
 
  126consteval string_view<char_type> determine_common_space_prefix_sequence(
 
  127    std::vector<string_view<char_type>> 
const& lines) {
 
  128  std::vector<string_view<char_type>> space_sequences = {
 
  129      string_view<char_type>{}  
 
  132  for (string_view<char_type> line : lines) {
 
  133    string_view<char_type> spaces = get_leading_space_sequence(line);
 
  134    for (std::size_t len = 1; len <= spaces.size(); len++) {
 
  135      space_sequences.emplace_back(spaces.substr(0, len));
 
  139    std::ranges::sort(space_sequences);
 
  140    auto [first, last] = std::ranges::unique(space_sequences);
 
  141    space_sequences.erase(first, last);
 
  146  auto shared_prefixes = std::views::filter(
 
  147      space_sequences, [&lines](string_view<char_type> prefix) {
 
  148        return std::ranges::all_of(
 
  149            lines, [&prefix](string_view<char_type> line) {
 
  150              return line.empty() || line.starts_with(prefix);
 
  155  return *std::ranges::max_element(shared_prefixes, {},
 
  156                                   &string_view<char_type>::size);
 
  161template <
class char_type>
 
  162consteval std::vector<char_type> unindent_string(string_view<char_type> str) {
 
  163  string_view<char_type> line_ending = detect_line_ending(str);
 
  164  std::vector<string_view<char_type>> lines = split_lines(str, line_ending);
 
  165  string_view<char_type> common_space_sequence =
 
  166      determine_common_space_prefix_sequence(lines);
 
  168  std::vector<char_type> new_string;
 
  169  bool is_first = 
true;
 
  170  for (
auto line : lines) {
 
  175      new_string.insert(new_string.end(), line_ending.begin(),
 
  183    auto unindented = line.substr(common_space_sequence.size());
 
  184    new_string.insert(new_string.end(), unindented.begin(), unindented.end());
 
  188  new_string.push_back(null_char<char_type>);
 
  194template <
class char_type>
 
  195consteval std::size_t unindent_string_size(string_view<char_type> str) {
 
  196  return unindent_string(str).size();
 
  202template <
class _
char_type, std::
size_t size>
 
  203struct string_wrapper {
 
  204  using char_type = _char_type;
 
  206  consteval string_wrapper(
const char_type (&arr)[size]) {
 
  207    std::ranges::copy(arr, str);
 
  215template <
string_wrapper sw>
 
  216struct unindented_string_wrapper {
 
  217  using char_type = 
typename decltype(sw)::char_type;
 
  218  static constexpr std::size_t buffer_size =
 
  219      unindent_string_size<char_type>(sw.str);
 
  220  using array_ref = 
const char_type (&)[buffer_size];
 
  222  consteval unindented_string_wrapper(
int) {
 
  223    auto newstr = unindent_string<char_type>(sw.str);
 
  224    std::ranges::copy(newstr, buffer);
 
  227  consteval array_ref get()
 const { 
return buffer; }
 
  229  char_type buffer[buffer_size];
 
  235template <
string_wrapper str, unindented_
string_wrapper<str> unindented = 0>
 
  236consteval decltype(
auto) do_unindent() {
 
  237  return unindented.get();
 
  241template <
string_wrapper str>
 
  242consteval decltype(
auto) 
operator"" _unindent() {
 
  243  return do_unindent<str>();
 
  247using multiline_raw_string::operator
"" _unindent;