qxLib
logger_specifiers.inl
Go to the documentation of this file.
1 /**
2 
3  @file logger_specifiers.inl
4  @author Khrapov
5  @date 9.09.2026
6  @copyright © Nick Khrapov, 2026. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 namespace details
14 {
15 
16 constexpr std::optional<logger_specifiers> get_log_specifier(string_view svName) noexcept
17 {
18  if (svName == QXT("c"))
19  return logger_specifiers::category;
20 
21  if (svName == QXT("v"))
22  return logger_specifiers::verbosity;
23 
24  if (svName == QXT("ti"))
25  return logger_specifiers::thread_id;
26 
27  if (svName == QXT("t"))
28  return logger_specifiers::time;
29 
30  if (svName == QXT("file"))
31  return logger_specifiers::file;
32 
33  if (svName == QXT("func"))
34  return logger_specifiers::function;
35 
36  if (svName == QXT("l"))
37  return logger_specifiers::line;
38 
39  if (svName == QXT("m"))
40  return logger_specifiers::message;
41 
42  return std::nullopt;
43 }
44 
45 } // namespace details
46 
47 template<class char_t, class traits_t>
48 compile_pattern_result compile_pattern(basic_string<char_t, traits_t>& sPattern) noexcept
49 {
50  char_type* const pData = sPattern.data();
51  const size_t nSize = sPattern.size();
52 
53  size_t nRead = 0;
54  size_t nWrite = 0;
55  size_t nDepth = 0;
56 
57  while (nRead < nSize)
58  {
59  const char_type ch = pData[nRead];
60 
61  if (ch == QXT('{'))
62  {
63  // Escaped opening brace.
64  //
65  // "{{tid}}" must remain "{{tid}}" and must not become "{{0}}".
66  if (nDepth == 0 && nRead + 1 < nSize && pData[nRead + 1] == QXT('{'))
67  {
68  pData[nWrite++] = QXT('{');
69  pData[nWrite++] = QXT('{');
70 
71  nRead += 2;
72  continue;
73  }
74 
75  pData[nWrite++] = QXT('{');
76  ++nRead;
77  ++nDepth;
78 
79  const size_t nNameBegin = nRead;
80 
81  // Parse only arg-id:
82  //
83  // {time}
84  // ^^^^
85  //
86  // {time:%H:%M:%S}
87  // ^^^^
88  while (nRead < nSize && pData[nRead] != QXT(':') && pData[nRead] != QXT('}') && pData[nRead] != QXT('{'))
89  {
90  ++nRead;
91  }
92 
93  if (nRead == nSize)
94  return compile_pattern_result::unclosed_field;
95 
96  // '{' cannot occur inside arg-id.
97  if (pData[nRead] == QXT('{'))
98  return compile_pattern_result::invalid_field;
99 
100  const string_view svName { pData + nNameBegin, nRead - nNameBegin };
101 
102  if (const auto eSpecifier = details::get_log_specifier(svName))
103  {
104  const auto nSpecifier = static_cast<size_t>(*eSpecifier);
105 
106  pData[nWrite++] = static_cast<char_type>(QXT('0') + nSpecifier);
107  }
108  else
109  {
110  // Keep standard positional arguments intact:
111  //
112  // {}
113  // {0}
114  // {123}
115  bool bNumeric = svName.empty();
116 
117  if (!svName.empty())
118  {
119  bNumeric = true;
120 
121  for (const char_type chName : svName)
122  {
123  if (chName < QXT('0') || chName > QXT('9'))
124  {
125  bNumeric = false;
126  break;
127  }
128  }
129  }
130 
131  if (!bNumeric)
132  return compile_pattern_result::unknown_specifier;
133 
134  for (const char_type chName : svName)
135  pData[nWrite++] = chName;
136  }
137 
138  // ':' or '}' is intentionally not consumed.
139  continue;
140  }
141 
142  if (ch == QXT('}'))
143  {
144  if (nDepth != 0)
145  {
146  // Close current replacement field.
147  pData[nWrite++] = ch;
148  ++nRead;
149  --nDepth;
150  continue;
151  }
152 
153  // Escaped closing brace.
154  if (nRead + 1 < nSize && pData[nRead + 1] == QXT('}'))
155  {
156  pData[nWrite++] = QXT('}');
157  pData[nWrite++] = QXT('}');
158 
159  nRead += 2;
160  continue;
161  }
162 
163  return compile_pattern_result::unmatched_closing_brace;
164  }
165 
166  pData[nWrite++] = ch;
167  ++nRead;
168  }
169 
170  if (nDepth != 0)
171  return compile_pattern_result::unclosed_field;
172 
173  sPattern.resize(nWrite);
174 
175  return compile_pattern_result::ok;
176 }
177 
179  string& sStorage,
180  string_view svPattern,
181  const category& category,
182  verbosity eVerbosity,
183  std::thread::id threadId,
184  std::chrono::system_clock::time_point messageTime,
185  string_view svFile,
186  string_view svFunction,
187  int nLine,
188  string_view svMessage)
189 {
190  // must be kept in line with logger_specifiers indices
191  sStorage.vformat(
192  svPattern,
193  category,
194  eVerbosity,
195  0,
196  std::chrono::floor<std::chrono::seconds>(messageTime),
197  svFile,
198  svFunction,
199  nLine,
200  svMessage);
201 }
202 
203 } // namespace qx
String class.
Definition: string.h:83
requires format_acceptable_args_c< char_t, args_t... > void vformat(string_view svFormat, args_t &&... args)
Clear the string and format it with the format string and the args.
Definition: string.inl:156
A category is a class that identifies a particular piece of code. This code can be located in differe...
Definition: category.h:62
void format_log_specifiers(string &sStorage, string_view svPattern, const category &category, verbosity eVerbosity, std::thread::id threadId, std::chrono::system_clock::time_point messageTime, string_view svFile, string_view svFunction, int nLine, string_view svMessage)
Format sStorage using qx::logger_specifiers.
compile_pattern_result compile_pattern(basic_string< char_t, traits_t > &sPattern) noexcept
Compile an fmt pattern with named arguments into replacement fields.