arg_router  1.4.0
C++ command line argument parsing and routing
exception_formatter.hpp
1 // Copyright (C) 2023 by Camden Mannett.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
4 
5 #pragma once
6 
7 #include "arg_router/parsing/token_type.hpp"
9 #include "arg_router/utility/tuple_iterator.hpp"
10 
11 #include <boost/mp11/algorithm.hpp>
12 #include <boost/mp11/list.hpp>
13 
14 namespace arg_router::utility
15 {
52 template <typename S>
54 {
55  template <std::size_t Start, typename J>
56  struct placeholder {
57  static constexpr std::size_t start = Start;
58  using joining = J;
59  };
60 
61  template <typename Str, typename PHs, std::size_t Current>
62  [[nodiscard]] static constexpr auto placeholders_impl() noexcept
63  {
64  constexpr auto start = Str::get().find("{", Current);
65  constexpr auto end = Str::get().find("}", Current + 1);
66 
67  if constexpr ((start != std::string_view::npos) && (end != std::string_view::npos)) {
68  return placeholders_impl<
69  Str,
70  boost::mp11::mp_push_back<
71  PHs,
72  placeholder<start,
73  AR_STRING_SV(Str::get().substr(start + 1, end - start - 1))>>,
74  end + 1>();
75  } else {
76  return PHs{};
77  }
78  }
79 
80  template <typename PH>
81  struct is_greedy {
82  static constexpr bool value = PH::joining::size() > 0;
83  };
84 
85  template <typename Str>
86  using generate_placeholders = std::decay_t<decltype(placeholders_impl<Str, std::tuple<>, 0>())>;
87 
88  using initial_placeholders = generate_placeholders<S>;
89 
90 public:
96  [[nodiscard]] static string format(const vector<parsing::token_type>& tokens)
97  {
98  if constexpr ((std::tuple_size_v<initial_placeholders>) > 0) {
99  return fmt<S>(tokens);
100  } else if (!tokens.empty()) {
101  using greedy_appended = typename S::template append_t<AR_STRING(": {, }")>;
102  return fmt<greedy_appended>(tokens);
103  }
104 
105  return string{S::get()};
106  }
107 
108 private:
109  // Make sure there's only one greedy_token_placeholder in the string at most, and it's at the
110  // end
111  [[nodiscard]] static constexpr bool placeholder_check() noexcept
112  {
113  if constexpr ((std::tuple_size_v<initial_placeholders>) > 1) {
114  constexpr auto greedy_count =
115  boost::mp11::mp_count_if<initial_placeholders, is_greedy>::value;
116  static_assert(greedy_count <= 1,
117  "Can only be one greedy entry in the formatted string");
118 
119  if constexpr (greedy_count == 1) {
120  return is_greedy<boost::mp11::mp_back<initial_placeholders>>::value;
121  }
122  }
123 
124  return true;
125  }
126  static_assert(placeholder_check(), "Greedy entry must be last in the formatted string");
127 
128  template <typename Str>
129  [[nodiscard]] static string fmt(const vector<parsing::token_type>& tokens)
130  {
131  using std::to_string;
132  using placeholders = generate_placeholders<Str>;
133 
134  constexpr auto bracket_width = std::size_t{2};
135 
136  auto str = string{Str::get()};
137 
138  // The placeholders positions need to take into account that previous placeholder's tokens
139  // may be different with than the placeholders, so the start position needs to be shifted
140  // by the rolling sum of the previous token widths (minus placeholder brackets width)
141  auto offset = std::size_t{0};
142 
143  auto it = tokens.begin();
144  utility::tuple_type_iterator<placeholders>([&](auto i) {
145  using PH = std::tuple_element_t<i, placeholders>;
146 
147  // Wouldn't normally create a local from static data, but this is necessary due to
148  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92654
149  constexpr auto joining_str = PH::joining::get();
150  if (it != tokens.end()) {
151  auto next_str = to_string(*it);
152  auto pos = PH::start + offset;
153  str.replace(pos, joining_str.size() + bracket_width, next_str);
154  ++it;
155 
156  offset += next_str.size() - bracket_width;
157 
158  if constexpr (!joining_str.empty()) {
159  // Greedily consume the remaining tokens
160  pos += next_str.size();
161  for (; it != std::end(tokens); ++it) {
162  next_str = joining_str + to_string(*it);
163  str.insert(pos, next_str);
164  pos += next_str.size();
165  }
166  }
167  } else {
168  // Replace any remaining placeholders with empty strings
169  str.replace(PH::start + offset, joining_str.size() + bracket_width, "");
170  offset -= bracket_width;
171  }
172  });
173 
174  return str;
175  }
176 };
177 } // namespace arg_router::utility
static string format(const vector< parsing::token_type > &tokens)
constexpr std::string_view to_string(prefix_type prefix) noexcept
Definition: token_type.hpp:25
std::vector< T, config::allocator< T > > vector
Definition: basic_types.hpp:39