libfly  6.2.2
C++20 utility library for Linux, macOS, and Windows
format_string.hpp
1 #pragma once
2 
3 #include "fly/concepts/concepts.hpp"
4 #include "fly/fly.hpp"
5 #include "fly/types/string/concepts.hpp"
6 #include "fly/types/string/detail/format_parameter_type.hpp"
7 #include "fly/types/string/detail/format_parse_context.hpp"
8 #include "fly/types/string/detail/format_specifier.hpp"
9 #include "fly/types/string/detail/traits.hpp"
10 #include "fly/types/string/formatters.hpp"
11 #include "fly/types/string/lexer.hpp"
12 #include "fly/types/string/literals.hpp"
13 
14 #include <array>
15 #include <cstdint>
16 #include <optional>
17 #include <tuple>
18 #include <type_traits>
19 
20 namespace fly::detail {
21 
34 template <fly::StandardCharacter CharType, typename... ParameterTypes>
36 {
39 
40 public:
44  template <std::size_t N>
45  FLY_CONSTEVAL BasicFormatString(const CharType (&format)[N]) noexcept;
46 
48  BasicFormatString &operator=(BasicFormatString &&) = default;
49 
53  constexpr FormatParseContext &context();
54 
58  std::optional<FormatSpecifier> next_specifier();
59 
60 private:
61  BasicFormatString(const BasicFormatString &) = delete;
62  BasicFormatString &operator=(const BasicFormatString &) = delete;
63 
71  constexpr FormatSpecifier parse_specifier();
72 
80  template <std::size_t N = 0>
81  constexpr void parse_user_defined_specifier(FormatSpecifier &specifier);
82 
83  static constexpr const auto s_left_brace = FLY_CHR(CharType, '{');
84  static constexpr const auto s_right_brace = FLY_CHR(CharType, '}');
85  static constexpr const auto s_colon = FLY_CHR(CharType, ':');
86 
87  static constexpr std::array<ParameterType, sizeof...(ParameterTypes)> s_parameters {
88  infer_parameter_type<ParameterTypes>()...};
89 
90  FormatParseContext m_context;
91 
92  std::array<FormatSpecifier, 64> m_specifiers;
93  std::size_t m_specifier_count {0};
94  std::size_t m_specifier_index {0};
95 };
96 
97 //==================================================================================================
98 template <fly::StandardCharacter CharType, typename... ParameterTypes>
99 template <std::size_t N>
101  const CharType (&format)[N]) noexcept :
102  m_context(format, s_parameters.data(), s_parameters.size())
103 {
104  std::optional<CharType> ch;
105 
106  while (!m_context.has_error() && (ch = m_context.lexer().consume()))
107  {
108  if (ch == s_left_brace)
109  {
110  if (m_context.lexer().consume_if(s_left_brace))
111  {
112  continue;
113  }
114  else if (m_specifier_count >= m_specifiers.size())
115  {
116  m_context.on_error("Exceeded maximum allowed number of specifiers");
117  }
118  else
119  {
120  m_specifiers[m_specifier_count++] = parse_specifier();
121  }
122  }
123  else if (ch == s_right_brace)
124  {
125  if (m_context.lexer().consume_if(s_right_brace))
126  {
127  continue;
128  }
129 
130  m_context.on_error("Closing brace } must be escaped");
131  }
132  }
133 }
134 
135 //==================================================================================================
136 template <fly::StandardCharacter CharType, typename... ParameterTypes>
139 {
140  return m_context;
141 }
142 
143 //==================================================================================================
144 template <fly::StandardCharacter CharType, typename... ParameterTypes>
146  -> std::optional<FormatSpecifier>
147 {
148  if (m_specifier_index >= m_specifier_count)
149  {
150  return std::nullopt;
151  }
152 
153  return std::move(m_specifiers[m_specifier_index++]);
154 }
155 
156 //==================================================================================================
157 template <fly::StandardCharacter CharType, typename... ParameterTypes>
158 constexpr auto BasicFormatString<CharType, ParameterTypes...>::parse_specifier() -> FormatSpecifier
159 {
160  // The opening { will have already been consumed, so the starting position is one less.
161  const auto starting_position = m_context.lexer().position() - 1;
162 
163  FormatSpecifier specifier(m_context);
164  specifier.m_parse_index = m_context.lexer().position();
165 
166  if (m_context.lexer().consume_if(s_colon))
167  {
168  specifier.m_parse_index = m_context.lexer().position();
169 
170  if (m_context.parameter_type(specifier.m_position) == ParameterType::UserDefined)
171  {
172  parse_user_defined_specifier(specifier);
173  }
174  else
175  {
176  specifier.parse(m_context);
177  }
178  }
179  else if (!m_context.lexer().consume_if(s_right_brace))
180  {
181  m_context.on_error("Detected unclosed replacement field - must end with }");
182  }
183 
184  specifier.m_size = m_context.lexer().position() - starting_position;
185  return specifier;
186 }
187 
188 //==================================================================================================
189 template <fly::StandardCharacter CharType, typename... ParameterTypes>
190 template <std::size_t N>
191 constexpr void BasicFormatString<CharType, ParameterTypes...>::parse_user_defined_specifier(
192  FormatSpecifier &specifier)
193 {
194  if constexpr (N < sizeof...(ParameterTypes))
195  {
196  if (N != specifier.m_position)
197  {
198  parse_user_defined_specifier<N + 1>(specifier);
199  return;
200  }
201 
202  using T = std::tuple_element_t<N, std::tuple<std::remove_cvref_t<ParameterTypes>...>>;
203  using Formatter = fly::Formatter<T, CharType>;
204 
205  if constexpr (fly::FormattableWithParsing<FormatParseContext, Formatter>)
206  {
207  Formatter formatter;
208  formatter.parse(m_context);
209 
210  if constexpr (fly::DerivedFrom<Formatter, FormatSpecifier>)
211  {
212  formatter.copy_formatting_options_into(specifier);
213  }
214  }
215  else
216  {
217  if (!m_context.lexer().consume_if(s_right_brace))
218  {
219  m_context.on_error(
220  "User-defined formatter without a parser may not have formatting options");
221  }
222  }
223  }
224 }
225 
226 } // namespace fly::detail
Definition: format_parse_context.hpp:22
Definition: format_string.hpp:36
constexpr FormatParseContext & context()
Definition: format_string.hpp:138
FLY_CONSTEVAL BasicFormatString(const CharType(&format)[N]) noexcept
Definition: format_string.hpp:100
std::optional< FormatSpecifier > next_specifier()
Definition: format_string.hpp:145
Definition: formatters.hpp:47
Definition: format_specifier.hpp:116
constexpr void parse(FormatParseContext &context)
Definition: format_specifier.hpp:510