Line | Count | Source |
1 | | /* A simple header-only C++ argument parser library. |
2 | | * |
3 | | * https://github.com/Taywee/args |
4 | | * |
5 | | * Copyright (c) 2016-2024 Taylor Richberger <taylor@axfive.net> and Pavel |
6 | | * Belikov |
7 | | * |
8 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | | * of this software and associated documentation files (the "Software"), to |
10 | | * deal in the Software without restriction, including without limitation the |
11 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
12 | | * sell copies of the Software, and to permit persons to whom the Software is |
13 | | * furnished to do so, subject to the following conditions: |
14 | | * |
15 | | * The above copyright notice and this permission notice shall be included in |
16 | | * all copies or substantial portions of the Software. |
17 | | * |
18 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
23 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
24 | | * IN THE SOFTWARE. |
25 | | */ |
26 | | |
27 | | /** \file args.hxx |
28 | | * \brief this single-header lets you use all of the args functionality |
29 | | * |
30 | | * The important stuff is done inside the args namespace |
31 | | */ |
32 | | |
33 | | #ifndef ARGS_HXX |
34 | | #define ARGS_HXX |
35 | | #pragma push_macro("min") |
36 | | #pragma push_macro("max") |
37 | | #undef min |
38 | | #undef max |
39 | | |
40 | | #define ARGS_VERSION "6.6.0" |
41 | | #define ARGS_VERSION_MAJOR 6 |
42 | | #define ARGS_VERSION_MINOR 6 |
43 | | #define ARGS_VERSION_PATCH 0 |
44 | | |
45 | | #include <algorithm> |
46 | | #include <iterator> |
47 | | #include <exception> |
48 | | #include <functional> |
49 | | #include <sstream> |
50 | | #include <string> |
51 | | #include <tuple> |
52 | | #include <vector> |
53 | | #include <unordered_map> |
54 | | #include <unordered_set> |
55 | | #include <type_traits> |
56 | | #include <cstddef> |
57 | | #include <cctype> |
58 | | #include <cerrno> |
59 | | #include <cstdlib> |
60 | | #include <limits> |
61 | | #include <iostream> |
62 | | |
63 | | #if defined(_MSC_VER) && _MSC_VER <= 1800 |
64 | | #define noexcept |
65 | | #endif |
66 | | |
67 | | /** \namespace args |
68 | | * \brief contains all the functionality of the args library |
69 | | */ |
70 | | namespace args |
71 | | { |
72 | | /** Getter to grab the value from the argument type. |
73 | | * |
74 | | * If the Get() function of the type returns a reference, so does this, and |
75 | | * the value will be modifiable. |
76 | | */ |
77 | | template <typename Option> |
78 | | auto get(Option &option_) -> decltype(option_.Get()) |
79 | | { |
80 | | return option_.Get(); |
81 | | } |
82 | | |
83 | | /** (INTERNAL) Count UTF-8 glyphs |
84 | | * |
85 | | * This is not reliable, and will fail for combinatory glyphs, but it's |
86 | | * good enough here for now. |
87 | | * |
88 | | * \param string The string to count glyphs from |
89 | | * \return The UTF-8 glyphs in the string |
90 | | */ |
91 | | inline std::string::size_type Glyphs(const std::string &string_) |
92 | 0 | { |
93 | 0 | std::string::size_type length = 0; |
94 | 0 | for (const char c: string_) |
95 | 0 | { |
96 | 0 | if ((c & 0xc0) != 0x80) |
97 | 0 | { |
98 | 0 | ++length; |
99 | 0 | } |
100 | 0 | } |
101 | 0 | return length; |
102 | 0 | } |
103 | | |
104 | | /** Safe addition to prevent integer overflow. |
105 | | * Returns true if the addition is successful, false if it would overflow. |
106 | | */ |
107 | | template<typename T> |
108 | | bool SafeAdd(T a, T b, T& out) noexcept |
109 | 0 | { |
110 | 0 | static_assert(std::is_integral<T>::value, "SafeAdd requires integral types."); |
111 | 0 | if (std::is_unsigned<T>::value) |
112 | 0 | { |
113 | 0 | using U = typename std::make_unsigned<T>::type; |
114 | 0 | const U ua = static_cast<U>(a); |
115 | 0 | const U ub = static_cast<U>(b); |
116 | 0 | const U maxv = std::numeric_limits<U>::max(); |
117 | 0 | if (ua > maxv - ub) |
118 | 0 | { |
119 | 0 | return false; |
120 | 0 | } |
121 | 0 | out = static_cast<T>(ua + ub); |
122 | 0 | return true; |
123 | 0 | } |
124 | 0 | else |
125 | 0 | { |
126 | 0 | #if defined(__clang__) || defined(__GNUC__) |
127 | 0 | return !__builtin_add_overflow(a, b, &out); |
128 | | #else |
129 | | // Fallback bounds check |
130 | | if (b > 0 && a > std::numeric_limits<T>::max() - b) |
131 | | { |
132 | | return false; |
133 | | } |
134 | | if (b < 0 && a < std::numeric_limits<T>::min() - b) |
135 | | { |
136 | | return false; |
137 | | } |
138 | | out = a + b; |
139 | | return true; |
140 | | #endif |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | /** Safe multiplication to prevent integer overflow. |
145 | | * Returns true if the multiplication is successful, false if it would overflow. |
146 | | */ |
147 | | template<typename T> |
148 | | bool SafeMultiply(T a, T b, T& out) noexcept |
149 | 0 | { |
150 | 0 | static_assert(std::is_integral<T>::value, "SafeMultiply requires integral types."); |
151 | |
|
152 | 0 | if (a == 0 || b == 0) |
153 | 0 | { |
154 | 0 | out = 0; |
155 | 0 | return true; |
156 | 0 | } |
157 | | |
158 | 0 | if (std::is_unsigned<T>::value) |
159 | 0 | { |
160 | 0 | using U = typename std::make_unsigned<T>::type; |
161 | 0 | const U ua = static_cast<U>(a); |
162 | 0 | const U ub = static_cast<U>(b); |
163 | 0 | const U maxv = std::numeric_limits<U>::max(); |
164 | 0 | if (ub > maxv / ua) |
165 | 0 | { |
166 | 0 | return false; |
167 | 0 | } |
168 | 0 | out = static_cast<T>(ua * ub); |
169 | 0 | return true; |
170 | 0 | } |
171 | 0 | else |
172 | 0 | { |
173 | 0 | #if defined(__clang__) || defined(__GNUC__) |
174 | 0 | return !__builtin_mul_overflow(a, b, &out); |
175 | | #else |
176 | | // Fallback bounds check |
177 | | if (a == -1 && b == std::numeric_limits<T>::min()) |
178 | | { |
179 | | return false; |
180 | | } |
181 | | if (b == -1 && a == std::numeric_limits<T>::min()) |
182 | | { |
183 | | return false; |
184 | | } |
185 | | if ((a > 0 && b > 0 && a > std::numeric_limits<T>::max() / b) || |
186 | | (a > 0 && b < 0 && b < std::numeric_limits<T>::min() / a) || |
187 | | (a < 0 && b > 0 && a < std::numeric_limits<T>::min() / b) || |
188 | | (a < 0 && b < 0 && a < std::numeric_limits<T>::max() / b)) |
189 | | { |
190 | | return false; |
191 | | } |
192 | | out = a * b; |
193 | | return true; |
194 | | #endif |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | /** Safe subtraction to prevent integer underflow. |
199 | | * Returns true if the subtraction is successful, false if it would underflow. |
200 | | */ |
201 | | template<typename T> |
202 | | bool SafeSub(T a, T b, T& out) noexcept |
203 | | { |
204 | | static_assert(std::is_integral<T>::value, "SafeSub requires integral types."); |
205 | | if (std::is_unsigned<T>::value) |
206 | | { |
207 | | if (a < b) |
208 | | { |
209 | | return false; |
210 | | } |
211 | | out = a - b; |
212 | | return true; |
213 | | } |
214 | | else |
215 | | { |
216 | | #if defined(__clang__) || defined(__GNUC__) |
217 | | return !__builtin_sub_overflow(a, b, &out); |
218 | | #else |
219 | | // Fallback bounds check |
220 | | if (b > 0 && a < std::numeric_limits<T>::min() + b) |
221 | | { |
222 | | return false; |
223 | | } |
224 | | if (b < 0 && a > std::numeric_limits<T>::max() + b) |
225 | | { |
226 | | return false; |
227 | | } |
228 | | out = a - b; |
229 | | return true; |
230 | | #endif |
231 | | } |
232 | | } |
233 | | |
234 | | /** Safe negation to prevent integer overflow. |
235 | | * Returns true if the negation is successful, false if it would overflow. |
236 | | */ |
237 | | // Unsigned overload |
238 | | template<typename T> |
239 | | typename std::enable_if<std::is_unsigned<T>::value, bool>::type |
240 | | SafeNeg(T a, T& out) noexcept |
241 | | { |
242 | | static_assert(std::is_integral<T>::value, "SafeNeg requires integral types."); |
243 | | if (a != 0) |
244 | | { |
245 | | return false; |
246 | | } |
247 | | out = 0; |
248 | | return true; |
249 | | } |
250 | | |
251 | | // Signed overload |
252 | | template<typename T> |
253 | | typename std::enable_if<std::is_signed<T>::value, bool>::type |
254 | | SafeNeg(T a, T& out) noexcept |
255 | | { |
256 | | static_assert(std::is_integral<T>::value, "SafeNeg requires integral types."); |
257 | | if (a == std::numeric_limits<T>::min()) |
258 | | { |
259 | | return false; |
260 | | } |
261 | | out = -a; |
262 | | return true; |
263 | | } |
264 | | |
265 | | /** (INTERNAL) Wrap a vector of words into a vector of lines |
266 | | * |
267 | | * Empty words are skipped. Word "\n" forces wrapping. |
268 | | * |
269 | | * \param begin The begin iterator |
270 | | * \param end The end iterator |
271 | | * \param width The width of the body |
272 | | * \param firstlinewidth the width of the first line, defaults to the width of the body |
273 | | * \param firstlineindent the indent of the first line, defaults to 0 |
274 | | * \return the vector of lines |
275 | | */ |
276 | | template <typename It> |
277 | | inline std::vector<std::string> Wrap(It begin, |
278 | | It end, |
279 | | const std::string::size_type width, |
280 | | std::string::size_type firstlinewidth = 0, |
281 | | std::string::size_type firstlineindent = 0) |
282 | 0 | { |
283 | 0 | std::vector<std::string> output; |
284 | 0 | std::string line(firstlineindent, ' '); |
285 | 0 | bool empty = true; |
286 | 0 |
|
287 | 0 | if (firstlinewidth == 0) |
288 | 0 | { |
289 | 0 | firstlinewidth = width; |
290 | 0 | } |
291 | 0 |
|
292 | 0 | auto currentwidth = firstlinewidth; |
293 | 0 |
|
294 | 0 | for (auto it = begin; it != end; ++it) |
295 | 0 | { |
296 | 0 | if (it->empty()) |
297 | 0 | { |
298 | 0 | continue; |
299 | 0 | } |
300 | 0 |
|
301 | 0 | if (*it == "\n") |
302 | 0 | { |
303 | 0 | if (!empty) |
304 | 0 | { |
305 | 0 | output.push_back(line); |
306 | 0 | line.clear(); |
307 | 0 | empty = true; |
308 | 0 | currentwidth = width; |
309 | 0 | } |
310 | 0 |
|
311 | 0 | continue; |
312 | 0 | } |
313 | 0 |
|
314 | 0 | auto itemsize = Glyphs(*it); |
315 | 0 | |
316 | 0 | // Refactored to prevent integer overflow |
317 | 0 | bool needsWrap = false; |
318 | 0 | if (itemsize >= currentwidth) |
319 | 0 | { |
320 | 0 | needsWrap = true; |
321 | 0 | } |
322 | 0 | else |
323 | 0 | { |
324 | 0 | size_t remainingWidth = (currentwidth > itemsize) ? (currentwidth - itemsize) : 0; |
325 | 0 | size_t nextLength = 0; |
326 | 0 | if (!SafeAdd<std::string::size_type>(line.length(), static_cast<std::string::size_type>(1), nextLength) || nextLength > remainingWidth) |
327 | 0 | { |
328 | 0 | needsWrap = true; |
329 | 0 | } |
330 | 0 | } |
331 | 0 | |
332 | 0 | if (needsWrap) |
333 | 0 | { |
334 | 0 | if (!empty) |
335 | 0 | { |
336 | 0 | output.push_back(line); |
337 | 0 | line.clear(); |
338 | 0 | empty = true; |
339 | 0 | currentwidth = width; |
340 | 0 | } |
341 | 0 | } |
342 | 0 |
|
343 | 0 | if (itemsize > 0) |
344 | 0 | { |
345 | 0 | if (!empty) |
346 | 0 | { |
347 | 0 | line += ' '; |
348 | 0 | } |
349 | 0 |
|
350 | 0 | line += *it; |
351 | 0 | empty = false; |
352 | 0 | } |
353 | 0 | } |
354 | 0 |
|
355 | 0 | if (!empty) |
356 | 0 | { |
357 | 0 | output.push_back(line); |
358 | 0 | } |
359 | 0 |
|
360 | 0 | return output; |
361 | 0 | } Unexecuted instantiation: std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > args::Wrap<std::__1::istream_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char, std::__1::char_traits<char>, long> >(std::__1::istream_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char, std::__1::char_traits<char>, long>, std::__1::istream_iterator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, char, std::__1::char_traits<char>, long>, unsigned long, unsigned long, unsigned long) Unexecuted instantiation: std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > args::Wrap<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, unsigned long, unsigned long, unsigned long) |
362 | | |
363 | | namespace detail |
364 | | { |
365 | | template <typename T> |
366 | | std::string Join(const T& array, const std::string &delimiter) |
367 | 0 | { |
368 | 0 | std::string res; |
369 | | |
370 | | // Safely compute reservation size to avoid unbounded reallocations |
371 | 0 | using size_type = std::string::size_type; |
372 | 0 | size_type total = 0; |
373 | 0 | size_type count = 0; |
374 | 0 | const size_type delim_size = static_cast<size_type>(delimiter.size()); |
375 | 0 | bool can_reserve = true; |
376 | | |
377 | 0 | for (const auto &element : array) |
378 | 0 | { |
379 | 0 | const size_type elem_size = static_cast<size_type>(element.size()); |
380 | 0 | if (!SafeAdd<size_type>(total, elem_size, total)) |
381 | 0 | { |
382 | 0 | can_reserve = false; |
383 | 0 | break; |
384 | 0 | } |
385 | 0 | ++count; |
386 | 0 | } |
387 | | |
388 | 0 | if (can_reserve && count > 1) |
389 | 0 | { |
390 | 0 | size_type delim_count = count - 1; |
391 | 0 | size_type delim_total = 0; |
392 | 0 | if (!SafeMultiply<size_type>(delim_count, delim_size, delim_total) || |
393 | 0 | !SafeAdd<size_type>(total, delim_total, total)) |
394 | 0 | { |
395 | 0 | can_reserve = false; |
396 | 0 | } |
397 | 0 | } |
398 | | |
399 | 0 | if (can_reserve && total > 0) |
400 | 0 | { |
401 | 0 | res.reserve(total); |
402 | 0 | } |
403 | | |
404 | 0 | bool first = true; |
405 | 0 | for (const auto &element : array) |
406 | 0 | { |
407 | 0 | if (!first) |
408 | 0 | { |
409 | 0 | res += delimiter; |
410 | 0 | } |
411 | 0 | res += element; |
412 | 0 | first = false; |
413 | 0 | } |
414 | | |
415 | 0 | return res; |
416 | 0 | } |
417 | | } |
418 | | |
419 | | /** (INTERNAL) Wrap a string into a vector of lines |
420 | | * |
421 | | * This is quick and hacky, but works well enough. You can specify a |
422 | | * different width for the first line |
423 | | * |
424 | | * \param width The width of the body |
425 | | * \param firstlinewid the width of the first line, defaults to the width of the body |
426 | | * \return the vector of lines |
427 | | */ |
428 | | inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0) |
429 | 0 | { |
430 | 0 | // Preserve existing line breaks |
431 | 0 | const auto newlineloc = in.find('\n'); |
432 | 0 | if (newlineloc != in.npos) |
433 | 0 | { |
434 | 0 | auto first = Wrap(std::string(in, 0, newlineloc), width); |
435 | 0 | auto second = Wrap(std::string(in, newlineloc + 1), width); |
436 | 0 | first.insert( |
437 | 0 | std::end(first), |
438 | 0 | std::make_move_iterator(std::begin(second)), |
439 | 0 | std::make_move_iterator(std::end(second))); |
440 | 0 | return first; |
441 | 0 | } |
442 | 0 |
|
443 | 0 | std::istringstream stream(in); |
444 | 0 | std::string::size_type indent = 0; |
445 | 0 |
|
446 | 0 | for (auto c : in) |
447 | 0 | { |
448 | 0 | if (!std::isspace(static_cast<unsigned char>(c))) |
449 | 0 | { |
450 | 0 | break; |
451 | 0 | } |
452 | 0 | ++indent; |
453 | 0 | } |
454 | 0 |
|
455 | 0 | return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(), |
456 | 0 | width, firstlinewidth, indent); |
457 | 0 | } |
458 | | |
459 | | #ifdef ARGS_NOEXCEPT |
460 | | /// Error class, for when ARGS_NOEXCEPT is defined |
461 | | enum class Error |
462 | | { |
463 | | None, |
464 | | Usage, |
465 | | Parse, |
466 | | Validation, |
467 | | Required, |
468 | | Map, |
469 | | Extra, |
470 | | Help, |
471 | | Subparser, |
472 | | Completion, |
473 | | }; |
474 | | #else |
475 | | /** Base error class |
476 | | */ |
477 | | class Error : public std::runtime_error |
478 | | { |
479 | | public: |
480 | 950 | Error(const std::string &problem) : std::runtime_error(problem) {} |
481 | 0 | virtual ~Error() {} |
482 | | }; |
483 | | |
484 | | /** Errors that occur during usage |
485 | | */ |
486 | | class UsageError : public Error |
487 | | { |
488 | | public: |
489 | 0 | UsageError(const std::string &problem) : Error(problem) {} |
490 | 0 | virtual ~UsageError() {} |
491 | | }; |
492 | | |
493 | | /** Errors that occur during regular parsing |
494 | | */ |
495 | | class ParseError : public Error |
496 | | { |
497 | | public: |
498 | 932 | ParseError(const std::string &problem) : Error(problem) {} |
499 | 0 | virtual ~ParseError() {} |
500 | | }; |
501 | | |
502 | | /** Errors that are detected from group validation after parsing finishes |
503 | | */ |
504 | | class ValidationError : public Error |
505 | | { |
506 | | public: |
507 | 0 | ValidationError(const std::string &problem) : Error(problem) {} |
508 | 0 | virtual ~ValidationError() {} |
509 | | }; |
510 | | |
511 | | /** Errors that when a required flag is omitted |
512 | | */ |
513 | | class RequiredError : public ValidationError |
514 | | { |
515 | | public: |
516 | 0 | RequiredError(const std::string &problem) : ValidationError(problem) {} |
517 | 0 | virtual ~RequiredError() {} |
518 | | }; |
519 | | |
520 | | /** Errors in map lookups |
521 | | */ |
522 | | class MapError : public ParseError |
523 | | { |
524 | | public: |
525 | 0 | MapError(const std::string &problem) : ParseError(problem) {} |
526 | 0 | virtual ~MapError() {} |
527 | | }; |
528 | | |
529 | | /** Error that occurs when a singular flag is specified multiple times |
530 | | */ |
531 | | class ExtraError : public ParseError |
532 | | { |
533 | | public: |
534 | 0 | ExtraError(const std::string &problem) : ParseError(problem) {} |
535 | 0 | virtual ~ExtraError() {} |
536 | | }; |
537 | | |
538 | | /** An exception that indicates that the user has requested help |
539 | | */ |
540 | | class Help : public Error |
541 | | { |
542 | | public: |
543 | 18 | Help(const std::string &flag) : Error(flag) {} |
544 | 0 | virtual ~Help() {} |
545 | | }; |
546 | | |
547 | | /** (INTERNAL) An exception that emulates coroutine-like control flow for subparsers. |
548 | | */ |
549 | | class SubparserError : public Error |
550 | | { |
551 | | public: |
552 | 0 | SubparserError() : Error("") {} |
553 | 0 | virtual ~SubparserError() {} |
554 | | }; |
555 | | |
556 | | /** An exception that contains autocompletion reply |
557 | | */ |
558 | | class Completion : public Error |
559 | | { |
560 | | public: |
561 | 0 | Completion(const std::string &flag) : Error(flag) {} |
562 | 0 | virtual ~Completion() {} |
563 | | }; |
564 | | #endif |
565 | | |
566 | | /** A simple unified option type for unified initializer lists for the Matcher class. |
567 | | */ |
568 | | struct EitherFlag |
569 | | { |
570 | | const bool isShort; |
571 | | const char shortFlag; |
572 | | const std::string longFlag; |
573 | 6.35k | EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {} |
574 | 2.91k | EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {} |
575 | 19.9k | EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {} |
576 | | |
577 | | /** Get just the long flags from an initializer list of EitherFlags |
578 | | */ |
579 | | static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags) |
580 | 2.91k | { |
581 | 2.91k | std::unordered_set<std::string> longFlags; |
582 | 2.91k | for (const EitherFlag &flag: flags) |
583 | 5.83k | { |
584 | 5.83k | if (!flag.isShort) |
585 | 2.91k | { |
586 | 2.91k | longFlags.insert(flag.longFlag); |
587 | 2.91k | } |
588 | 5.83k | } |
589 | 2.91k | return longFlags; |
590 | 2.91k | } |
591 | | |
592 | | /** Get just the short flags from an initializer list of EitherFlags |
593 | | */ |
594 | | static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags) |
595 | 2.91k | { |
596 | 2.91k | std::unordered_set<char> shortFlags; |
597 | 2.91k | for (const EitherFlag &flag: flags) |
598 | 5.83k | { |
599 | 5.83k | if (flag.isShort) |
600 | 2.91k | { |
601 | 2.91k | shortFlags.insert(flag.shortFlag); |
602 | 2.91k | } |
603 | 5.83k | } |
604 | 2.91k | return shortFlags; |
605 | 2.91k | } |
606 | | |
607 | | std::string str() const |
608 | 0 | { |
609 | 0 | return isShort ? std::string(1, shortFlag) : longFlag; |
610 | 0 | } |
611 | | |
612 | | std::string str(const std::string &shortPrefix, const std::string &longPrefix) const |
613 | 0 | { |
614 | 0 | return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag; |
615 | 0 | } |
616 | | }; |
617 | | |
618 | | |
619 | | |
620 | | /** A class of "matchers", specifying short and flags that can possibly be |
621 | | * matched. |
622 | | * |
623 | | * This is supposed to be constructed and then passed in, not used directly |
624 | | * from user code. |
625 | | */ |
626 | | class Matcher |
627 | | { |
628 | | private: |
629 | | const std::unordered_set<char> shortFlags; |
630 | | const std::unordered_set<std::string> longFlags; |
631 | | |
632 | | public: |
633 | | /** Specify short and long flags separately as iterators |
634 | | * |
635 | | * ex: `args::Matcher(shortFlags.begin(), shortFlags.end(), longFlags.begin(), longFlags.end())` |
636 | | */ |
637 | | template <typename ShortIt, typename LongIt> |
638 | | Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) : |
639 | 2.91k | shortFlags(shortFlagsStart, shortFlagsEnd), |
640 | 2.91k | longFlags(longFlagsStart, longFlagsEnd) |
641 | 2.91k | { |
642 | 2.91k | if (shortFlags.empty() && longFlags.empty()) |
643 | 0 | { |
644 | 0 | #ifndef ARGS_NOEXCEPT |
645 | 0 | throw UsageError("empty Matcher"); |
646 | 0 | #endif |
647 | 0 | } |
648 | 2.91k | } |
649 | | |
650 | | #ifdef ARGS_NOEXCEPT |
651 | | /// Only for ARGS_NOEXCEPT |
652 | | Error GetError() const noexcept |
653 | | { |
654 | | return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None; |
655 | | } |
656 | | #endif |
657 | | |
658 | | /** Specify short and long flags separately as iterables |
659 | | * |
660 | | * ex: `args::Matcher(shortFlags, longFlags)` |
661 | | */ |
662 | | template <typename Short, typename Long> |
663 | | Matcher(Short &&shortIn, Long &&longIn) : |
664 | 2.91k | Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn)) |
665 | 2.91k | {} |
666 | | |
667 | | /** Specify a mixed single initializer-list of both short and long flags |
668 | | * |
669 | | * This is the fancy one. It takes a single initializer list of |
670 | | * any number of any mixed kinds of flags. Chars are |
671 | | * automatically interpreted as short flags, and strings are |
672 | | * automatically interpreted as long flags: |
673 | | * |
674 | | * args::Matcher{'a'} |
675 | | * args::Matcher{"foo"} |
676 | | * args::Matcher{'h', "help"} |
677 | | * args::Matcher{"foo", 'f', 'F', "FoO"} |
678 | | */ |
679 | | Matcher(std::initializer_list<EitherFlag> in) : |
680 | 2.91k | Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {} |
681 | | |
682 | 2.91k | Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags)) |
683 | 2.91k | {} |
684 | | |
685 | 5.83k | ~Matcher() {} |
686 | | |
687 | | /** (INTERNAL) Check if there is a match of a short flag |
688 | | */ |
689 | | bool Match(const char flag) const |
690 | 31.9k | { |
691 | 31.9k | return shortFlags.find(flag) != shortFlags.end(); |
692 | 31.9k | } |
693 | | |
694 | | /** (INTERNAL) Check if there is a match of a long flag |
695 | | */ |
696 | | bool Match(const std::string &flag) const |
697 | 1.44k | { |
698 | 1.44k | return longFlags.find(flag) != longFlags.end(); |
699 | 1.44k | } |
700 | | |
701 | | /** (INTERNAL) Check if there is a match of a flag |
702 | | */ |
703 | | bool Match(const EitherFlag &flag) const |
704 | 33.4k | { |
705 | 33.4k | return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag); |
706 | 33.4k | } |
707 | | |
708 | | /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded |
709 | | */ |
710 | | std::vector<EitherFlag> GetFlagStrings() const |
711 | 5.83k | { |
712 | 5.83k | std::vector<EitherFlag> flagStrings; |
713 | 5.83k | flagStrings.reserve(shortFlags.size() + longFlags.size()); |
714 | 5.83k | for (const char flag: shortFlags) |
715 | 5.83k | { |
716 | 5.83k | flagStrings.emplace_back(flag); |
717 | 5.83k | } |
718 | 5.83k | for (const std::string &flag: longFlags) |
719 | 5.83k | { |
720 | 5.83k | flagStrings.emplace_back(flag); |
721 | 5.83k | } |
722 | 5.83k | return flagStrings; |
723 | 5.83k | } |
724 | | |
725 | | /** (INTERNAL) Get long flag if it exists or any short flag |
726 | | */ |
727 | | EitherFlag GetLongOrAny() const |
728 | 0 | { |
729 | 0 | if (!longFlags.empty()) |
730 | 0 | { |
731 | 0 | return *longFlags.begin(); |
732 | 0 | } |
733 | | |
734 | 0 | if (!shortFlags.empty()) |
735 | 0 | { |
736 | 0 | return *shortFlags.begin(); |
737 | 0 | } |
738 | | |
739 | | // should be unreachable |
740 | 0 | return ' '; |
741 | 0 | } |
742 | | |
743 | | /** (INTERNAL) Get short flag if it exists or any long flag |
744 | | */ |
745 | | EitherFlag GetShortOrAny() const |
746 | 0 | { |
747 | 0 | if (!shortFlags.empty()) |
748 | 0 | { |
749 | 0 | return *shortFlags.begin(); |
750 | 0 | } |
751 | | |
752 | 0 | if (!longFlags.empty()) |
753 | 0 | { |
754 | 0 | return *longFlags.begin(); |
755 | 0 | } |
756 | | |
757 | | // should be unreachable |
758 | 0 | return ' '; |
759 | 0 | } |
760 | | }; |
761 | | |
762 | | /** Attributes for flags. |
763 | | */ |
764 | | enum class Options |
765 | | { |
766 | | /** Default options. |
767 | | */ |
768 | | None = 0x0, |
769 | | |
770 | | /** Flag can't be passed multiple times. |
771 | | */ |
772 | | Single = 0x01, |
773 | | |
774 | | /** Flag can't be omitted. |
775 | | */ |
776 | | Required = 0x02, |
777 | | |
778 | | /** Flag is excluded from usage line. |
779 | | */ |
780 | | HiddenFromUsage = 0x04, |
781 | | |
782 | | /** Flag is excluded from options help. |
783 | | */ |
784 | | HiddenFromDescription = 0x08, |
785 | | |
786 | | /** Flag is global and can be used in any subcommand. |
787 | | */ |
788 | | Global = 0x10, |
789 | | |
790 | | /** Flag stops a parser. |
791 | | */ |
792 | | KickOut = 0x20, |
793 | | |
794 | | /** Flag is excluded from auto completion. |
795 | | */ |
796 | | HiddenFromCompletion = 0x40, |
797 | | |
798 | | /** Flag is excluded from options help and usage line |
799 | | */ |
800 | | Hidden = HiddenFromUsage | HiddenFromDescription | HiddenFromCompletion, |
801 | | }; |
802 | | |
803 | | inline Options operator | (Options lhs, Options rhs) |
804 | 0 | { |
805 | 0 | return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs)); |
806 | 0 | } |
807 | | |
808 | | inline Options operator & (Options lhs, Options rhs) |
809 | 22.1k | { |
810 | 22.1k | return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs)); |
811 | 22.1k | } |
812 | | |
813 | | class FlagBase; |
814 | | class PositionalBase; |
815 | | class Command; |
816 | | class ArgumentParser; |
817 | | |
818 | | /** A simple structure of parameters for easy user-modifyable help menus |
819 | | */ |
820 | | struct HelpParams |
821 | | { |
822 | | /** The width of the help menu |
823 | | */ |
824 | | unsigned int width = 80; |
825 | | /** The indent of the program line |
826 | | */ |
827 | | unsigned int progindent = 2; |
828 | | /** The indent of the program trailing lines for long parameters |
829 | | */ |
830 | | unsigned int progtailindent = 4; |
831 | | /** The indent of the description and epilogs |
832 | | */ |
833 | | unsigned int descriptionindent = 4; |
834 | | /** The indent of the flags |
835 | | */ |
836 | | unsigned int flagindent = 6; |
837 | | /** The indent of the flag descriptions |
838 | | */ |
839 | | unsigned int helpindent = 40; |
840 | | /** The additional indent each group adds |
841 | | */ |
842 | | unsigned int eachgroupindent = 2; |
843 | | |
844 | | /** The minimum gutter between each flag and its help |
845 | | */ |
846 | | unsigned int gutter = 1; |
847 | | |
848 | | /** Show the terminator when both options and positional parameters are present |
849 | | */ |
850 | | bool showTerminator = true; |
851 | | |
852 | | /** Show the {OPTIONS} on the prog line when this is true |
853 | | */ |
854 | | bool showProglineOptions = true; |
855 | | |
856 | | /** Show the positionals on the prog line when this is true |
857 | | */ |
858 | | bool showProglinePositionals = true; |
859 | | |
860 | | /** The prefix for short flags |
861 | | */ |
862 | | std::string shortPrefix; |
863 | | |
864 | | /** The prefix for long flags |
865 | | */ |
866 | | std::string longPrefix; |
867 | | |
868 | | /** The separator for short flags |
869 | | */ |
870 | | std::string shortSeparator; |
871 | | |
872 | | /** The separator for long flags |
873 | | */ |
874 | | std::string longSeparator; |
875 | | |
876 | | /** The program name for help generation |
877 | | */ |
878 | | std::string programName; |
879 | | |
880 | | /** Show command's flags |
881 | | */ |
882 | | bool showCommandChildren = false; |
883 | | |
884 | | /** Show command's descriptions and epilog |
885 | | */ |
886 | | bool showCommandFullHelp = false; |
887 | | |
888 | | /** The postfix for progline when showProglineOptions is true and command has any flags |
889 | | */ |
890 | | std::string proglineOptions = "{OPTIONS}"; |
891 | | |
892 | | /** The prefix for progline when command has any subcommands |
893 | | */ |
894 | | std::string proglineCommand = "COMMAND"; |
895 | | |
896 | | /** The prefix for progline value |
897 | | */ |
898 | | std::string proglineValueOpen = " <"; |
899 | | |
900 | | /** The postfix for progline value |
901 | | */ |
902 | | std::string proglineValueClose = ">"; |
903 | | |
904 | | /** The prefix for progline required argument |
905 | | */ |
906 | | std::string proglineRequiredOpen = ""; |
907 | | |
908 | | /** The postfix for progline required argument |
909 | | */ |
910 | | std::string proglineRequiredClose = ""; |
911 | | |
912 | | /** The prefix for progline non-required argument |
913 | | */ |
914 | | std::string proglineNonrequiredOpen = "["; |
915 | | |
916 | | /** The postfix for progline non-required argument |
917 | | */ |
918 | | std::string proglineNonrequiredClose = "]"; |
919 | | |
920 | | /** Show flags in program line |
921 | | */ |
922 | | bool proglineShowFlags = false; |
923 | | |
924 | | /** Use short flags in program lines when possible |
925 | | */ |
926 | | bool proglinePreferShortFlags = false; |
927 | | |
928 | | /** Program line prefix |
929 | | */ |
930 | | std::string usageString; |
931 | | |
932 | | /** String shown in help before flags descriptions |
933 | | */ |
934 | | std::string optionsString = "OPTIONS:"; |
935 | | |
936 | | /** Display value name after all the long and short flags |
937 | | */ |
938 | | bool useValueNameOnce = false; |
939 | | |
940 | | /** Show value name |
941 | | */ |
942 | | bool showValueName = true; |
943 | | |
944 | | /** Add newline before flag description |
945 | | */ |
946 | | bool addNewlineBeforeDescription = false; |
947 | | |
948 | | /** The prefix for option value |
949 | | */ |
950 | | std::string valueOpen = "["; |
951 | | |
952 | | /** The postfix for option value |
953 | | */ |
954 | | std::string valueClose = "]"; |
955 | | |
956 | | /** Add choices to argument description |
957 | | */ |
958 | | bool addChoices = false; |
959 | | |
960 | | /** The prefix for choices |
961 | | */ |
962 | | std::string choiceString = "\nOne of: "; |
963 | | |
964 | | /** Add default values to argument description |
965 | | */ |
966 | | bool addDefault = false; |
967 | | |
968 | | /** The prefix for default values |
969 | | */ |
970 | | std::string defaultString = "\nDefault: "; |
971 | | }; |
972 | | |
973 | | /** A number of arguments which can be consumed by an option. |
974 | | * |
975 | | * Represents a closed interval [min, max]. |
976 | | */ |
977 | | struct Nargs |
978 | | { |
979 | | const size_t min; |
980 | | const size_t max; |
981 | | |
982 | | Nargs(size_t min_, size_t max_) : min{min_}, max{max_} |
983 | 0 | { |
984 | 0 | #ifndef ARGS_NOEXCEPT |
985 | 0 | if (max < min) |
986 | 0 | { |
987 | 0 | throw UsageError("Nargs: max < min"); |
988 | 0 | } |
989 | 0 | #endif |
990 | 0 | } |
991 | | |
992 | 11.0k | Nargs(size_t num_) : min{num_}, max{num_} |
993 | 11.0k | { |
994 | 11.0k | } |
995 | | |
996 | | friend bool operator == (const Nargs &lhs, const Nargs &rhs) |
997 | 0 | { |
998 | 0 | return lhs.min == rhs.min && lhs.max == rhs.max; |
999 | 0 | } |
1000 | | |
1001 | | friend bool operator != (const Nargs &lhs, const Nargs &rhs) |
1002 | 0 | { |
1003 | 0 | return !(lhs == rhs); |
1004 | 0 | } |
1005 | | }; |
1006 | | |
1007 | | /** Base class for all match types |
1008 | | */ |
1009 | | class Base |
1010 | | { |
1011 | | private: |
1012 | | Options options = {}; |
1013 | | |
1014 | | protected: |
1015 | | bool matched = false; |
1016 | | const std::string help; |
1017 | | #ifdef ARGS_NOEXCEPT |
1018 | | /// Only for ARGS_NOEXCEPT |
1019 | | mutable Error error = Error::None; |
1020 | | mutable std::string errorMsg; |
1021 | | #endif |
1022 | | |
1023 | | public: |
1024 | 3.88k | Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {} |
1025 | 3.88k | virtual ~Base() {} |
1026 | | |
1027 | | Options GetOptions() const noexcept |
1028 | 11.0k | { |
1029 | 11.0k | return options; |
1030 | 11.0k | } |
1031 | | |
1032 | | bool IsRequired() const noexcept |
1033 | 33 | { |
1034 | 33 | return (GetOptions() & Options::Required) != Options::None; |
1035 | 33 | } |
1036 | | |
1037 | | virtual bool Matched() const noexcept |
1038 | 13.0k | { |
1039 | 13.0k | return matched; |
1040 | 13.0k | } |
1041 | | |
1042 | | virtual void Validate(const std::string &, const std::string &) const |
1043 | 0 | { |
1044 | 0 | } |
1045 | | |
1046 | | operator bool() const noexcept |
1047 | 0 | { |
1048 | 0 | return Matched(); |
1049 | 0 | } |
1050 | | |
1051 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const |
1052 | 0 | { |
1053 | 0 | std::tuple<std::string, std::string, unsigned> description; |
1054 | 0 | std::get<1>(description) = help; |
1055 | 0 | std::get<2>(description) = indentLevel; |
1056 | 0 | return { std::move(description) }; |
1057 | 0 | } |
1058 | | |
1059 | | virtual std::vector<Command*> GetCommands() |
1060 | 2.91k | { |
1061 | 2.91k | return {}; |
1062 | 2.91k | } |
1063 | | |
1064 | | virtual bool IsGroup() const |
1065 | 66 | { |
1066 | 66 | return false; |
1067 | 66 | } |
1068 | | |
1069 | | virtual bool IsFlag() const |
1070 | 0 | { |
1071 | 0 | return false; |
1072 | 0 | } |
1073 | | |
1074 | | virtual FlagBase *Match(const EitherFlag &) |
1075 | 0 | { |
1076 | 0 | return nullptr; |
1077 | 0 | } |
1078 | | |
1079 | | virtual PositionalBase *GetNextPositional() |
1080 | 741 | { |
1081 | 741 | return nullptr; |
1082 | 741 | } |
1083 | | |
1084 | | virtual std::vector<FlagBase*> GetAllFlags() |
1085 | 0 | { |
1086 | 0 | return {}; |
1087 | 0 | } |
1088 | | |
1089 | | virtual bool HasFlag() const |
1090 | 0 | { |
1091 | 0 | return false; |
1092 | 0 | } |
1093 | | |
1094 | | virtual bool HasPositional() const |
1095 | 0 | { |
1096 | 0 | return false; |
1097 | 0 | } |
1098 | | |
1099 | | virtual bool HasCommand() const |
1100 | 66 | { |
1101 | 66 | return false; |
1102 | 66 | } |
1103 | | |
1104 | | virtual std::vector<std::string> GetProgramLine(const HelpParams &) const |
1105 | 0 | { |
1106 | 0 | return {}; |
1107 | 0 | } |
1108 | | |
1109 | | /// Sets a kick-out value for building subparsers |
1110 | | void KickOut(bool kickout_) noexcept |
1111 | 0 | { |
1112 | 0 | if (kickout_) |
1113 | 0 | { |
1114 | 0 | options = options | Options::KickOut; |
1115 | 0 | } |
1116 | 0 | else |
1117 | 0 | { |
1118 | 0 | options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut)); |
1119 | 0 | } |
1120 | 0 | } |
1121 | | |
1122 | | /// Gets the kick-out value for building subparsers |
1123 | | bool KickOut() const noexcept |
1124 | 11.0k | { |
1125 | 11.0k | return (options & Options::KickOut) != Options::None; |
1126 | 11.0k | } |
1127 | | |
1128 | | virtual void Reset() noexcept |
1129 | 3.88k | { |
1130 | 3.88k | matched = false; |
1131 | | #ifdef ARGS_NOEXCEPT |
1132 | | error = Error::None; |
1133 | | errorMsg.clear(); |
1134 | | #endif |
1135 | 3.88k | } |
1136 | | |
1137 | | #ifdef ARGS_NOEXCEPT |
1138 | | /// Only for ARGS_NOEXCEPT |
1139 | | virtual Error GetError() const |
1140 | | { |
1141 | | return error; |
1142 | | } |
1143 | | |
1144 | | /// Only for ARGS_NOEXCEPT |
1145 | | virtual std::string GetErrorMsg() const |
1146 | | { |
1147 | | return errorMsg; |
1148 | | } |
1149 | | #endif |
1150 | | }; |
1151 | | |
1152 | | /** Base class for all match types that have a name |
1153 | | */ |
1154 | | class NamedBase : public Base |
1155 | | { |
1156 | | protected: |
1157 | | const std::string name; |
1158 | | bool kickout = false; |
1159 | | std::string defaultString; |
1160 | | bool defaultStringManual = false; |
1161 | | std::vector<std::string> choicesStrings; |
1162 | | bool choicesStringManual = false; |
1163 | | |
1164 | 0 | virtual std::string GetDefaultString(const HelpParams&) const { return {}; } |
1165 | | |
1166 | 0 | virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; } |
1167 | | |
1168 | 0 | virtual std::string GetNameString(const HelpParams&) const { return Name(); } |
1169 | | |
1170 | | void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const |
1171 | 0 | { |
1172 | 0 | if (isManual && !manual.empty()) |
1173 | 0 | { |
1174 | 0 | dest += str; |
1175 | 0 | dest += manual; |
1176 | 0 | } |
1177 | 0 | else if (!isManual && isGenerated && !generated.empty()) |
1178 | 0 | { |
1179 | 0 | dest += str; |
1180 | 0 | dest += generated; |
1181 | 0 | } |
1182 | 0 | } |
1183 | | |
1184 | | public: |
1185 | 2.91k | NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {} |
1186 | 2.91k | virtual ~NamedBase() {} |
1187 | | |
1188 | | /** Sets default value string that will be added to argument description. |
1189 | | * Use empty string to disable it for this argument. |
1190 | | */ |
1191 | | void HelpDefault(const std::string &str) |
1192 | 0 | { |
1193 | 0 | defaultStringManual = true; |
1194 | 0 | defaultString = str; |
1195 | 0 | } |
1196 | | |
1197 | | /** Gets default value string that will be added to argument description. |
1198 | | */ |
1199 | | std::string HelpDefault(const HelpParams ¶ms) const |
1200 | 0 | { |
1201 | 0 | return defaultStringManual ? defaultString : GetDefaultString(params); |
1202 | 0 | } |
1203 | | |
1204 | | /** Sets choices strings that will be added to argument description. |
1205 | | * Use empty vector to disable it for this argument. |
1206 | | */ |
1207 | | void HelpChoices(const std::vector<std::string> &array) |
1208 | 0 | { |
1209 | 0 | choicesStringManual = true; |
1210 | 0 | choicesStrings = array; |
1211 | 0 | } |
1212 | | |
1213 | | /** Gets choices strings that will be added to argument description. |
1214 | | */ |
1215 | | std::vector<std::string> HelpChoices(const HelpParams ¶ms) const |
1216 | 0 | { |
1217 | 0 | return choicesStringManual ? choicesStrings : GetChoicesStrings(params); |
1218 | 0 | } |
1219 | | |
1220 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned indentLevel) const override |
1221 | 0 | { |
1222 | 0 | std::tuple<std::string, std::string, unsigned> description; |
1223 | 0 | std::get<0>(description) = GetNameString(params); |
1224 | 0 | std::get<1>(description) = help; |
1225 | 0 | std::get<2>(description) = indentLevel; |
1226 | |
|
1227 | 0 | AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString); |
1228 | 0 | AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString); |
1229 | |
|
1230 | 0 | return { std::move(description) }; |
1231 | 0 | } |
1232 | | |
1233 | | virtual std::string Name() const |
1234 | 18 | { |
1235 | 18 | return name; |
1236 | 18 | } |
1237 | | }; |
1238 | | |
1239 | | namespace detail |
1240 | | { |
1241 | | template<typename T> |
1242 | | using vector = std::vector<T, std::allocator<T>>; |
1243 | | |
1244 | | template<typename K, typename T> |
1245 | | using unordered_map = std::unordered_map<K, T, std::hash<K>, |
1246 | | std::equal_to<K>, std::allocator<std::pair<const K, T> > >; |
1247 | | |
1248 | | template<typename S, typename T> |
1249 | | class is_streamable |
1250 | | { |
1251 | | template<typename SS, typename TT> |
1252 | | static auto test(int) |
1253 | | -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() ); |
1254 | | |
1255 | | template<typename, typename> |
1256 | | static auto test(...) -> std::false_type; |
1257 | | |
1258 | | public: |
1259 | | using type = decltype(test<S,T>(0)); |
1260 | | }; |
1261 | | |
1262 | | template <typename T> |
1263 | | using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type; |
1264 | | |
1265 | | template <typename T> |
1266 | | typename std::enable_if<IsConvertableToString<T>::value, std::string>::type |
1267 | | ToString(const T &value) |
1268 | | { |
1269 | | std::ostringstream s; |
1270 | | s << value; |
1271 | | return s.str(); |
1272 | | } |
1273 | | |
1274 | | template <typename T> |
1275 | | typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type |
1276 | | ToString(const T &) |
1277 | | { |
1278 | | return {}; |
1279 | | } |
1280 | | |
1281 | | // Keep emplace_back for sequences: insert can require move assignment |
1282 | | // even when appending a copyable value at the end of a vector. |
1283 | | template <typename Container, typename T> |
1284 | | auto AppendMappedValue(Container &container, const T &value, int) |
1285 | | -> decltype(container.emplace_back(value), void()) |
1286 | | { |
1287 | | container.emplace_back(value); |
1288 | | } |
1289 | | |
1290 | | template <typename Container, typename T> |
1291 | | auto AppendMappedValue(Container &container, const T &value, long) |
1292 | | -> decltype(container.insert(std::end(container), value), void()) |
1293 | | { |
1294 | | container.insert(std::end(container), value); |
1295 | | } |
1296 | | |
1297 | | template <typename T> |
1298 | | std::vector<std::string> MapKeysToStrings(const T &map) |
1299 | | { |
1300 | | std::vector<std::string> res; |
1301 | | using K = typename std::decay<decltype(std::begin(map)->first)>::type; |
1302 | | if (IsConvertableToString<K>::value) |
1303 | | { |
1304 | | for (const auto &p : map) |
1305 | | { |
1306 | | res.push_back(detail::ToString(p.first)); |
1307 | | } |
1308 | | |
1309 | | std::sort(res.begin(), res.end()); |
1310 | | } |
1311 | | return res; |
1312 | | } |
1313 | | } |
1314 | | |
1315 | | /** Base class for all flag options |
1316 | | */ |
1317 | | class FlagBase : public NamedBase |
1318 | | { |
1319 | | protected: |
1320 | | const Matcher matcher; |
1321 | | |
1322 | | virtual std::string GetNameString(const HelpParams ¶ms) const override |
1323 | 0 | { |
1324 | 0 | const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name(); |
1325 | 0 | std::string flags; |
1326 | 0 | const auto flagStrings = matcher.GetFlagStrings(); |
1327 | 0 | const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce; |
1328 | 0 | for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it) |
1329 | 0 | { |
1330 | 0 | auto &flag = *it; |
1331 | 0 | if (it != flagStrings.begin()) |
1332 | 0 | { |
1333 | 0 | flags += ", "; |
1334 | 0 | } |
1335 | |
|
1336 | 0 | flags += flag.isShort ? params.shortPrefix : params.longPrefix; |
1337 | 0 | flags += flag.str(); |
1338 | |
|
1339 | 0 | if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) |
1340 | 0 | { |
1341 | 0 | flags += flag.isShort ? params.shortSeparator : params.longSeparator; |
1342 | 0 | flags += params.valueOpen + postfix + params.valueClose; |
1343 | 0 | } |
1344 | 0 | } |
1345 | |
|
1346 | 0 | return flags; |
1347 | 0 | } |
1348 | | |
1349 | | public: |
1350 | 0 | FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {} |
1351 | | |
1352 | 2.91k | FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {} |
1353 | | |
1354 | 2.91k | virtual ~FlagBase() {} |
1355 | | |
1356 | | virtual bool IsFlag() const override |
1357 | 2.91k | { |
1358 | 2.91k | return true; |
1359 | 2.91k | } |
1360 | | |
1361 | | virtual FlagBase *Match(const EitherFlag &flag) override |
1362 | 33.4k | { |
1363 | 33.4k | if (matcher.Match(flag)) |
1364 | 11.0k | { |
1365 | 11.0k | if ((GetOptions() & Options::Single) != Options::None && matched) |
1366 | 0 | { |
1367 | 0 | std::ostringstream problem; |
1368 | 0 | problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once"; |
1369 | | #ifdef ARGS_NOEXCEPT |
1370 | | error = Error::Extra; |
1371 | | errorMsg = problem.str(); |
1372 | | #else |
1373 | 0 | throw ExtraError(problem.str()); |
1374 | 0 | #endif |
1375 | 0 | } |
1376 | 11.0k | matched = true; |
1377 | 11.0k | return this; |
1378 | 11.0k | } |
1379 | 22.3k | return nullptr; |
1380 | 33.4k | } |
1381 | | |
1382 | | virtual std::vector<FlagBase*> GetAllFlags() override |
1383 | 0 | { |
1384 | 0 | return { this }; |
1385 | 0 | } |
1386 | | |
1387 | | const Matcher &GetMatcher() const |
1388 | 5.83k | { |
1389 | 5.83k | return matcher; |
1390 | 5.83k | } |
1391 | | |
1392 | | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override |
1393 | 66 | { |
1394 | 66 | if (!Matched() && IsRequired()) |
1395 | 0 | { |
1396 | 0 | std::ostringstream problem; |
1397 | 0 | problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required"; |
1398 | | #ifdef ARGS_NOEXCEPT |
1399 | | error = Error::Required; |
1400 | | errorMsg = problem.str(); |
1401 | | #else |
1402 | 0 | throw RequiredError(problem.str()); |
1403 | 0 | #endif |
1404 | 0 | } |
1405 | 66 | } |
1406 | | |
1407 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1408 | 0 | { |
1409 | 0 | if (!params.proglineShowFlags) |
1410 | 0 | { |
1411 | 0 | return {}; |
1412 | 0 | } |
1413 | | |
1414 | 0 | const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name(); |
1415 | 0 | const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny(); |
1416 | 0 | std::string res = flag.str(params.shortPrefix, params.longPrefix); |
1417 | 0 | if (!postfix.empty()) |
1418 | 0 | { |
1419 | 0 | res += params.proglineValueOpen + postfix + params.proglineValueClose; |
1420 | 0 | } |
1421 | |
|
1422 | 0 | return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose |
1423 | 0 | : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose }; |
1424 | 0 | } |
1425 | | |
1426 | | virtual bool HasFlag() const override |
1427 | 0 | { |
1428 | 0 | return true; |
1429 | 0 | } |
1430 | | |
1431 | | #ifdef ARGS_NOEXCEPT |
1432 | | /// Only for ARGS_NOEXCEPT |
1433 | | bool usageError = false; |
1434 | | void SetUsageError() |
1435 | | { |
1436 | | usageError = true; |
1437 | | } |
1438 | | void ClearUsageError() |
1439 | | { |
1440 | | usageError = false; |
1441 | | } |
1442 | | virtual Error GetError() const override |
1443 | | { |
1444 | | if(usageError) |
1445 | | { |
1446 | | return Error::Usage; |
1447 | | } |
1448 | | const auto nargs = NumberOfArguments(); |
1449 | | if (nargs.min > nargs.max) |
1450 | | { |
1451 | | return Error::Usage; |
1452 | | } |
1453 | | |
1454 | | const auto matcherError = matcher.GetError(); |
1455 | | if (matcherError != Error::None) |
1456 | | { |
1457 | | return matcherError; |
1458 | | } |
1459 | | |
1460 | | return error; |
1461 | | } |
1462 | | #endif |
1463 | | |
1464 | | /** Defines how many values can be consumed by this option. |
1465 | | * |
1466 | | * \return closed interval [min, max] |
1467 | | */ |
1468 | | virtual Nargs NumberOfArguments() const noexcept = 0; |
1469 | | |
1470 | | /** Parse values of this option. |
1471 | | * |
1472 | | * \param value Vector of values. It's size must be in NumberOfArguments() interval. |
1473 | | */ |
1474 | | virtual void ParseValue(const std::vector<std::string> &value) = 0; |
1475 | | }; |
1476 | | |
1477 | | /** Base class for value-accepting flag options |
1478 | | */ |
1479 | | class ValueFlagBase : public FlagBase |
1480 | | { |
1481 | | public: |
1482 | 0 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {} |
1483 | 0 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {} |
1484 | 0 | virtual ~ValueFlagBase() {} |
1485 | | |
1486 | | virtual Nargs NumberOfArguments() const noexcept override |
1487 | 0 | { |
1488 | 0 | return 1; |
1489 | 0 | } |
1490 | | }; |
1491 | | |
1492 | | class CompletionFlag : public ValueFlagBase |
1493 | | { |
1494 | | public: |
1495 | | std::vector<std::string> reply; |
1496 | | size_t cword = 0; |
1497 | | std::string syntax; |
1498 | | |
1499 | | template <typename GroupClass> |
1500 | | CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden) |
1501 | | { |
1502 | | group_.AddCompletion(*this); |
1503 | | } |
1504 | | |
1505 | 0 | virtual ~CompletionFlag() {} |
1506 | | |
1507 | | virtual Nargs NumberOfArguments() const noexcept override |
1508 | 0 | { |
1509 | 0 | return 2; |
1510 | 0 | } |
1511 | | |
1512 | | virtual void ParseValue(const std::vector<std::string> &value_) override |
1513 | 0 | { |
1514 | 0 | syntax = value_.at(0); |
1515 | 0 | const std::string &raw = value_.at(1); |
1516 | 0 | bool failed = false; |
1517 | 0 |
|
1518 | 0 | const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c) |
1519 | 0 | { |
1520 | 0 | return std::isspace(static_cast<unsigned char>(c)) != 0; |
1521 | 0 | }); |
1522 | 0 |
|
1523 | 0 | // Reject explicit signs: cword must be a plain non-negative |
1524 | 0 | // decimal index. istringstream would otherwise silently |
1525 | 0 | // accept "+1". |
1526 | 0 | if (firstNonSpace != raw.end() && (*firstNonSpace == '-' || *firstNonSpace == '+')) |
1527 | 0 | { |
1528 | 0 | failed = true; |
1529 | 0 | } |
1530 | 0 |
|
1531 | 0 | size_t parsed = 0; |
1532 | 0 | if (!failed) |
1533 | 0 | { |
1534 | 0 | std::istringstream ss(raw); |
1535 | 0 | // Use the C locale so that the cword index parses |
1536 | 0 | // consistently regardless of any std::locale::global call |
1537 | 0 | // elsewhere in the process. A locale with a non-empty |
1538 | 0 | // grouping facet would otherwise reject digit-only inputs |
1539 | 0 | // like "12" when grouping rules expect separators. |
1540 | 0 | ss.imbue(std::locale::classic()); |
1541 | 0 | ss >> parsed; |
1542 | 0 | if (ss.fail()) |
1543 | 0 | { |
1544 | 0 | failed = true; |
1545 | 0 | } |
1546 | 0 | else |
1547 | 0 | { |
1548 | 0 | char extra; |
1549 | 0 | if (ss >> extra) |
1550 | 0 | { |
1551 | 0 | failed = true; |
1552 | 0 | } |
1553 | 0 | else if (!ss.eof()) |
1554 | 0 | { |
1555 | 0 | failed = true; |
1556 | 0 | } |
1557 | 0 | } |
1558 | 0 | } |
1559 | 0 |
|
1560 | 0 | if (failed) |
1561 | 0 | { |
1562 | 0 | #ifdef ARGS_NOEXCEPT |
1563 | 0 | error = Error::Parse; |
1564 | 0 | errorMsg = "Argument 'completion' received invalid value type '" + raw + "'"; |
1565 | 0 | #else |
1566 | 0 | std::ostringstream problem; |
1567 | 0 | problem << "Argument 'completion' received invalid value type '" << raw << "'"; |
1568 | 0 | throw ParseError(problem.str()); |
1569 | 0 | #endif |
1570 | 0 | return; |
1571 | 0 | } |
1572 | 0 |
|
1573 | 0 | cword = parsed; |
1574 | 0 | } |
1575 | | |
1576 | | /** Get the completion reply |
1577 | | */ |
1578 | | std::string Get() noexcept |
1579 | 0 | { |
1580 | 0 | return detail::Join(reply, "\n"); |
1581 | 0 | } |
1582 | | |
1583 | | virtual void Reset() noexcept override |
1584 | 0 | { |
1585 | 0 | ValueFlagBase::Reset(); |
1586 | 0 | cword = 0; |
1587 | 0 | syntax.clear(); |
1588 | 0 | reply.clear(); |
1589 | 0 | } |
1590 | | }; |
1591 | | |
1592 | | |
1593 | | /** Base class for positional options |
1594 | | */ |
1595 | | class PositionalBase : public NamedBase |
1596 | | { |
1597 | | protected: |
1598 | | bool ready; |
1599 | | |
1600 | | public: |
1601 | 0 | PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {} |
1602 | 0 | virtual ~PositionalBase() {} |
1603 | | |
1604 | | bool Ready() |
1605 | 0 | { |
1606 | 0 | return ready; |
1607 | 0 | } |
1608 | | |
1609 | | virtual void ParseValue(const std::string &value_) = 0; |
1610 | | |
1611 | | virtual void Reset() noexcept override |
1612 | 0 | { |
1613 | 0 | matched = false; |
1614 | 0 | ready = true; |
1615 | 0 | #ifdef ARGS_NOEXCEPT |
1616 | 0 | error = Error::None; |
1617 | 0 | errorMsg.clear(); |
1618 | 0 | #endif |
1619 | 0 | } |
1620 | | |
1621 | | virtual PositionalBase *GetNextPositional() override |
1622 | 0 | { |
1623 | 0 | return Ready() ? this : nullptr; |
1624 | 0 | } |
1625 | | |
1626 | | virtual bool HasPositional() const override |
1627 | 0 | { |
1628 | 0 | return true; |
1629 | 0 | } |
1630 | | |
1631 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1632 | 0 | { |
1633 | 0 | return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose |
1634 | 0 | : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose }; |
1635 | 0 | } |
1636 | | |
1637 | | virtual void Validate(const std::string &, const std::string &) const override |
1638 | 0 | { |
1639 | 0 | if (IsRequired() && !Matched()) |
1640 | 0 | { |
1641 | 0 | std::ostringstream problem; |
1642 | 0 | problem << "Option '" << Name() << "' is required"; |
1643 | 0 | #ifdef ARGS_NOEXCEPT |
1644 | 0 | error = Error::Required; |
1645 | 0 | errorMsg = problem.str(); |
1646 | 0 | #else |
1647 | 0 | throw RequiredError(problem.str()); |
1648 | 0 | #endif |
1649 | 0 | } |
1650 | 0 | } |
1651 | | }; |
1652 | | |
1653 | | /** Class for all kinds of validating groups, including ArgumentParser |
1654 | | */ |
1655 | | class Group : public Base |
1656 | | { |
1657 | | private: |
1658 | | Group* parent; |
1659 | | std::vector<Base*> children; |
1660 | | std::function<bool(const Group &)> validator; |
1661 | | |
1662 | | public: |
1663 | | /** Default validators |
1664 | | */ |
1665 | | struct Validators |
1666 | | { |
1667 | | static bool Xor(const Group &group) |
1668 | 0 | { |
1669 | 0 | return group.MatchedChildren() == 1; |
1670 | 0 | } |
1671 | | |
1672 | | static bool AtLeastOne(const Group &group) |
1673 | 0 | { |
1674 | 0 | return group.MatchedChildren() >= 1; |
1675 | 0 | } |
1676 | | |
1677 | | static bool AtMostOne(const Group &group) |
1678 | 0 | { |
1679 | 0 | return group.MatchedChildren() <= 1; |
1680 | 0 | } |
1681 | | |
1682 | | static bool All(const Group &group) |
1683 | 0 | { |
1684 | 0 | return group.Children().size() == group.MatchedChildren(); |
1685 | 0 | } |
1686 | | |
1687 | | static bool AllOrNone(const Group &group) |
1688 | 0 | { |
1689 | 0 | return (All(group) || None(group)); |
1690 | 0 | } |
1691 | | |
1692 | | static bool AllChildGroups(const Group &group) |
1693 | 0 | { |
1694 | 0 | return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool { |
1695 | 0 | return child->IsGroup() && !child->Matched(); |
1696 | 0 | }); |
1697 | 0 | } |
1698 | | |
1699 | | static bool DontCare(const Group &) |
1700 | 0 | { |
1701 | 0 | return true; |
1702 | 0 | } |
1703 | | |
1704 | | static bool CareTooMuch(const Group &) |
1705 | 0 | { |
1706 | 0 | return false; |
1707 | 0 | } |
1708 | | |
1709 | | static bool None(const Group &group) |
1710 | 0 | { |
1711 | 0 | return group.MatchedChildren() == 0; |
1712 | 0 | } |
1713 | | }; |
1714 | | /// If help is empty, this group will not be printed in help output |
1715 | 972 | Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) |
1716 | 972 | { |
1717 | 972 | parent = nullptr; |
1718 | 972 | } |
1719 | | /// If help is empty, this group will not be printed in help output |
1720 | | Group(Group &group_, const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) |
1721 | 0 | { |
1722 | 0 | group_.Add(*this); |
1723 | 0 | parent = &group_; |
1724 | 0 | } |
1725 | 972 | virtual ~Group() {} |
1726 | | |
1727 | | /** Append a child to this Group. |
1728 | | */ |
1729 | | void Add(Base &child) |
1730 | 2.91k | { |
1731 | 2.91k | children.emplace_back(&child); |
1732 | | |
1733 | 2.91k | if(child.IsFlag()) { |
1734 | 2.91k | #ifndef ARGS_NOEXCEPT |
1735 | | // Detection runs from the child's own constructor, so a |
1736 | | // duplicate throws before that constructor completes and the |
1737 | | // child's storage is released while the stack unwinds. Undo |
1738 | | // the registration first, or a caller that catches the error |
1739 | | // leaves this group holding a pointer to a dead object. |
1740 | 2.91k | try |
1741 | 2.91k | { |
1742 | 2.91k | SignalDetectDuplicates(); |
1743 | 2.91k | } |
1744 | 2.91k | catch (...) |
1745 | 2.91k | { |
1746 | 0 | children.pop_back(); |
1747 | 0 | throw; |
1748 | 0 | } |
1749 | | #else |
1750 | | SignalDetectDuplicates(); |
1751 | | #endif |
1752 | 2.91k | } |
1753 | 2.91k | } |
1754 | | |
1755 | | /** Get all this group's children |
1756 | | */ |
1757 | | const std::vector<Base *> &Children() const |
1758 | 16.9k | { |
1759 | 16.9k | return children; |
1760 | 16.9k | } |
1761 | | |
1762 | | /** Return the first FlagBase that matches flag, or nullptr |
1763 | | * |
1764 | | * \param flag The flag with prefixes stripped |
1765 | | * \return the first matching FlagBase pointer, or nullptr if there is no match |
1766 | | */ |
1767 | | virtual FlagBase *Match(const EitherFlag &flag) override |
1768 | 11.7k | { |
1769 | 11.7k | for (Base *child: Children()) |
1770 | 33.4k | { |
1771 | 33.4k | if (FlagBase *match = child->Match(flag)) |
1772 | 11.0k | { |
1773 | 11.0k | return match; |
1774 | 11.0k | } |
1775 | 33.4k | } |
1776 | 666 | return nullptr; |
1777 | 11.7k | } |
1778 | | |
1779 | | virtual std::vector<FlagBase*> GetAllFlags() override |
1780 | 0 | { |
1781 | 0 | std::vector<FlagBase*> res; |
1782 | 0 | for (Base *child: Children()) |
1783 | 0 | { |
1784 | 0 | auto childRes = child->GetAllFlags(); |
1785 | 0 | res.insert(res.end(), childRes.begin(), childRes.end()); |
1786 | 0 | } |
1787 | 0 | return res; |
1788 | 0 | } |
1789 | | |
1790 | | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override |
1791 | 0 | { |
1792 | 0 | for (Base *child: Children()) |
1793 | 0 | { |
1794 | 0 | child->Validate(shortPrefix, longPrefix); |
1795 | 0 | } |
1796 | 0 | } |
1797 | | |
1798 | | /** Get the next ready positional, or nullptr if there is none |
1799 | | * |
1800 | | * \return the first ready PositionalBase pointer, or nullptr if there is no match |
1801 | | */ |
1802 | | virtual PositionalBase *GetNextPositional() override |
1803 | 247 | { |
1804 | 247 | for (Base *child: Children()) |
1805 | 741 | { |
1806 | 741 | if (auto next = child->GetNextPositional()) |
1807 | 0 | { |
1808 | 0 | return next; |
1809 | 0 | } |
1810 | 741 | } |
1811 | 247 | return nullptr; |
1812 | 247 | } |
1813 | | |
1814 | | /** Get whether this has any FlagBase children |
1815 | | * |
1816 | | * \return Whether or not there are any FlagBase children |
1817 | | */ |
1818 | | virtual bool HasFlag() const override |
1819 | 0 | { |
1820 | 0 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); }); |
1821 | 0 | } |
1822 | | |
1823 | | /** Get whether this has any PositionalBase children |
1824 | | * |
1825 | | * \return Whether or not there are any PositionalBase children |
1826 | | */ |
1827 | | virtual bool HasPositional() const override |
1828 | 0 | { |
1829 | 0 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); }); |
1830 | 0 | } |
1831 | | |
1832 | | /** Get whether this has any Command children |
1833 | | * |
1834 | | * \return Whether or not there are any Command children |
1835 | | */ |
1836 | | virtual bool HasCommand() const override |
1837 | 22 | { |
1838 | 66 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); }); |
1839 | 22 | } |
1840 | | |
1841 | | /** Count the number of matched children this group has |
1842 | | */ |
1843 | | std::vector<Base *>::size_type MatchedChildren() const |
1844 | 0 | { |
1845 | 0 | // Cast to avoid warnings from -Wsign-conversion |
1846 | 0 | return static_cast<std::vector<Base *>::size_type>( |
1847 | 0 | std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();})); |
1848 | 0 | } |
1849 | | |
1850 | | /** Get the list of children which were matched |
1851 | | */ |
1852 | | std::vector<Base *> GetMatchedChildren() const |
1853 | 0 | { |
1854 | 0 | // Could be replaced by C++ 20 filter, or a custom iterator. |
1855 | 0 | std::vector<Base*> matched_children; |
1856 | 0 | std::copy_if(children.begin(), children.end(), std::back_inserter(matched_children), [](Base* b){ |
1857 | 0 | return b->Matched(); |
1858 | 0 | }); |
1859 | 0 | return matched_children; |
1860 | 0 | } |
1861 | | |
1862 | | /** Gets the children which are a certain type. |
1863 | | * \tparam ChildType The type of child to select. |
1864 | | * \param matching Return only children of the type which matched (default false). |
1865 | | * \return Vector of children meeting the criteria. |
1866 | | */ |
1867 | | template <typename ChildType> |
1868 | | std::vector<ChildType *> GetFilteredChildren(bool matching = false) const |
1869 | | { |
1870 | | std::vector<ChildType *> filtered_children; |
1871 | | for(Base *child : children) { |
1872 | | if(!matching || child->Matched()) |
1873 | | { |
1874 | | ChildType* cast_result = dynamic_cast<ChildType*>(child); |
1875 | | if(cast_result != nullptr) |
1876 | | { |
1877 | | filtered_children.push_back(cast_result); |
1878 | | } |
1879 | | |
1880 | | } |
1881 | | } |
1882 | | return filtered_children; |
1883 | | } |
1884 | | |
1885 | | /** Whether or not this group matches validation |
1886 | | */ |
1887 | | virtual bool Matched() const noexcept override |
1888 | 0 | { |
1889 | 0 | return validator(*this); |
1890 | 0 | } |
1891 | | |
1892 | | /** Get validation |
1893 | | */ |
1894 | | bool Get() const |
1895 | 0 | { |
1896 | 0 | return Matched(); |
1897 | 0 | } |
1898 | | |
1899 | | /** Get all the child descriptions for help generation |
1900 | | */ |
1901 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override |
1902 | 0 | { |
1903 | 0 | std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions; |
1904 | | |
1905 | | // Push that group description on the back if not empty |
1906 | 0 | unsigned addindent = 0; |
1907 | 0 | if (!help.empty()) |
1908 | 0 | { |
1909 | 0 | descriptions.emplace_back(help, "", indent); |
1910 | 0 | addindent = 1; |
1911 | 0 | } |
1912 | |
|
1913 | 0 | for (Base *child: Children()) |
1914 | 0 | { |
1915 | 0 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) |
1916 | 0 | { |
1917 | 0 | continue; |
1918 | 0 | } |
1919 | | |
1920 | 0 | auto groupDescriptions = child->GetDescription(params, indent + addindent); |
1921 | 0 | descriptions.insert( |
1922 | 0 | std::end(descriptions), |
1923 | 0 | std::make_move_iterator(std::begin(groupDescriptions)), |
1924 | 0 | std::make_move_iterator(std::end(groupDescriptions))); |
1925 | 0 | } |
1926 | 0 | return descriptions; |
1927 | 0 | } |
1928 | | |
1929 | | /** Get the names of positional parameters |
1930 | | */ |
1931 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1932 | 0 | { |
1933 | 0 | std::vector <std::string> names; |
1934 | 0 | for (Base *child: Children()) |
1935 | 0 | { |
1936 | 0 | if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None) |
1937 | 0 | { |
1938 | 0 | continue; |
1939 | 0 | } |
1940 | | |
1941 | 0 | auto groupNames = child->GetProgramLine(params); |
1942 | 0 | names.insert( |
1943 | 0 | std::end(names), |
1944 | 0 | std::make_move_iterator(std::begin(groupNames)), |
1945 | 0 | std::make_move_iterator(std::end(groupNames))); |
1946 | 0 | } |
1947 | 0 | return names; |
1948 | 0 | } |
1949 | | |
1950 | | virtual std::vector<Command*> GetCommands() override |
1951 | 972 | { |
1952 | 972 | std::vector<Command*> res; |
1953 | 972 | for (const auto &child : Children()) |
1954 | 2.91k | { |
1955 | 2.91k | auto subparsers = child->GetCommands(); |
1956 | 2.91k | res.insert(std::end(res), std::begin(subparsers), std::end(subparsers)); |
1957 | 2.91k | } |
1958 | 972 | return res; |
1959 | 972 | } |
1960 | | |
1961 | | virtual bool IsGroup() const override |
1962 | 0 | { |
1963 | 0 | return true; |
1964 | 0 | } |
1965 | | |
1966 | | virtual void Reset() noexcept override |
1967 | 972 | { |
1968 | 972 | Base::Reset(); |
1969 | | |
1970 | 972 | for (auto &child: Children()) |
1971 | 2.91k | { |
1972 | 2.91k | child->Reset(); |
1973 | 2.91k | } |
1974 | | #ifdef ARGS_NOEXCEPT |
1975 | | error = Error::None; |
1976 | | errorMsg.clear(); |
1977 | | #endif |
1978 | 972 | } |
1979 | | |
1980 | | /** Sends a signal to the root of the tree to begin checking for |
1981 | | * duplicates. If this is the root, begins checking. |
1982 | | */ |
1983 | | void SignalDetectDuplicates() |
1984 | 2.91k | { |
1985 | 2.91k | if(parent != nullptr) parent->SignalDetectDuplicates(); |
1986 | 2.91k | else DetectDuplicateFlags(); |
1987 | 2.91k | } |
1988 | | |
1989 | | /** Detect duplicate flags. |
1990 | | * In a noexcept context, sets an error on the duplicate flag. |
1991 | | * In a normal context, throws a ParseError. |
1992 | | */ |
1993 | | void DetectDuplicateFlags() |
1994 | 2.91k | { |
1995 | 2.91k | std::unordered_set<char> usedShortFlags; |
1996 | 2.91k | std::unordered_set<std::string> usedLongFlags; |
1997 | 2.91k | DetectDuplicateFlags(usedShortFlags, usedLongFlags); |
1998 | 2.91k | } |
1999 | | |
2000 | | /** Used by parameterless DetectDuplicateFlags. |
2001 | | */ |
2002 | | void DetectDuplicateFlags(std::unordered_set<char> &usedShortFlags, std::unordered_set<std::string> &usedLongFlags) |
2003 | 2.91k | { |
2004 | 2.91k | for (Base *child: Children()) |
2005 | 5.83k | { |
2006 | 5.83k | if(auto flag = dynamic_cast<FlagBase*>(child)) |
2007 | 5.83k | { |
2008 | | // Check for duplicate flags, setting a usage error on the |
2009 | | // flag if a duplicate is detected. |
2010 | 5.83k | for(EitherFlag flagString: flag->GetMatcher().GetFlagStrings()) |
2011 | 11.6k | { |
2012 | 11.6k | if(flagString.isShort) |
2013 | 5.83k | { |
2014 | 5.83k | if(usedShortFlags.count(flagString.shortFlag)) |
2015 | 0 | { |
2016 | | #ifdef ARGS_NOEXCEPT |
2017 | | flag->SetUsageError(); |
2018 | | #else |
2019 | 0 | throw ParseError("duplicate short flag detected"); |
2020 | 0 | #endif |
2021 | 0 | } |
2022 | 5.83k | else |
2023 | 5.83k | { |
2024 | 5.83k | usedShortFlags.insert(flagString.shortFlag); |
2025 | 5.83k | } |
2026 | 5.83k | } |
2027 | 5.83k | else |
2028 | 5.83k | { |
2029 | 5.83k | if(usedLongFlags.count(flagString.longFlag)) |
2030 | 0 | { |
2031 | | #ifdef ARGS_NOEXCEPT |
2032 | | flag->SetUsageError(); |
2033 | | #else |
2034 | 0 | throw ParseError("duplicate long flag detected"); |
2035 | 0 | #endif |
2036 | 0 | } |
2037 | 5.83k | else |
2038 | 5.83k | { |
2039 | 5.83k | usedLongFlags.insert(flagString.longFlag); |
2040 | 5.83k | } |
2041 | 5.83k | } |
2042 | 11.6k | } |
2043 | 5.83k | } |
2044 | 0 | else if(auto group = dynamic_cast<Group*>(child)) |
2045 | 0 | { |
2046 | | // A command opens its own flag namespace and runs its |
2047 | | // own duplicate detection as a separate root, so a flag |
2048 | | // reused either side of a command boundary is not a |
2049 | | // genuine duplicate. Only descend into plain groups |
2050 | | // here; IsGroup() is false for a Command. |
2051 | 0 | if(group->IsGroup()) |
2052 | 0 | { |
2053 | 0 | group->DetectDuplicateFlags(usedShortFlags, usedLongFlags); |
2054 | 0 | } |
2055 | 0 | } |
2056 | 5.83k | } |
2057 | 2.91k | } |
2058 | | |
2059 | | #ifdef ARGS_NOEXCEPT |
2060 | | /// Only for ARGS_NOEXCEPT |
2061 | | virtual Error GetError() const override |
2062 | | { |
2063 | | if (error != Error::None) |
2064 | | { |
2065 | | return error; |
2066 | | } |
2067 | | |
2068 | | auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); |
2069 | | if (it == Children().end()) |
2070 | | { |
2071 | | return Error::None; |
2072 | | } else |
2073 | | { |
2074 | | return (*it)->GetError(); |
2075 | | } |
2076 | | } |
2077 | | |
2078 | | /// Only for ARGS_NOEXCEPT |
2079 | | virtual std::string GetErrorMsg() const override |
2080 | | { |
2081 | | if (error != Error::None) |
2082 | | { |
2083 | | return errorMsg; |
2084 | | } |
2085 | | |
2086 | | auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); |
2087 | | if (it == Children().end()) |
2088 | | { |
2089 | | return ""; |
2090 | | } else |
2091 | | { |
2092 | | return (*it)->GetErrorMsg(); |
2093 | | } |
2094 | | } |
2095 | | #endif |
2096 | | |
2097 | | }; |
2098 | | |
2099 | | /** Class for using global options in ArgumentParser. |
2100 | | */ |
2101 | | class GlobalOptions : public Group |
2102 | | { |
2103 | | public: |
2104 | | GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global) |
2105 | 0 | { |
2106 | 0 | Add(options_); |
2107 | 0 | } |
2108 | | }; |
2109 | | |
2110 | | /** Utility class for building subparsers with coroutines/callbacks. |
2111 | | * |
2112 | | * Brief example: |
2113 | | * \code |
2114 | | * Command command(argumentParser, "command", "my command", [](args::Subparser &s) |
2115 | | * { |
2116 | | * // your command flags/positionals |
2117 | | * s.Parse(); //required |
2118 | | * //your command code |
2119 | | * }); |
2120 | | * \endcode |
2121 | | * |
2122 | | * For ARGS_NOEXCEPT mode don't forget to check `s.GetError()` after `s.Parse()` |
2123 | | * and return if it isn't equals to args::Error::None. |
2124 | | * |
2125 | | * \sa Command |
2126 | | */ |
2127 | | class Subparser : public Group |
2128 | | { |
2129 | | private: |
2130 | | std::vector<std::string> args; |
2131 | | std::vector<std::string> kicked; |
2132 | | ArgumentParser *parser = nullptr; |
2133 | | const HelpParams &helpParams; |
2134 | | const Command &command; |
2135 | | bool isParsed = false; |
2136 | | |
2137 | | public: |
2138 | | Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_) |
2139 | 0 | : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_) |
2140 | 0 | { |
2141 | 0 | } |
2142 | | |
2143 | 0 | Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_) |
2144 | 0 | { |
2145 | 0 | } |
2146 | | |
2147 | | Subparser(const Subparser&) = delete; |
2148 | | Subparser(Subparser&&) = delete; |
2149 | | Subparser &operator = (const Subparser&) = delete; |
2150 | | Subparser &operator = (Subparser&&) = delete; |
2151 | | |
2152 | | const Command &GetCommand() |
2153 | 0 | { |
2154 | 0 | return command; |
2155 | 0 | } |
2156 | | |
2157 | | /** (INTERNAL) Determines whether Parse was called or not. |
2158 | | */ |
2159 | | bool IsParsed() const |
2160 | 0 | { |
2161 | 0 | return isParsed; |
2162 | 0 | } |
2163 | | |
2164 | | /** Continue parsing arguments for new command. |
2165 | | */ |
2166 | | void Parse(); |
2167 | | |
2168 | | /** Returns a vector of kicked out arguments. |
2169 | | * |
2170 | | * \sa Base::KickOut |
2171 | | */ |
2172 | | const std::vector<std::string> &KickedOut() const noexcept |
2173 | 0 | { |
2174 | 0 | return kicked; |
2175 | 0 | } |
2176 | | }; |
2177 | | |
2178 | | /** Main class for building subparsers. |
2179 | | * |
2180 | | * /sa Subparser |
2181 | | */ |
2182 | | class Command : public Group |
2183 | | { |
2184 | | private: |
2185 | | friend class Subparser; |
2186 | | |
2187 | | std::string name; |
2188 | | std::string help; |
2189 | | std::string description; |
2190 | | std::string epilog; |
2191 | | std::string proglinePostfix; |
2192 | | |
2193 | | std::function<void(Subparser&)> parserCoroutine; |
2194 | | bool commandIsRequired = true; |
2195 | | Command *selectedCommand = nullptr; |
2196 | | |
2197 | | mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription; |
2198 | | mutable std::vector<std::string> subparserProgramLine; |
2199 | | mutable bool subparserHasFlag = false; |
2200 | | mutable bool subparserHasPositional = false; |
2201 | | mutable bool subparserHasCommand = false; |
2202 | | #ifdef ARGS_NOEXCEPT |
2203 | | mutable Error subparserError = Error::None; |
2204 | | #endif |
2205 | | mutable Subparser *subparser = nullptr; |
2206 | | |
2207 | | protected: |
2208 | | |
2209 | | class RaiiSubparser |
2210 | | { |
2211 | | public: |
2212 | | RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_); |
2213 | | RaiiSubparser(const Command &command_, const HelpParams ¶ms_); |
2214 | | |
2215 | | ~RaiiSubparser() |
2216 | 0 | { |
2217 | 0 | command.subparser = oldSubparser; |
2218 | 0 | } |
2219 | | |
2220 | | Subparser &Parser() |
2221 | 0 | { |
2222 | 0 | return parser; |
2223 | 0 | } |
2224 | | |
2225 | | private: |
2226 | | const Command &command; |
2227 | | Subparser parser; |
2228 | | Subparser *oldSubparser; |
2229 | | }; |
2230 | | |
2231 | 972 | Command() = default; |
2232 | | |
2233 | | std::function<void(Subparser&)> &GetCoroutine() |
2234 | 0 | { |
2235 | 0 | return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine; |
2236 | 0 | } |
2237 | | |
2238 | | Command &SelectedCommand() |
2239 | 0 | { |
2240 | 0 | Command *res = this; |
2241 | 0 | while (res->selectedCommand != nullptr) |
2242 | 0 | { |
2243 | 0 | res = res->selectedCommand; |
2244 | 0 | } |
2245 | |
|
2246 | 0 | return *res; |
2247 | 0 | } |
2248 | | |
2249 | | const Command &SelectedCommand() const |
2250 | 0 | { |
2251 | 0 | const Command *res = this; |
2252 | 0 | while (res->selectedCommand != nullptr) |
2253 | 0 | { |
2254 | 0 | res = res->selectedCommand; |
2255 | 0 | } |
2256 | 0 |
|
2257 | 0 | return *res; |
2258 | 0 | } |
2259 | | |
2260 | | void UpdateSubparserHelp(const HelpParams ¶ms) const |
2261 | 0 | { |
2262 | 0 | if (parserCoroutine) |
2263 | 0 | { |
2264 | 0 | RaiiSubparser coro(*this, params); |
2265 | 0 | #ifndef ARGS_NOEXCEPT |
2266 | 0 | try |
2267 | 0 | { |
2268 | 0 | parserCoroutine(coro.Parser()); |
2269 | 0 | } |
2270 | 0 | catch (args::SubparserError&) |
2271 | 0 | { |
2272 | 0 | } |
2273 | | #else |
2274 | | parserCoroutine(coro.Parser()); |
2275 | | #endif |
2276 | 0 | } |
2277 | 0 | } |
2278 | | |
2279 | | public: |
2280 | | Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {}) |
2281 | | : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_)) |
2282 | 0 | { |
2283 | 0 | base_.Add(*this); |
2284 | 0 | } |
2285 | | |
2286 | | /** The description that appears on the prog line after options |
2287 | | */ |
2288 | | const std::string &ProglinePostfix() const |
2289 | 0 | { return proglinePostfix; } |
2290 | | |
2291 | | /** The description that appears on the prog line after options |
2292 | | */ |
2293 | | void ProglinePostfix(const std::string &proglinePostfix_) |
2294 | 0 | { this->proglinePostfix = proglinePostfix_; } |
2295 | | |
2296 | | /** The description that appears above options |
2297 | | */ |
2298 | | const std::string &Description() const |
2299 | 0 | { return description; } |
2300 | | /** The description that appears above options |
2301 | | */ |
2302 | | |
2303 | | void Description(const std::string &description_) |
2304 | 972 | { this->description = description_; } |
2305 | | |
2306 | | /** The description that appears below options |
2307 | | */ |
2308 | | const std::string &Epilog() const |
2309 | 0 | { return epilog; } |
2310 | | |
2311 | | /** The description that appears below options |
2312 | | */ |
2313 | | void Epilog(const std::string &epilog_) |
2314 | 972 | { this->epilog = epilog_; } |
2315 | | |
2316 | | /** The name of command |
2317 | | */ |
2318 | | const std::string &Name() const |
2319 | 0 | { return name; } |
2320 | | |
2321 | | /** The description of command |
2322 | | */ |
2323 | | const std::string &Help() const |
2324 | 0 | { return help; } |
2325 | | |
2326 | | /** If value is true, parser will fail if no command was parsed. |
2327 | | * |
2328 | | * Default: true. |
2329 | | */ |
2330 | | void RequireCommand(bool value) |
2331 | 0 | { commandIsRequired = value; } |
2332 | | |
2333 | | virtual bool IsGroup() const override |
2334 | 0 | { return false; } |
2335 | | |
2336 | | virtual bool Matched() const noexcept override |
2337 | 12.9k | { return Base::Matched(); } |
2338 | | |
2339 | | operator bool() const noexcept |
2340 | 0 | { return Matched(); } |
2341 | | |
2342 | | void Match() noexcept |
2343 | 0 | { matched = true; } |
2344 | | |
2345 | | void SelectCommand(Command *c) noexcept |
2346 | 0 | { |
2347 | 0 | selectedCommand = c; |
2348 | |
|
2349 | 0 | if (c != nullptr) |
2350 | 0 | { |
2351 | 0 | c->Match(); |
2352 | 0 | } |
2353 | 0 | } |
2354 | | |
2355 | | virtual FlagBase *Match(const EitherFlag &flag) override |
2356 | 11.7k | { |
2357 | 11.7k | if (selectedCommand != nullptr) |
2358 | 0 | { |
2359 | 0 | if (auto *res = selectedCommand->Match(flag)) |
2360 | 0 | { |
2361 | 0 | return res; |
2362 | 0 | } |
2363 | | |
2364 | 0 | for (auto *child: Children()) |
2365 | 0 | { |
2366 | 0 | if ((child->GetOptions() & Options::Global) != Options::None) |
2367 | 0 | { |
2368 | 0 | if (auto *res = child->Match(flag)) |
2369 | 0 | { |
2370 | 0 | return res; |
2371 | 0 | } |
2372 | 0 | } |
2373 | 0 | } |
2374 | | |
2375 | 0 | return nullptr; |
2376 | 0 | } |
2377 | | |
2378 | 11.7k | if (subparser != nullptr) |
2379 | 0 | { |
2380 | 0 | return subparser->Match(flag); |
2381 | 0 | } |
2382 | | |
2383 | 11.7k | return Matched() ? Group::Match(flag) : nullptr; |
2384 | 11.7k | } |
2385 | | |
2386 | | virtual std::vector<FlagBase*> GetAllFlags() override |
2387 | 0 | { |
2388 | 0 | std::vector<FlagBase*> res; |
2389 | |
|
2390 | 0 | if (!Matched()) |
2391 | 0 | { |
2392 | 0 | return res; |
2393 | 0 | } |
2394 | | |
2395 | 0 | for (auto *child: Children()) |
2396 | 0 | { |
2397 | 0 | if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None) |
2398 | 0 | { |
2399 | 0 | auto childFlags = child->GetAllFlags(); |
2400 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
2401 | 0 | } |
2402 | 0 | } |
2403 | |
|
2404 | 0 | if (selectedCommand != nullptr) |
2405 | 0 | { |
2406 | 0 | auto childFlags = selectedCommand->GetAllFlags(); |
2407 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
2408 | 0 | } |
2409 | |
|
2410 | 0 | if (subparser != nullptr) |
2411 | 0 | { |
2412 | 0 | auto childFlags = subparser->GetAllFlags(); |
2413 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
2414 | 0 | } |
2415 | |
|
2416 | 0 | return res; |
2417 | 0 | } |
2418 | | |
2419 | | virtual PositionalBase *GetNextPositional() override |
2420 | 247 | { |
2421 | 247 | if (selectedCommand != nullptr) |
2422 | 0 | { |
2423 | 0 | if (auto *res = selectedCommand->GetNextPositional()) |
2424 | 0 | { |
2425 | 0 | return res; |
2426 | 0 | } |
2427 | | |
2428 | 0 | for (auto *child: Children()) |
2429 | 0 | { |
2430 | 0 | if ((child->GetOptions() & Options::Global) != Options::None) |
2431 | 0 | { |
2432 | 0 | if (auto *res = child->GetNextPositional()) |
2433 | 0 | { |
2434 | 0 | return res; |
2435 | 0 | } |
2436 | 0 | } |
2437 | 0 | } |
2438 | | |
2439 | 0 | return nullptr; |
2440 | 0 | } |
2441 | | |
2442 | 247 | if (subparser != nullptr) |
2443 | 0 | { |
2444 | 0 | return subparser->GetNextPositional(); |
2445 | 0 | } |
2446 | | |
2447 | 247 | return Matched() ? Group::GetNextPositional() : nullptr; |
2448 | 247 | } |
2449 | | |
2450 | | virtual bool HasFlag() const override |
2451 | 0 | { |
2452 | 0 | return subparserHasFlag || Group::HasFlag(); |
2453 | 0 | } |
2454 | | |
2455 | | virtual bool HasPositional() const override |
2456 | 0 | { |
2457 | 0 | return subparserHasPositional || Group::HasPositional(); |
2458 | 0 | } |
2459 | | |
2460 | | virtual bool HasCommand() const override |
2461 | 0 | { |
2462 | 0 | return true; |
2463 | 0 | } |
2464 | | |
2465 | | std::vector<std::string> GetCommandProgramLine(const HelpParams ¶ms) const |
2466 | 0 | { |
2467 | 0 | UpdateSubparserHelp(params); |
2468 | |
|
2469 | 0 | std::vector<std::string> res; |
2470 | |
|
2471 | 0 | if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags) |
2472 | 0 | { |
2473 | 0 | res.push_back(params.proglineOptions); |
2474 | 0 | } |
2475 | |
|
2476 | 0 | auto group_res = Group::GetProgramLine(params); |
2477 | 0 | std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res)); |
2478 | |
|
2479 | 0 | res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end()); |
2480 | |
|
2481 | 0 | if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand)) |
2482 | 0 | { |
2483 | 0 | res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]"); |
2484 | 0 | } |
2485 | |
|
2486 | 0 | if (!Name().empty()) |
2487 | 0 | { |
2488 | 0 | res.insert(res.begin(), Name()); |
2489 | 0 | } |
2490 | |
|
2491 | 0 | if (!ProglinePostfix().empty()) |
2492 | 0 | { |
2493 | 0 | std::string line; |
2494 | 0 | for (auto c : ProglinePostfix()) |
2495 | 0 | { |
2496 | 0 | if (std::isspace(static_cast<unsigned char>(c))) |
2497 | 0 | { |
2498 | 0 | if (!line.empty()) |
2499 | 0 | { |
2500 | 0 | res.push_back(line); |
2501 | 0 | line.clear(); |
2502 | 0 | } |
2503 | |
|
2504 | 0 | if (c == '\n') |
2505 | 0 | { |
2506 | 0 | res.push_back("\n"); |
2507 | 0 | } |
2508 | 0 | } |
2509 | 0 | else |
2510 | 0 | { |
2511 | 0 | line += c; |
2512 | 0 | } |
2513 | 0 | } |
2514 | |
|
2515 | 0 | if (!line.empty()) |
2516 | 0 | { |
2517 | 0 | res.push_back(line); |
2518 | 0 | } |
2519 | 0 | } |
2520 | |
|
2521 | 0 | return res; |
2522 | 0 | } |
2523 | | |
2524 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
2525 | 0 | { |
2526 | 0 | if (!Matched()) |
2527 | 0 | { |
2528 | 0 | return {}; |
2529 | 0 | } |
2530 | | |
2531 | 0 | return GetCommandProgramLine(params); |
2532 | 0 | } |
2533 | | |
2534 | | virtual std::vector<Command*> GetCommands() override |
2535 | 972 | { |
2536 | 972 | if (selectedCommand != nullptr) |
2537 | 0 | { |
2538 | 0 | return selectedCommand->GetCommands(); |
2539 | 0 | } |
2540 | | |
2541 | 972 | if (Matched()) |
2542 | 972 | { |
2543 | 972 | return Group::GetCommands(); |
2544 | 972 | } |
2545 | | |
2546 | 0 | return { this }; |
2547 | 972 | } |
2548 | | |
2549 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override |
2550 | 0 | { |
2551 | 0 | std::vector<std::tuple<std::string, std::string, unsigned>> descriptions; |
2552 | 0 | unsigned addindent = 0; |
2553 | |
|
2554 | 0 | UpdateSubparserHelp(params); |
2555 | |
|
2556 | 0 | if (!Matched()) |
2557 | 0 | { |
2558 | 0 | if (params.showCommandFullHelp) |
2559 | 0 | { |
2560 | 0 | std::ostringstream s; |
2561 | 0 | bool empty = true; |
2562 | 0 | for (const auto &progline: GetCommandProgramLine(params)) |
2563 | 0 | { |
2564 | 0 | if (!empty) |
2565 | 0 | { |
2566 | 0 | s << ' '; |
2567 | 0 | } |
2568 | 0 | else |
2569 | 0 | { |
2570 | 0 | empty = false; |
2571 | 0 | } |
2572 | |
|
2573 | 0 | s << progline; |
2574 | 0 | } |
2575 | |
|
2576 | 0 | descriptions.emplace_back(s.str(), "", indent); |
2577 | 0 | } |
2578 | 0 | else |
2579 | 0 | { |
2580 | 0 | descriptions.emplace_back(Name(), help, indent); |
2581 | 0 | } |
2582 | |
|
2583 | 0 | if (!params.showCommandChildren && !params.showCommandFullHelp) |
2584 | 0 | { |
2585 | 0 | return descriptions; |
2586 | 0 | } |
2587 | | |
2588 | 0 | addindent = 1; |
2589 | 0 | } |
2590 | | |
2591 | 0 | if (params.showCommandFullHelp && !Matched()) |
2592 | 0 | { |
2593 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2594 | 0 | descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent); |
2595 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2596 | 0 | } |
2597 | |
|
2598 | 0 | for (Base *child: Children()) |
2599 | 0 | { |
2600 | 0 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) |
2601 | 0 | { |
2602 | 0 | continue; |
2603 | 0 | } |
2604 | | |
2605 | 0 | auto groupDescriptions = child->GetDescription(params, indent + addindent); |
2606 | 0 | descriptions.insert( |
2607 | 0 | std::end(descriptions), |
2608 | 0 | std::make_move_iterator(std::begin(groupDescriptions)), |
2609 | 0 | std::make_move_iterator(std::end(groupDescriptions))); |
2610 | 0 | } |
2611 | |
|
2612 | 0 | for (auto childDescription: subparserDescription) |
2613 | 0 | { |
2614 | 0 | std::get<2>(childDescription) += indent + addindent; |
2615 | 0 | descriptions.push_back(std::move(childDescription)); |
2616 | 0 | } |
2617 | |
|
2618 | 0 | if (params.showCommandFullHelp && !Matched()) |
2619 | 0 | { |
2620 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2621 | 0 | if (!Epilog().empty()) |
2622 | 0 | { |
2623 | 0 | descriptions.emplace_back(Epilog(), "", indent + addindent); |
2624 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2625 | 0 | } |
2626 | 0 | } |
2627 | |
|
2628 | 0 | return descriptions; |
2629 | 0 | } |
2630 | | |
2631 | | virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override |
2632 | 22 | { |
2633 | 22 | if (!Matched()) |
2634 | 0 | { |
2635 | 0 | return; |
2636 | 0 | } |
2637 | | |
2638 | 22 | auto onValidationError = [&] |
2639 | 22 | { |
2640 | 0 | std::ostringstream problem; |
2641 | 0 | problem << "Group validation failed somewhere!"; |
2642 | | #ifdef ARGS_NOEXCEPT |
2643 | | error = Error::Validation; |
2644 | | errorMsg = problem.str(); |
2645 | | #else |
2646 | 0 | throw ValidationError(problem.str()); |
2647 | 0 | #endif |
2648 | 0 | }; |
2649 | | |
2650 | 22 | for (Base *child: Children()) |
2651 | 66 | { |
2652 | 66 | if (child->IsGroup() && !child->Matched()) |
2653 | 0 | { |
2654 | 0 | onValidationError(); |
2655 | 0 | } |
2656 | | |
2657 | 66 | child->Validate(shortprefix, longprefix); |
2658 | 66 | } |
2659 | | |
2660 | 22 | if (subparser != nullptr) |
2661 | 0 | { |
2662 | 0 | subparser->Validate(shortprefix, longprefix); |
2663 | 0 | if (!subparser->Matched()) |
2664 | 0 | { |
2665 | 0 | onValidationError(); |
2666 | 0 | } |
2667 | 0 | } |
2668 | | |
2669 | 22 | if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand)) |
2670 | 0 | { |
2671 | 0 | std::ostringstream problem; |
2672 | 0 | problem << "Command is required"; |
2673 | | #ifdef ARGS_NOEXCEPT |
2674 | | error = Error::Validation; |
2675 | | errorMsg = problem.str(); |
2676 | | #else |
2677 | 0 | throw ValidationError(problem.str()); |
2678 | 0 | #endif |
2679 | 0 | } |
2680 | 22 | } |
2681 | | |
2682 | | virtual void Reset() noexcept override |
2683 | 972 | { |
2684 | 972 | Group::Reset(); |
2685 | 972 | selectedCommand = nullptr; |
2686 | 972 | subparserProgramLine.clear(); |
2687 | 972 | subparserDescription.clear(); |
2688 | 972 | subparserHasFlag = false; |
2689 | 972 | subparserHasPositional = false; |
2690 | 972 | subparserHasCommand = false; |
2691 | | #ifdef ARGS_NOEXCEPT |
2692 | | subparserError = Error::None; |
2693 | | #endif |
2694 | 972 | } |
2695 | | |
2696 | | #ifdef ARGS_NOEXCEPT |
2697 | | /// Only for ARGS_NOEXCEPT |
2698 | | virtual Error GetError() const override |
2699 | | { |
2700 | | if (!Matched()) |
2701 | | { |
2702 | | return Error::None; |
2703 | | } |
2704 | | |
2705 | | if (error != Error::None) |
2706 | | { |
2707 | | return error; |
2708 | | } |
2709 | | |
2710 | | if (subparserError != Error::None) |
2711 | | { |
2712 | | return subparserError; |
2713 | | } |
2714 | | |
2715 | | return Group::GetError(); |
2716 | | } |
2717 | | #endif |
2718 | | }; |
2719 | | |
2720 | | /** The main user facing command line argument parser class |
2721 | | */ |
2722 | | class ArgumentParser : public Command |
2723 | | { |
2724 | | friend class Subparser; |
2725 | | |
2726 | | private: |
2727 | | std::string longprefix; |
2728 | | std::string shortprefix; |
2729 | | |
2730 | | std::string longseparator; |
2731 | | |
2732 | | std::string terminator; |
2733 | | |
2734 | | bool allowJoinedShortValue = true; |
2735 | | bool allowJoinedLongValue = true; |
2736 | | bool allowSeparateShortValue = true; |
2737 | | bool allowSeparateLongValue = true; |
2738 | | |
2739 | | bool readCompletion = false; |
2740 | | CompletionFlag *completion = nullptr; |
2741 | | |
2742 | | protected: |
2743 | | enum class OptionType |
2744 | | { |
2745 | | LongFlag, |
2746 | | ShortFlag, |
2747 | | Positional |
2748 | | }; |
2749 | | |
2750 | | OptionType ParseOption(const std::string &s, bool allowEmpty = false) |
2751 | 2.23k | { |
2752 | 2.23k | const bool matchesLong = s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length()); |
2753 | 2.23k | const bool matchesShort = s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length()); |
2754 | | |
2755 | | // A chunk can start with both prefixes when one is a prefix of |
2756 | | // the other, or when the long prefix is empty (every string |
2757 | | // starts with it). Resolve to the longer, more specific prefix: |
2758 | | // this keeps the default "--"/"-" preference for long flags |
2759 | | // while letting a short flag be recognised under an empty long |
2760 | | // prefix instead of being swallowed as a nameless long flag. |
2761 | 2.23k | if (matchesLong && matchesShort) |
2762 | 518 | { |
2763 | 518 | return longprefix.length() >= shortprefix.length() ? OptionType::LongFlag : OptionType::ShortFlag; |
2764 | 518 | } |
2765 | | |
2766 | 1.71k | if (matchesLong) |
2767 | 0 | { |
2768 | 0 | return OptionType::LongFlag; |
2769 | 0 | } |
2770 | | |
2771 | 1.71k | if (matchesShort) |
2772 | 1.22k | { |
2773 | 1.22k | return OptionType::ShortFlag; |
2774 | 1.22k | } |
2775 | | |
2776 | 490 | return OptionType::Positional; |
2777 | 1.71k | } |
2778 | | |
2779 | | template <typename It> |
2780 | | bool Complete(FlagBase &flag, It it, It end) |
2781 | 0 | { |
2782 | 0 | auto nextIt = it; |
2783 | 0 | if (!readCompletion || (++nextIt != end)) |
2784 | 0 | { |
2785 | 0 | return false; |
2786 | 0 | } |
2787 | | |
2788 | 0 | const auto &chunk = *it; |
2789 | 0 | for (auto &choice : flag.HelpChoices(helpParams)) |
2790 | 0 | { |
2791 | 0 | AddCompletionReply(chunk, choice); |
2792 | 0 | } |
2793 | |
|
2794 | 0 | #ifndef ARGS_NOEXCEPT |
2795 | 0 | throw Completion(completion->Get()); |
2796 | | #else |
2797 | | return true; |
2798 | | #endif |
2799 | 0 | } Unexecuted instantiation: bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(args::FlagBase&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>) Unexecuted instantiation: bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(args::FlagBase&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>) |
2800 | | |
2801 | | /** (INTERNAL) Parse flag's values |
2802 | | * |
2803 | | * \param arg The string to display in error message as a flag name |
2804 | | * \param[in, out] it The iterator to first value. It will point to the last value |
2805 | | * \param end The end iterator |
2806 | | * \param joinedArg Joined value (e.g. bar in --foo=bar) |
2807 | | * \param canDiscardJoined If true joined value can be parsed as flag not as a value (as in -abcd) |
2808 | | * \param[out] values The vector to store parsed arg's values |
2809 | | */ |
2810 | | template <typename It> |
2811 | | std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end, |
2812 | | const bool allowSeparate, const bool allowJoined, |
2813 | | const bool hasJoined, const std::string &joinedArg, |
2814 | | const bool canDiscardJoined, std::vector<std::string> &values) |
2815 | 11.0k | { |
2816 | 11.0k | values.clear(); |
2817 | | |
2818 | 11.0k | Nargs nargs = flag.NumberOfArguments(); |
2819 | | |
2820 | 11.0k | if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined)) |
2821 | 0 | { |
2822 | 0 | return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; |
2823 | 0 | } |
2824 | | |
2825 | 11.0k | if (hasJoined) |
2826 | 10.6k | { |
2827 | 10.6k | if (!canDiscardJoined || (allowJoined && nargs.max != 0)) |
2828 | 19 | { |
2829 | 19 | values.push_back(joinedArg); |
2830 | 19 | } |
2831 | 10.6k | } else if (!allowSeparate) |
2832 | 0 | { |
2833 | 0 | if (nargs.min != 0) |
2834 | 0 | { |
2835 | 0 | return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; |
2836 | 0 | } |
2837 | 0 | } |
2838 | | |
2839 | | // Only gather separate values when they are allowed. A joined |
2840 | | // value that was discarded rather than taken (short chunks such |
2841 | | // as -nf when joined short values are off) means this flag isn't |
2842 | | // taking an argument here, so the rest of the chunk is flags. |
2843 | 11.0k | if (allowSeparate && (!hasJoined || !values.empty())) |
2844 | 448 | { |
2845 | 448 | auto valueIt = it; |
2846 | 448 | ++valueIt; |
2847 | | |
2848 | 448 | while (valueIt != end && |
2849 | 417 | *valueIt != terminator && |
2850 | 416 | values.size() < nargs.max && |
2851 | 0 | (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional)) |
2852 | 0 | { |
2853 | 0 | if (Complete(flag, valueIt, end)) |
2854 | 0 | { |
2855 | | // Park `it` on the completion position rather than |
2856 | | // `end`. In ARGS_NOEXCEPT mode Complete returns |
2857 | | // true (no throw), so the caller's for-loop will |
2858 | | // run its ++it after we return; advancing an |
2859 | | // already-end iterator is undefined behavior and |
2860 | | // causes a subsequent out-of-bounds read of the |
2861 | | // arg vector. Since Complete only fires when |
2862 | | // ++nextIt == end, valueIt is the last element, |
2863 | | // and ++(it=valueIt) safely lands on end. |
2864 | 0 | it = valueIt; |
2865 | 0 | return ""; |
2866 | 0 | } |
2867 | | |
2868 | 0 | values.push_back(*valueIt); |
2869 | 0 | ++it; |
2870 | 0 | ++valueIt; |
2871 | 0 | } |
2872 | 448 | } |
2873 | | |
2874 | 11.0k | if (values.size() > nargs.max) |
2875 | 19 | { |
2876 | 19 | return "Passed an argument into a non-argument flag: " + arg; |
2877 | 11.0k | } else if (values.size() < nargs.min) |
2878 | 0 | { |
2879 | 0 | if (nargs.min == 1 && nargs.max == 1) |
2880 | 0 | { |
2881 | 0 | return "Flag '" + arg + "' requires an argument but received none"; |
2882 | 0 | } else if (nargs.min == 1) |
2883 | 0 | { |
2884 | 0 | return "Flag '" + arg + "' requires at least one argument but received none"; |
2885 | 0 | } else if (nargs.min != nargs.max) |
2886 | 0 | { |
2887 | 0 | return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + |
2888 | 0 | " arguments but received " + std::to_string(values.size()); |
2889 | 0 | } else |
2890 | 0 | { |
2891 | 0 | return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + |
2892 | 0 | " arguments but received " + std::to_string(values.size()); |
2893 | 0 | } |
2894 | 0 | } |
2895 | | |
2896 | 11.0k | return {}; |
2897 | 11.0k | } std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > args::ArgumentParser::ParseArgsValues<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(args::FlagBase&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, bool, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&) Line | Count | Source | 2815 | 11.0k | { | 2816 | 11.0k | values.clear(); | 2817 | | | 2818 | 11.0k | Nargs nargs = flag.NumberOfArguments(); | 2819 | | | 2820 | 11.0k | if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined)) | 2821 | 0 | { | 2822 | 0 | return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; | 2823 | 0 | } | 2824 | | | 2825 | 11.0k | if (hasJoined) | 2826 | 10.6k | { | 2827 | 10.6k | if (!canDiscardJoined || (allowJoined && nargs.max != 0)) | 2828 | 19 | { | 2829 | 19 | values.push_back(joinedArg); | 2830 | 19 | } | 2831 | 10.6k | } else if (!allowSeparate) | 2832 | 0 | { | 2833 | 0 | if (nargs.min != 0) | 2834 | 0 | { | 2835 | 0 | return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; | 2836 | 0 | } | 2837 | 0 | } | 2838 | | | 2839 | | // Only gather separate values when they are allowed. A joined | 2840 | | // value that was discarded rather than taken (short chunks such | 2841 | | // as -nf when joined short values are off) means this flag isn't | 2842 | | // taking an argument here, so the rest of the chunk is flags. | 2843 | 11.0k | if (allowSeparate && (!hasJoined || !values.empty())) | 2844 | 448 | { | 2845 | 448 | auto valueIt = it; | 2846 | 448 | ++valueIt; | 2847 | | | 2848 | 448 | while (valueIt != end && | 2849 | 417 | *valueIt != terminator && | 2850 | 416 | values.size() < nargs.max && | 2851 | 0 | (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional)) | 2852 | 0 | { | 2853 | 0 | if (Complete(flag, valueIt, end)) | 2854 | 0 | { | 2855 | | // Park `it` on the completion position rather than | 2856 | | // `end`. In ARGS_NOEXCEPT mode Complete returns | 2857 | | // true (no throw), so the caller's for-loop will | 2858 | | // run its ++it after we return; advancing an | 2859 | | // already-end iterator is undefined behavior and | 2860 | | // causes a subsequent out-of-bounds read of the | 2861 | | // arg vector. Since Complete only fires when | 2862 | | // ++nextIt == end, valueIt is the last element, | 2863 | | // and ++(it=valueIt) safely lands on end. | 2864 | 0 | it = valueIt; | 2865 | 0 | return ""; | 2866 | 0 | } | 2867 | | | 2868 | 0 | values.push_back(*valueIt); | 2869 | 0 | ++it; | 2870 | 0 | ++valueIt; | 2871 | 0 | } | 2872 | 448 | } | 2873 | | | 2874 | 11.0k | if (values.size() > nargs.max) | 2875 | 19 | { | 2876 | 19 | return "Passed an argument into a non-argument flag: " + arg; | 2877 | 11.0k | } else if (values.size() < nargs.min) | 2878 | 0 | { | 2879 | 0 | if (nargs.min == 1 && nargs.max == 1) | 2880 | 0 | { | 2881 | 0 | return "Flag '" + arg + "' requires an argument but received none"; | 2882 | 0 | } else if (nargs.min == 1) | 2883 | 0 | { | 2884 | 0 | return "Flag '" + arg + "' requires at least one argument but received none"; | 2885 | 0 | } else if (nargs.min != nargs.max) | 2886 | 0 | { | 2887 | 0 | return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + | 2888 | 0 | " arguments but received " + std::to_string(values.size()); | 2889 | 0 | } else | 2890 | 0 | { | 2891 | 0 | return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + | 2892 | 0 | " arguments but received " + std::to_string(values.size()); | 2893 | 0 | } | 2894 | 0 | } | 2895 | | | 2896 | 11.0k | return {}; | 2897 | 11.0k | } |
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > args::ArgumentParser::ParseArgsValues<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(args::FlagBase&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, bool, bool, bool, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&) |
2898 | | |
2899 | | template <typename It> |
2900 | | bool ParseLong(It &it, It end) |
2901 | 518 | { |
2902 | 518 | const auto &chunk = *it; |
2903 | 518 | const auto argchunk = chunk.substr(longprefix.size()); |
2904 | | // Try to separate it, in case of a separator: |
2905 | 518 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); |
2906 | | // If the separator is in the argument, separate it. |
2907 | 518 | const auto arg = (separator != argchunk.npos ? |
2908 | 238 | std::string(argchunk, 0, separator) |
2909 | 518 | : argchunk); |
2910 | 518 | const auto joined = (separator != argchunk.npos ? |
2911 | 238 | argchunk.substr(separator + longseparator.size()) |
2912 | 518 | : std::string()); |
2913 | | |
2914 | 518 | if (auto flag = Match(arg)) |
2915 | 98 | { |
2916 | | #ifdef ARGS_NOEXCEPT |
2917 | | // Match() may set the flag's error (e.g. Error::Extra when |
2918 | | // Options::Single is violated). In non-noexcept mode that |
2919 | | // path throws and parsing stops before the value is read; |
2920 | | // in noexcept mode we must mirror that and skip the value |
2921 | | // parsing so the previously-stored value is preserved. |
2922 | | if (flag->GetError() != Error::None) |
2923 | | { |
2924 | | return false; |
2925 | | } |
2926 | | #endif |
2927 | 98 | std::vector<std::string> values; |
2928 | 98 | const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, |
2929 | 98 | separator != argchunk.npos, joined, false, values); |
2930 | 98 | if (!errorMessage.empty()) |
2931 | 19 | { |
2932 | 19 | #ifndef ARGS_NOEXCEPT |
2933 | 19 | throw ParseError(errorMessage); |
2934 | | #else |
2935 | | error = Error::Parse; |
2936 | | errorMsg = errorMessage; |
2937 | | return false; |
2938 | | #endif |
2939 | 19 | } |
2940 | | |
2941 | 79 | if (!readCompletion) |
2942 | 79 | { |
2943 | 79 | flag->ParseValue(values); |
2944 | | #ifdef ARGS_NOEXCEPT |
2945 | | // Non-noexcept ParseValue paths throw on Help, reader |
2946 | | // failure, or Map miss, which halts parsing. Mirror |
2947 | | // that here so a later parser-level error (e.g. an |
2948 | | // unknown flag) cannot shadow the flag's error in |
2949 | | // ArgumentParser::GetError(). |
2950 | | if (flag->GetError() != Error::None) |
2951 | | { |
2952 | | return false; |
2953 | | } |
2954 | | #endif |
2955 | 79 | } |
2956 | | |
2957 | 79 | if (flag->KickOut()) |
2958 | 0 | { |
2959 | 0 | ++it; |
2960 | 0 | return false; |
2961 | 0 | } |
2962 | 79 | } else |
2963 | 420 | { |
2964 | 420 | const std::string errorMessage("Flag could not be matched: " + arg); |
2965 | 420 | #ifndef ARGS_NOEXCEPT |
2966 | 420 | throw ParseError(errorMessage); |
2967 | | #else |
2968 | | error = Error::Parse; |
2969 | | errorMsg = errorMessage; |
2970 | | return false; |
2971 | | #endif |
2972 | 420 | } |
2973 | | |
2974 | 79 | return true; |
2975 | 518 | } bool args::ArgumentParser::ParseLong<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>) Line | Count | Source | 2901 | 518 | { | 2902 | 518 | const auto &chunk = *it; | 2903 | 518 | const auto argchunk = chunk.substr(longprefix.size()); | 2904 | | // Try to separate it, in case of a separator: | 2905 | 518 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); | 2906 | | // If the separator is in the argument, separate it. | 2907 | 518 | const auto arg = (separator != argchunk.npos ? | 2908 | 238 | std::string(argchunk, 0, separator) | 2909 | 518 | : argchunk); | 2910 | 518 | const auto joined = (separator != argchunk.npos ? | 2911 | 238 | argchunk.substr(separator + longseparator.size()) | 2912 | 518 | : std::string()); | 2913 | | | 2914 | 518 | if (auto flag = Match(arg)) | 2915 | 98 | { | 2916 | | #ifdef ARGS_NOEXCEPT | 2917 | | // Match() may set the flag's error (e.g. Error::Extra when | 2918 | | // Options::Single is violated). In non-noexcept mode that | 2919 | | // path throws and parsing stops before the value is read; | 2920 | | // in noexcept mode we must mirror that and skip the value | 2921 | | // parsing so the previously-stored value is preserved. | 2922 | | if (flag->GetError() != Error::None) | 2923 | | { | 2924 | | return false; | 2925 | | } | 2926 | | #endif | 2927 | 98 | std::vector<std::string> values; | 2928 | 98 | const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, | 2929 | 98 | separator != argchunk.npos, joined, false, values); | 2930 | 98 | if (!errorMessage.empty()) | 2931 | 19 | { | 2932 | 19 | #ifndef ARGS_NOEXCEPT | 2933 | 19 | throw ParseError(errorMessage); | 2934 | | #else | 2935 | | error = Error::Parse; | 2936 | | errorMsg = errorMessage; | 2937 | | return false; | 2938 | | #endif | 2939 | 19 | } | 2940 | | | 2941 | 79 | if (!readCompletion) | 2942 | 79 | { | 2943 | 79 | flag->ParseValue(values); | 2944 | | #ifdef ARGS_NOEXCEPT | 2945 | | // Non-noexcept ParseValue paths throw on Help, reader | 2946 | | // failure, or Map miss, which halts parsing. Mirror | 2947 | | // that here so a later parser-level error (e.g. an | 2948 | | // unknown flag) cannot shadow the flag's error in | 2949 | | // ArgumentParser::GetError(). | 2950 | | if (flag->GetError() != Error::None) | 2951 | | { | 2952 | | return false; | 2953 | | } | 2954 | | #endif | 2955 | 79 | } | 2956 | | | 2957 | 79 | if (flag->KickOut()) | 2958 | 0 | { | 2959 | 0 | ++it; | 2960 | 0 | return false; | 2961 | 0 | } | 2962 | 79 | } else | 2963 | 420 | { | 2964 | 420 | const std::string errorMessage("Flag could not be matched: " + arg); | 2965 | 420 | #ifndef ARGS_NOEXCEPT | 2966 | 420 | throw ParseError(errorMessage); | 2967 | | #else | 2968 | | error = Error::Parse; | 2969 | | errorMsg = errorMessage; | 2970 | | return false; | 2971 | | #endif | 2972 | 420 | } | 2973 | | | 2974 | 79 | return true; | 2975 | 518 | } |
Unexecuted instantiation: bool args::ArgumentParser::ParseLong<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>) |
2976 | | |
2977 | | template <typename It> |
2978 | | bool ParseShort(It &it, It end) |
2979 | 612 | { |
2980 | 612 | const auto &chunk = *it; |
2981 | 612 | const auto argchunk = chunk.substr(shortprefix.size()); |
2982 | 11.5k | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) |
2983 | 11.2k | { |
2984 | 11.2k | const auto arg = *argit; |
2985 | | |
2986 | 11.2k | if (auto flag = Match(arg)) |
2987 | 10.9k | { |
2988 | | #ifdef ARGS_NOEXCEPT |
2989 | | // See ParseLong: if Match recorded an error |
2990 | | // (e.g. Options::Single violation), bail before the |
2991 | | // value is parsed so the prior value is preserved. |
2992 | | if (flag->GetError() != Error::None) |
2993 | | { |
2994 | | return false; |
2995 | | } |
2996 | | #endif |
2997 | 10.9k | const std::string value(argit + 1, std::end(argchunk)); |
2998 | 10.9k | std::vector<std::string> values; |
2999 | 10.9k | const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, |
3000 | 10.9k | allowSeparateShortValue, allowJoinedShortValue, |
3001 | 10.9k | !value.empty(), value, !value.empty(), values); |
3002 | | |
3003 | 10.9k | if (!errorMessage.empty()) |
3004 | 0 | { |
3005 | 0 | #ifndef ARGS_NOEXCEPT |
3006 | 0 | throw ParseError(errorMessage); |
3007 | | #else |
3008 | | error = Error::Parse; |
3009 | | errorMsg = errorMessage; |
3010 | | return false; |
3011 | | #endif |
3012 | 0 | } |
3013 | | |
3014 | 10.9k | if (!readCompletion) |
3015 | 10.9k | { |
3016 | 10.9k | flag->ParseValue(values); |
3017 | | #ifdef ARGS_NOEXCEPT |
3018 | | // See ParseLong: ensure a flag-level error from |
3019 | | // ParseValue (Help, Parse, Map) halts parsing so |
3020 | | // it cannot be shadowed by a later parser error. |
3021 | | if (flag->GetError() != Error::None) |
3022 | | { |
3023 | | return false; |
3024 | | } |
3025 | | #endif |
3026 | 10.9k | } |
3027 | | |
3028 | 10.9k | if (flag->KickOut()) |
3029 | 0 | { |
3030 | 0 | ++it; |
3031 | 0 | return false; |
3032 | 0 | } |
3033 | | |
3034 | 10.9k | if (!values.empty()) |
3035 | 0 | { |
3036 | 0 | break; |
3037 | 0 | } |
3038 | 10.9k | } else |
3039 | 246 | { |
3040 | 246 | const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); |
3041 | 246 | #ifndef ARGS_NOEXCEPT |
3042 | 246 | throw ParseError(errorMessage); |
3043 | | #else |
3044 | | error = Error::Parse; |
3045 | | errorMsg = errorMessage; |
3046 | | return false; |
3047 | | #endif |
3048 | 246 | } |
3049 | 11.2k | } |
3050 | | |
3051 | 366 | return true; |
3052 | 612 | } bool args::ArgumentParser::ParseShort<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>) Line | Count | Source | 2979 | 612 | { | 2980 | 612 | const auto &chunk = *it; | 2981 | 612 | const auto argchunk = chunk.substr(shortprefix.size()); | 2982 | 11.5k | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) | 2983 | 11.2k | { | 2984 | 11.2k | const auto arg = *argit; | 2985 | | | 2986 | 11.2k | if (auto flag = Match(arg)) | 2987 | 10.9k | { | 2988 | | #ifdef ARGS_NOEXCEPT | 2989 | | // See ParseLong: if Match recorded an error | 2990 | | // (e.g. Options::Single violation), bail before the | 2991 | | // value is parsed so the prior value is preserved. | 2992 | | if (flag->GetError() != Error::None) | 2993 | | { | 2994 | | return false; | 2995 | | } | 2996 | | #endif | 2997 | 10.9k | const std::string value(argit + 1, std::end(argchunk)); | 2998 | 10.9k | std::vector<std::string> values; | 2999 | 10.9k | const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, | 3000 | 10.9k | allowSeparateShortValue, allowJoinedShortValue, | 3001 | 10.9k | !value.empty(), value, !value.empty(), values); | 3002 | | | 3003 | 10.9k | if (!errorMessage.empty()) | 3004 | 0 | { | 3005 | 0 | #ifndef ARGS_NOEXCEPT | 3006 | 0 | throw ParseError(errorMessage); | 3007 | | #else | 3008 | | error = Error::Parse; | 3009 | | errorMsg = errorMessage; | 3010 | | return false; | 3011 | | #endif | 3012 | 0 | } | 3013 | | | 3014 | 10.9k | if (!readCompletion) | 3015 | 10.9k | { | 3016 | 10.9k | flag->ParseValue(values); | 3017 | | #ifdef ARGS_NOEXCEPT | 3018 | | // See ParseLong: ensure a flag-level error from | 3019 | | // ParseValue (Help, Parse, Map) halts parsing so | 3020 | | // it cannot be shadowed by a later parser error. | 3021 | | if (flag->GetError() != Error::None) | 3022 | | { | 3023 | | return false; | 3024 | | } | 3025 | | #endif | 3026 | 10.9k | } | 3027 | | | 3028 | 10.9k | if (flag->KickOut()) | 3029 | 0 | { | 3030 | 0 | ++it; | 3031 | 0 | return false; | 3032 | 0 | } | 3033 | | | 3034 | 10.9k | if (!values.empty()) | 3035 | 0 | { | 3036 | 0 | break; | 3037 | 0 | } | 3038 | 10.9k | } else | 3039 | 246 | { | 3040 | 246 | const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); | 3041 | 246 | #ifndef ARGS_NOEXCEPT | 3042 | 246 | throw ParseError(errorMessage); | 3043 | | #else | 3044 | | error = Error::Parse; | 3045 | | errorMsg = errorMessage; | 3046 | | return false; | 3047 | | #endif | 3048 | 246 | } | 3049 | 11.2k | } | 3050 | | | 3051 | 366 | return true; | 3052 | 612 | } |
Unexecuted instantiation: bool args::ArgumentParser::ParseShort<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>&, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>) |
3053 | | |
3054 | | bool AddCompletionReply(const std::string &cur, const std::string &choice) |
3055 | 0 | { |
3056 | 0 | if (cur.empty() || choice.find(cur) == 0) |
3057 | 0 | { |
3058 | 0 | if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos) |
3059 | 0 | { |
3060 | 0 | completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size())); |
3061 | 0 | } else |
3062 | 0 | { |
3063 | 0 | completion->reply.push_back(choice); |
3064 | 0 | } |
3065 | 0 | return true; |
3066 | 0 | } |
3067 | | |
3068 | 0 | return false; |
3069 | 0 | } |
3070 | | |
3071 | | template <typename It> |
3072 | | bool Complete(It it, It end, bool terminated) |
3073 | 1.38k | { |
3074 | 1.38k | auto nextIt = it; |
3075 | 1.38k | if (!readCompletion || (++nextIt != end)) |
3076 | 1.38k | { |
3077 | 1.38k | return false; |
3078 | 1.38k | } |
3079 | | |
3080 | 0 | const auto &chunk = *it; |
3081 | 0 | auto pos = GetNextPositional(); |
3082 | 0 | std::vector<Command *> commands = GetCommands(); |
3083 | 0 | const auto optionType = ParseOption(chunk, true); |
3084 | | |
3085 | | // Once the terminator has been seen the parser treats every |
3086 | | // following chunk as positional, so only positional choices are |
3087 | | // valid completions here. Suggesting flags or commands past the |
3088 | | // terminator offers candidates the parser would then reject. |
3089 | 0 | if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) |
3090 | 0 | { |
3091 | 0 | for (auto &cmd : commands) |
3092 | 0 | { |
3093 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
3094 | 0 | { |
3095 | 0 | AddCompletionReply(chunk, cmd->Name()); |
3096 | 0 | } |
3097 | 0 | } |
3098 | 0 | } else |
3099 | 0 | { |
3100 | 0 | bool hasPositionalCompletion = true; |
3101 | |
|
3102 | 0 | if (!terminated && !commands.empty()) |
3103 | 0 | { |
3104 | 0 | for (auto &cmd : commands) |
3105 | 0 | { |
3106 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
3107 | 0 | { |
3108 | 0 | AddCompletionReply(chunk, cmd->Name()); |
3109 | 0 | } |
3110 | 0 | } |
3111 | 0 | } else if (pos) |
3112 | 0 | { |
3113 | 0 | if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
3114 | 0 | { |
3115 | 0 | auto choices = pos->HelpChoices(helpParams); |
3116 | 0 | hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; |
3117 | 0 | for (auto &choice : choices) |
3118 | 0 | { |
3119 | 0 | AddCompletionReply(chunk, choice); |
3120 | 0 | } |
3121 | 0 | } |
3122 | 0 | } |
3123 | |
|
3124 | 0 | if (!terminated && hasPositionalCompletion) |
3125 | 0 | { |
3126 | 0 | auto flags = GetAllFlags(); |
3127 | 0 | for (auto flag : flags) |
3128 | 0 | { |
3129 | 0 | if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) |
3130 | 0 | { |
3131 | 0 | continue; |
3132 | 0 | } |
3133 | | |
3134 | 0 | auto &matcher = flag->GetMatcher(); |
3135 | 0 | if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) |
3136 | 0 | { |
3137 | 0 | for (auto &flagName : matcher.GetFlagStrings()) |
3138 | 0 | { |
3139 | 0 | if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) |
3140 | 0 | { |
3141 | 0 | break; |
3142 | 0 | } |
3143 | 0 | } |
3144 | 0 | } |
3145 | 0 | } |
3146 | |
|
3147 | 0 | if (optionType == OptionType::LongFlag && allowJoinedLongValue) |
3148 | 0 | { |
3149 | 0 | const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); |
3150 | | // Only attempt joined-value completion when the |
3151 | | // separator lies at or past the long prefix, so |
3152 | | // there is a (possibly empty) flag name between |
3153 | | // them. With a custom longseparator that overlaps |
3154 | | // the prefix (e.g. LongSeparator("-") under the |
3155 | | // default "--" prefix), an attacker-controlled |
3156 | | // completion word like "--x" puts the separator |
3157 | | // inside the prefix, making `arg` shorter than |
3158 | | // longprefix. arg.substr(longprefix.size()) would |
3159 | | // then throw std::out_of_range, which escapes the |
3160 | | // parser as a non-args exception (bypassing the |
3161 | | // documented catch(args::Error) idiom) and is |
3162 | | // thrown even under ARGS_NOEXCEPT. |
3163 | 0 | if (separator != chunk.npos && separator >= longprefix.size()) |
3164 | 0 | { |
3165 | 0 | std::string arg(chunk, 0, separator); |
3166 | 0 | if (auto flag = this->Match(arg.substr(longprefix.size()))) |
3167 | 0 | { |
3168 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) |
3169 | 0 | { |
3170 | 0 | AddCompletionReply(chunk, arg + longseparator + choice); |
3171 | 0 | } |
3172 | 0 | } |
3173 | 0 | } |
3174 | 0 | } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) |
3175 | 0 | { |
3176 | 0 | if (chunk.size() > shortprefix.size() + 1) |
3177 | 0 | { |
3178 | 0 | auto arg = chunk.at(shortprefix.size()); |
3179 | | //TODO: support -abcVALUE where a and b take no value |
3180 | 0 | if (auto flag = this->Match(arg)) |
3181 | 0 | { |
3182 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) |
3183 | 0 | { |
3184 | 0 | AddCompletionReply(chunk, shortprefix + arg + choice); |
3185 | 0 | } |
3186 | 0 | } |
3187 | 0 | } |
3188 | 0 | } |
3189 | 0 | } |
3190 | 0 | } |
3191 | |
|
3192 | 0 | #ifndef ARGS_NOEXCEPT |
3193 | 0 | throw Completion(completion->Get()); |
3194 | | #else |
3195 | | return true; |
3196 | | #endif |
3197 | 1.38k | } bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, bool) Line | Count | Source | 3073 | 1.38k | { | 3074 | 1.38k | auto nextIt = it; | 3075 | 1.38k | if (!readCompletion || (++nextIt != end)) | 3076 | 1.38k | { | 3077 | 1.38k | return false; | 3078 | 1.38k | } | 3079 | | | 3080 | 0 | const auto &chunk = *it; | 3081 | 0 | auto pos = GetNextPositional(); | 3082 | 0 | std::vector<Command *> commands = GetCommands(); | 3083 | 0 | const auto optionType = ParseOption(chunk, true); | 3084 | | | 3085 | | // Once the terminator has been seen the parser treats every | 3086 | | // following chunk as positional, so only positional choices are | 3087 | | // valid completions here. Suggesting flags or commands past the | 3088 | | // terminator offers candidates the parser would then reject. | 3089 | 0 | if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) | 3090 | 0 | { | 3091 | 0 | for (auto &cmd : commands) | 3092 | 0 | { | 3093 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 3094 | 0 | { | 3095 | 0 | AddCompletionReply(chunk, cmd->Name()); | 3096 | 0 | } | 3097 | 0 | } | 3098 | 0 | } else | 3099 | 0 | { | 3100 | 0 | bool hasPositionalCompletion = true; | 3101 | |
| 3102 | 0 | if (!terminated && !commands.empty()) | 3103 | 0 | { | 3104 | 0 | for (auto &cmd : commands) | 3105 | 0 | { | 3106 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 3107 | 0 | { | 3108 | 0 | AddCompletionReply(chunk, cmd->Name()); | 3109 | 0 | } | 3110 | 0 | } | 3111 | 0 | } else if (pos) | 3112 | 0 | { | 3113 | 0 | if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 3114 | 0 | { | 3115 | 0 | auto choices = pos->HelpChoices(helpParams); | 3116 | 0 | hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; | 3117 | 0 | for (auto &choice : choices) | 3118 | 0 | { | 3119 | 0 | AddCompletionReply(chunk, choice); | 3120 | 0 | } | 3121 | 0 | } | 3122 | 0 | } | 3123 | |
| 3124 | 0 | if (!terminated && hasPositionalCompletion) | 3125 | 0 | { | 3126 | 0 | auto flags = GetAllFlags(); | 3127 | 0 | for (auto flag : flags) | 3128 | 0 | { | 3129 | 0 | if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) | 3130 | 0 | { | 3131 | 0 | continue; | 3132 | 0 | } | 3133 | | | 3134 | 0 | auto &matcher = flag->GetMatcher(); | 3135 | 0 | if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) | 3136 | 0 | { | 3137 | 0 | for (auto &flagName : matcher.GetFlagStrings()) | 3138 | 0 | { | 3139 | 0 | if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) | 3140 | 0 | { | 3141 | 0 | break; | 3142 | 0 | } | 3143 | 0 | } | 3144 | 0 | } | 3145 | 0 | } | 3146 | |
| 3147 | 0 | if (optionType == OptionType::LongFlag && allowJoinedLongValue) | 3148 | 0 | { | 3149 | 0 | const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); | 3150 | | // Only attempt joined-value completion when the | 3151 | | // separator lies at or past the long prefix, so | 3152 | | // there is a (possibly empty) flag name between | 3153 | | // them. With a custom longseparator that overlaps | 3154 | | // the prefix (e.g. LongSeparator("-") under the | 3155 | | // default "--" prefix), an attacker-controlled | 3156 | | // completion word like "--x" puts the separator | 3157 | | // inside the prefix, making `arg` shorter than | 3158 | | // longprefix. arg.substr(longprefix.size()) would | 3159 | | // then throw std::out_of_range, which escapes the | 3160 | | // parser as a non-args exception (bypassing the | 3161 | | // documented catch(args::Error) idiom) and is | 3162 | | // thrown even under ARGS_NOEXCEPT. | 3163 | 0 | if (separator != chunk.npos && separator >= longprefix.size()) | 3164 | 0 | { | 3165 | 0 | std::string arg(chunk, 0, separator); | 3166 | 0 | if (auto flag = this->Match(arg.substr(longprefix.size()))) | 3167 | 0 | { | 3168 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) | 3169 | 0 | { | 3170 | 0 | AddCompletionReply(chunk, arg + longseparator + choice); | 3171 | 0 | } | 3172 | 0 | } | 3173 | 0 | } | 3174 | 0 | } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) | 3175 | 0 | { | 3176 | 0 | if (chunk.size() > shortprefix.size() + 1) | 3177 | 0 | { | 3178 | 0 | auto arg = chunk.at(shortprefix.size()); | 3179 | | //TODO: support -abcVALUE where a and b take no value | 3180 | 0 | if (auto flag = this->Match(arg)) | 3181 | 0 | { | 3182 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) | 3183 | 0 | { | 3184 | 0 | AddCompletionReply(chunk, shortprefix + arg + choice); | 3185 | 0 | } | 3186 | 0 | } | 3187 | 0 | } | 3188 | 0 | } | 3189 | 0 | } | 3190 | 0 | } | 3191 | |
| 3192 | 0 | #ifndef ARGS_NOEXCEPT | 3193 | 0 | throw Completion(completion->Get()); | 3194 | | #else | 3195 | | return true; | 3196 | | #endif | 3197 | 1.38k | } |
Unexecuted instantiation: bool args::ArgumentParser::Complete<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, bool) |
3198 | | |
3199 | | template <typename It> |
3200 | | It Parse(It begin, It end) |
3201 | 972 | { |
3202 | 972 | bool terminated = false; |
3203 | 972 | std::vector<Command *> commands = GetCommands(); |
3204 | | |
3205 | | // Check all arg chunks |
3206 | 2.10k | for (auto it = begin; it != end; ++it) |
3207 | 1.38k | { |
3208 | 1.38k | if (Complete(it, end, terminated)) |
3209 | 0 | { |
3210 | 0 | return end; |
3211 | 0 | } |
3212 | | |
3213 | 1.38k | const auto &chunk = *it; |
3214 | | |
3215 | 1.38k | if (!terminated && chunk == terminator) |
3216 | 3 | { |
3217 | 3 | terminated = true; |
3218 | 1.37k | } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) |
3219 | 518 | { |
3220 | 518 | if (!ParseLong(it, end)) |
3221 | 0 | { |
3222 | 0 | return it; |
3223 | 0 | } |
3224 | 859 | } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) |
3225 | 612 | { |
3226 | 612 | if (!ParseShort(it, end)) |
3227 | 0 | { |
3228 | 0 | return it; |
3229 | 0 | } |
3230 | 612 | } else if (!terminated && !commands.empty()) |
3231 | 0 | { |
3232 | 0 | auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); |
3233 | 0 | if (itCommand == commands.end()) |
3234 | 0 | { |
3235 | 0 | const std::string errorMessage("Unknown command: " + chunk); |
3236 | 0 | #ifndef ARGS_NOEXCEPT |
3237 | 0 | throw ParseError(errorMessage); |
3238 | | #else |
3239 | | error = Error::Parse; |
3240 | | errorMsg = errorMessage; |
3241 | | return it; |
3242 | | #endif |
3243 | 0 | } |
3244 | | |
3245 | 0 | SelectCommand(*itCommand); |
3246 | |
|
3247 | 0 | if (const auto &coroutine = GetCoroutine()) |
3248 | 0 | { |
3249 | 0 | ++it; |
3250 | 0 | RaiiSubparser coro(*this, std::vector<std::string>(it, end)); |
3251 | 0 | coroutine(coro.Parser()); |
3252 | | #ifdef ARGS_NOEXCEPT |
3253 | | error = GetError(); |
3254 | | if (error != Error::None) |
3255 | | { |
3256 | | return end; |
3257 | | } |
3258 | | |
3259 | | if (!coro.Parser().IsParsed()) |
3260 | | { |
3261 | | error = Error::Usage; |
3262 | | return end; |
3263 | | } |
3264 | | #else |
3265 | 0 | if (!coro.Parser().IsParsed()) |
3266 | 0 | { |
3267 | 0 | throw UsageError("Subparser::Parse was not called"); |
3268 | 0 | } |
3269 | 0 | #endif |
3270 | | |
3271 | 0 | break; |
3272 | 0 | } |
3273 | | |
3274 | 0 | commands = GetCommands(); |
3275 | 0 | } else |
3276 | 247 | { |
3277 | 247 | auto pos = GetNextPositional(); |
3278 | 247 | if (pos) |
3279 | 0 | { |
3280 | 0 | pos->ParseValue(chunk); |
3281 | | #ifdef ARGS_NOEXCEPT |
3282 | | if (pos->GetError() != Error::None) |
3283 | | { |
3284 | | return it; |
3285 | | } |
3286 | | #endif |
3287 | |
|
3288 | 0 | if (pos->KickOut()) |
3289 | 0 | { |
3290 | 0 | return ++it; |
3291 | 0 | } |
3292 | 0 | } else |
3293 | 247 | { |
3294 | 247 | const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); |
3295 | 247 | #ifndef ARGS_NOEXCEPT |
3296 | 247 | throw ParseError(errorMessage); |
3297 | | #else |
3298 | | error = Error::Parse; |
3299 | | errorMsg = errorMessage; |
3300 | | return it; |
3301 | | #endif |
3302 | 247 | } |
3303 | 247 | } |
3304 | | |
3305 | 1.13k | if (!readCompletion && completion != nullptr && completion->Matched()) |
3306 | 0 | { |
3307 | | #ifdef ARGS_NOEXCEPT |
3308 | | if (completion->GetError() != Error::None) |
3309 | | { |
3310 | | error = completion->GetError(); |
3311 | | if (errorMsg.empty()) |
3312 | | { |
3313 | | errorMsg = completion->GetErrorMsg(); |
3314 | | } |
3315 | | return it; |
3316 | | } |
3317 | | |
3318 | | error = Error::Completion; |
3319 | | #endif |
3320 | 0 | readCompletion = true; |
3321 | 0 | ++it; |
3322 | 0 | const auto argsLeft = static_cast<size_t>(std::distance(it, end)); |
3323 | 0 | if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) |
3324 | 0 | { |
3325 | 0 | #ifndef ARGS_NOEXCEPT |
3326 | 0 | throw Completion(""); |
3327 | | #else |
3328 | | return end; |
3329 | | #endif |
3330 | 0 | } |
3331 | | |
3332 | 0 | ++it; |
3333 | 0 | std::vector<std::string> curArgs; |
3334 | 0 | curArgs.reserve(completion->cword); |
3335 | 0 | auto curIt = it; |
3336 | 0 | for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt) |
3337 | 0 | { |
3338 | 0 | curArgs.push_back(*curIt); |
3339 | 0 | } |
3340 | |
|
3341 | 0 | if (completion->syntax == "bash") |
3342 | 0 | { |
3343 | | // bash tokenizes --flag=value as --flag=value |
3344 | | // Security fix: Use size_t arithmetic throughout to avoid conversion issues |
3345 | 0 | for (size_t idx = 0; idx < curArgs.size(); ) |
3346 | 0 | { |
3347 | 0 | if (idx > 0 && curArgs[idx] == "=") |
3348 | 0 | { |
3349 | 0 | size_t prev_idx = idx - 1; // Safe since we checked idx > 0 |
3350 | 0 | curArgs[prev_idx] += "="; |
3351 | 0 | size_t next_idx = 0; |
3352 | 0 | if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size()) |
3353 | 0 | { |
3354 | 0 | curArgs[prev_idx] += curArgs[next_idx]; |
3355 | | // Erase the '=' token and the following value token. |
3356 | 0 | size_t erase_end = 0; |
3357 | 0 | if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end)) |
3358 | 0 | { |
3359 | 0 | typedef std::vector<std::string>::difference_type diff_t; |
3360 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx), |
3361 | 0 | curArgs.begin() + static_cast<diff_t>(erase_end)); |
3362 | 0 | } |
3363 | 0 | } else |
3364 | 0 | { |
3365 | | // Safe erase of single '=' token at the end |
3366 | 0 | typedef std::vector<std::string>::difference_type diff_t; |
3367 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx)); |
3368 | 0 | } |
3369 | | // Do not increment idx - next element slides into current position |
3370 | 0 | } else |
3371 | 0 | { |
3372 | 0 | ++idx; |
3373 | 0 | } |
3374 | 0 | } |
3375 | |
|
3376 | 0 | } |
3377 | 0 | #ifndef ARGS_NOEXCEPT |
3378 | 0 | try |
3379 | 0 | { |
3380 | 0 | Parse(curArgs.begin(), curArgs.end()); |
3381 | 0 | throw Completion(""); |
3382 | 0 | } |
3383 | 0 | catch (Completion &) |
3384 | 0 | { |
3385 | 0 | throw; |
3386 | 0 | } |
3387 | 0 | catch (args::Error&) |
3388 | 0 | { |
3389 | 0 | throw Completion(""); |
3390 | 0 | } |
3391 | | #else |
3392 | | // Discard the nested Parse's return value: it points |
3393 | | // into the local curArgs vector, which is destroyed |
3394 | | // when this function returns, leaving the caller with |
3395 | | // a dangling iterator that would be compared against |
3396 | | // the outer `end` in ParseCLI. Return the outer |
3397 | | // `end` instead so the iterator stays in the caller's |
3398 | | // container. |
3399 | | Parse(curArgs.begin(), curArgs.end()); |
3400 | | error = Error::Completion; |
3401 | | errorMsg.clear(); |
3402 | | return end; |
3403 | | #endif |
3404 | 0 | } |
3405 | 1.13k | } |
3406 | | |
3407 | 725 | Validate(shortprefix, longprefix); |
3408 | 725 | return end; |
3409 | 972 | } std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> args::ArgumentParser::Parse<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>) Line | Count | Source | 3201 | 972 | { | 3202 | 972 | bool terminated = false; | 3203 | 972 | std::vector<Command *> commands = GetCommands(); | 3204 | | | 3205 | | // Check all arg chunks | 3206 | 2.10k | for (auto it = begin; it != end; ++it) | 3207 | 1.38k | { | 3208 | 1.38k | if (Complete(it, end, terminated)) | 3209 | 0 | { | 3210 | 0 | return end; | 3211 | 0 | } | 3212 | | | 3213 | 1.38k | const auto &chunk = *it; | 3214 | | | 3215 | 1.38k | if (!terminated && chunk == terminator) | 3216 | 3 | { | 3217 | 3 | terminated = true; | 3218 | 1.37k | } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) | 3219 | 518 | { | 3220 | 518 | if (!ParseLong(it, end)) | 3221 | 0 | { | 3222 | 0 | return it; | 3223 | 0 | } | 3224 | 859 | } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) | 3225 | 612 | { | 3226 | 612 | if (!ParseShort(it, end)) | 3227 | 0 | { | 3228 | 0 | return it; | 3229 | 0 | } | 3230 | 612 | } else if (!terminated && !commands.empty()) | 3231 | 0 | { | 3232 | 0 | auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); | 3233 | 0 | if (itCommand == commands.end()) | 3234 | 0 | { | 3235 | 0 | const std::string errorMessage("Unknown command: " + chunk); | 3236 | 0 | #ifndef ARGS_NOEXCEPT | 3237 | 0 | throw ParseError(errorMessage); | 3238 | | #else | 3239 | | error = Error::Parse; | 3240 | | errorMsg = errorMessage; | 3241 | | return it; | 3242 | | #endif | 3243 | 0 | } | 3244 | | | 3245 | 0 | SelectCommand(*itCommand); | 3246 | |
| 3247 | 0 | if (const auto &coroutine = GetCoroutine()) | 3248 | 0 | { | 3249 | 0 | ++it; | 3250 | 0 | RaiiSubparser coro(*this, std::vector<std::string>(it, end)); | 3251 | 0 | coroutine(coro.Parser()); | 3252 | | #ifdef ARGS_NOEXCEPT | 3253 | | error = GetError(); | 3254 | | if (error != Error::None) | 3255 | | { | 3256 | | return end; | 3257 | | } | 3258 | | | 3259 | | if (!coro.Parser().IsParsed()) | 3260 | | { | 3261 | | error = Error::Usage; | 3262 | | return end; | 3263 | | } | 3264 | | #else | 3265 | 0 | if (!coro.Parser().IsParsed()) | 3266 | 0 | { | 3267 | 0 | throw UsageError("Subparser::Parse was not called"); | 3268 | 0 | } | 3269 | 0 | #endif | 3270 | | | 3271 | 0 | break; | 3272 | 0 | } | 3273 | | | 3274 | 0 | commands = GetCommands(); | 3275 | 0 | } else | 3276 | 247 | { | 3277 | 247 | auto pos = GetNextPositional(); | 3278 | 247 | if (pos) | 3279 | 0 | { | 3280 | 0 | pos->ParseValue(chunk); | 3281 | | #ifdef ARGS_NOEXCEPT | 3282 | | if (pos->GetError() != Error::None) | 3283 | | { | 3284 | | return it; | 3285 | | } | 3286 | | #endif | 3287 | |
| 3288 | 0 | if (pos->KickOut()) | 3289 | 0 | { | 3290 | 0 | return ++it; | 3291 | 0 | } | 3292 | 0 | } else | 3293 | 247 | { | 3294 | 247 | const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); | 3295 | 247 | #ifndef ARGS_NOEXCEPT | 3296 | 247 | throw ParseError(errorMessage); | 3297 | | #else | 3298 | | error = Error::Parse; | 3299 | | errorMsg = errorMessage; | 3300 | | return it; | 3301 | | #endif | 3302 | 247 | } | 3303 | 247 | } | 3304 | | | 3305 | 1.13k | if (!readCompletion && completion != nullptr && completion->Matched()) | 3306 | 0 | { | 3307 | | #ifdef ARGS_NOEXCEPT | 3308 | | if (completion->GetError() != Error::None) | 3309 | | { | 3310 | | error = completion->GetError(); | 3311 | | if (errorMsg.empty()) | 3312 | | { | 3313 | | errorMsg = completion->GetErrorMsg(); | 3314 | | } | 3315 | | return it; | 3316 | | } | 3317 | | | 3318 | | error = Error::Completion; | 3319 | | #endif | 3320 | 0 | readCompletion = true; | 3321 | 0 | ++it; | 3322 | 0 | const auto argsLeft = static_cast<size_t>(std::distance(it, end)); | 3323 | 0 | if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) | 3324 | 0 | { | 3325 | 0 | #ifndef ARGS_NOEXCEPT | 3326 | 0 | throw Completion(""); | 3327 | | #else | 3328 | | return end; | 3329 | | #endif | 3330 | 0 | } | 3331 | | | 3332 | 0 | ++it; | 3333 | 0 | std::vector<std::string> curArgs; | 3334 | 0 | curArgs.reserve(completion->cword); | 3335 | 0 | auto curIt = it; | 3336 | 0 | for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt) | 3337 | 0 | { | 3338 | 0 | curArgs.push_back(*curIt); | 3339 | 0 | } | 3340 | |
| 3341 | 0 | if (completion->syntax == "bash") | 3342 | 0 | { | 3343 | | // bash tokenizes --flag=value as --flag=value | 3344 | | // Security fix: Use size_t arithmetic throughout to avoid conversion issues | 3345 | 0 | for (size_t idx = 0; idx < curArgs.size(); ) | 3346 | 0 | { | 3347 | 0 | if (idx > 0 && curArgs[idx] == "=") | 3348 | 0 | { | 3349 | 0 | size_t prev_idx = idx - 1; // Safe since we checked idx > 0 | 3350 | 0 | curArgs[prev_idx] += "="; | 3351 | 0 | size_t next_idx = 0; | 3352 | 0 | if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size()) | 3353 | 0 | { | 3354 | 0 | curArgs[prev_idx] += curArgs[next_idx]; | 3355 | | // Erase the '=' token and the following value token. | 3356 | 0 | size_t erase_end = 0; | 3357 | 0 | if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end)) | 3358 | 0 | { | 3359 | 0 | typedef std::vector<std::string>::difference_type diff_t; | 3360 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx), | 3361 | 0 | curArgs.begin() + static_cast<diff_t>(erase_end)); | 3362 | 0 | } | 3363 | 0 | } else | 3364 | 0 | { | 3365 | | // Safe erase of single '=' token at the end | 3366 | 0 | typedef std::vector<std::string>::difference_type diff_t; | 3367 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx)); | 3368 | 0 | } | 3369 | | // Do not increment idx - next element slides into current position | 3370 | 0 | } else | 3371 | 0 | { | 3372 | 0 | ++idx; | 3373 | 0 | } | 3374 | 0 | } | 3375 | |
| 3376 | 0 | } | 3377 | 0 | #ifndef ARGS_NOEXCEPT | 3378 | 0 | try | 3379 | 0 | { | 3380 | 0 | Parse(curArgs.begin(), curArgs.end()); | 3381 | 0 | throw Completion(""); | 3382 | 0 | } | 3383 | 0 | catch (Completion &) | 3384 | 0 | { | 3385 | 0 | throw; | 3386 | 0 | } | 3387 | 0 | catch (args::Error&) | 3388 | 0 | { | 3389 | 0 | throw Completion(""); | 3390 | 0 | } | 3391 | | #else | 3392 | | // Discard the nested Parse's return value: it points | 3393 | | // into the local curArgs vector, which is destroyed | 3394 | | // when this function returns, leaving the caller with | 3395 | | // a dangling iterator that would be compared against | 3396 | | // the outer `end` in ParseCLI. Return the outer | 3397 | | // `end` instead so the iterator stays in the caller's | 3398 | | // container. | 3399 | | Parse(curArgs.begin(), curArgs.end()); | 3400 | | error = Error::Completion; | 3401 | | errorMsg.clear(); | 3402 | | return end; | 3403 | | #endif | 3404 | 0 | } | 3405 | 1.13k | } | 3406 | | | 3407 | 725 | Validate(shortprefix, longprefix); | 3408 | 725 | return end; | 3409 | 972 | } |
Unexecuted instantiation: std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> args::ArgumentParser::Parse<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>) |
3410 | | |
3411 | | public: |
3412 | | HelpParams helpParams; |
3413 | | |
3414 | | ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string()) |
3415 | 972 | { |
3416 | 972 | Description(description_); |
3417 | 972 | Epilog(epilog_); |
3418 | 972 | LongPrefix("--"); |
3419 | 972 | ShortPrefix("-"); |
3420 | 972 | LongSeparator("="); |
3421 | 972 | Terminator("--"); |
3422 | 972 | SetArgumentSeparations(true, true, true, true); |
3423 | 972 | matched = true; |
3424 | 972 | } |
3425 | | |
3426 | | void AddCompletion(CompletionFlag &completionFlag) |
3427 | 0 | { |
3428 | 0 | // Only take the pointer once registration has succeeded: Add() |
3429 | 0 | // throws on a duplicate flag, and the flag it was handed is |
3430 | 0 | // gone by the time that error reaches the caller. |
3431 | 0 | Add(completionFlag); |
3432 | 0 | completion = &completionFlag; |
3433 | 0 | } |
3434 | | |
3435 | | /** The program name for help generation |
3436 | | */ |
3437 | | const std::string &Prog() const |
3438 | 0 | { return helpParams.programName; } |
3439 | | /** The program name for help generation |
3440 | | */ |
3441 | | void Prog(const std::string &prog_) |
3442 | 0 | { this->helpParams.programName = prog_; } |
3443 | | |
3444 | | /** The prefix for long flags |
3445 | | */ |
3446 | | const std::string &LongPrefix() const |
3447 | 0 | { return longprefix; } |
3448 | | /** The prefix for long flags |
3449 | | */ |
3450 | | void LongPrefix(const std::string &longprefix_) |
3451 | 972 | { |
3452 | 972 | this->longprefix = longprefix_; |
3453 | 972 | this->helpParams.longPrefix = longprefix_; |
3454 | 972 | } |
3455 | | |
3456 | | /** The prefix for short flags |
3457 | | */ |
3458 | | const std::string &ShortPrefix() const |
3459 | 0 | { return shortprefix; } |
3460 | | /** The prefix for short flags |
3461 | | */ |
3462 | | void ShortPrefix(const std::string &shortprefix_) |
3463 | 972 | { |
3464 | 972 | this->shortprefix = shortprefix_; |
3465 | 972 | this->helpParams.shortPrefix = shortprefix_; |
3466 | 972 | } |
3467 | | |
3468 | | /** The separator for long flags |
3469 | | */ |
3470 | | const std::string &LongSeparator() const |
3471 | 0 | { return longseparator; } |
3472 | | /** The separator for long flags |
3473 | | */ |
3474 | | void LongSeparator(const std::string &longseparator_) |
3475 | 972 | { |
3476 | 972 | if (longseparator_.empty()) |
3477 | 0 | { |
3478 | 0 | const std::string errorMessage("longseparator can not be set to empty"); |
3479 | | #ifdef ARGS_NOEXCEPT |
3480 | | error = Error::Usage; |
3481 | | errorMsg = errorMessage; |
3482 | | #else |
3483 | 0 | throw UsageError(errorMessage); |
3484 | 0 | #endif |
3485 | 0 | } else |
3486 | 972 | { |
3487 | 972 | this->longseparator = longseparator_; |
3488 | 972 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " "; |
3489 | 972 | } |
3490 | 972 | } |
3491 | | |
3492 | | /** The terminator that forcibly separates flags from positionals |
3493 | | */ |
3494 | | const std::string &Terminator() const |
3495 | 0 | { return terminator; } |
3496 | | /** The terminator that forcibly separates flags from positionals |
3497 | | */ |
3498 | | void Terminator(const std::string &terminator_) |
3499 | 972 | { this->terminator = terminator_; } |
3500 | | |
3501 | | /** Get the current argument separation parameters. |
3502 | | * |
3503 | | * See SetArgumentSeparations for details on what each one means. |
3504 | | */ |
3505 | | void GetArgumentSeparations( |
3506 | | bool &allowJoinedShortValue_, |
3507 | | bool &allowJoinedLongValue_, |
3508 | | bool &allowSeparateShortValue_, |
3509 | | bool &allowSeparateLongValue_) const |
3510 | 0 | { |
3511 | 0 | allowJoinedShortValue_ = this->allowJoinedShortValue; |
3512 | 0 | allowJoinedLongValue_ = this->allowJoinedLongValue; |
3513 | 0 | allowSeparateShortValue_ = this->allowSeparateShortValue; |
3514 | 0 | allowSeparateLongValue_ = this->allowSeparateLongValue; |
3515 | 0 | } |
3516 | | |
3517 | | /** Change allowed option separation. |
3518 | | * |
3519 | | * \param allowJoinedShortValue_ Allow a short flag that accepts an argument to be passed its argument immediately next to it (ie. in the same argv field) |
3520 | | * \param allowJoinedLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by the longseparator (ie. in the same argv field) |
3521 | | * \param allowSeparateShortValue_ Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) |
3522 | | * \param allowSeparateLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) |
3523 | | */ |
3524 | | void SetArgumentSeparations( |
3525 | | const bool allowJoinedShortValue_, |
3526 | | const bool allowJoinedLongValue_, |
3527 | | const bool allowSeparateShortValue_, |
3528 | | const bool allowSeparateLongValue_) |
3529 | 972 | { |
3530 | 972 | this->allowJoinedShortValue = allowJoinedShortValue_; |
3531 | 972 | this->allowJoinedLongValue = allowJoinedLongValue_; |
3532 | 972 | this->allowSeparateShortValue = allowSeparateShortValue_; |
3533 | 972 | this->allowSeparateLongValue = allowSeparateLongValue_; |
3534 | | |
3535 | 972 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " "; |
3536 | 972 | this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " "; |
3537 | 972 | } |
3538 | | |
3539 | | /** Pass the help menu into an ostream |
3540 | | */ |
3541 | | void Help(std::ostream &help_) const |
3542 | 0 | { |
3543 | 0 | auto &command = SelectedCommand(); |
3544 | 0 | const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description(); |
3545 | 0 | const auto desc_indent = helpParams.descriptionindent; |
3546 | 0 | const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0; |
3547 | 0 | const auto description_text = Wrap(commandDescription, effective_desc_width); |
3548 | 0 | const auto epilog_text = Wrap(command.Epilog(), effective_desc_width); |
3549 | 0 |
|
3550 | 0 | const bool hasoptions = command.HasFlag(); |
3551 | 0 | const bool hasarguments = command.HasPositional(); |
3552 | 0 |
|
3553 | 0 | std::vector<std::string> prognameline; |
3554 | 0 | prognameline.push_back(helpParams.usageString); |
3555 | 0 | prognameline.push_back(Prog()); |
3556 | 0 | auto commandProgLine = command.GetProgramLine(helpParams); |
3557 | 0 | prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end()); |
3558 | 0 |
|
3559 | 0 | const auto prog_sum = helpParams.progindent + helpParams.progtailindent; |
3560 | 0 | const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0; |
3561 | 0 | const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0; |
3562 | 0 | const auto proglines = Wrap(prognameline.begin(), prognameline.end(), |
3563 | 0 | effective_prog_width, |
3564 | 0 | effective_prog_first); |
3565 | 0 | auto progit = std::begin(proglines); |
3566 | 0 | if (progit != std::end(proglines)) |
3567 | 0 | { |
3568 | 0 | help_ << std::string(helpParams.progindent, ' ') << *progit << '\n'; |
3569 | 0 | ++progit; |
3570 | 0 | } |
3571 | 0 | for (; progit != std::end(proglines); ++progit) |
3572 | 0 | { |
3573 | 0 | help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n'; |
3574 | 0 | } |
3575 | 0 |
|
3576 | 0 | help_ << '\n'; |
3577 | 0 |
|
3578 | 0 | if (!description_text.empty()) |
3579 | 0 | { |
3580 | 0 | for (const auto &line: description_text) |
3581 | 0 | { |
3582 | 0 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; |
3583 | 0 | } |
3584 | 0 | help_ << "\n"; |
3585 | 0 | } |
3586 | 0 |
|
3587 | 0 | bool lastDescriptionIsNewline = false; |
3588 | 0 |
|
3589 | 0 | if (!helpParams.optionsString.empty()) |
3590 | 0 | { |
3591 | 0 | help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n"; |
3592 | 0 | } |
3593 | 0 |
|
3594 | 0 | for (const auto &desc: command.GetDescription(helpParams, 0)) |
3595 | 0 | { |
3596 | 0 | lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty(); |
3597 | 0 | const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent; |
3598 | 0 | const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter; |
3599 | 0 | const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0; |
3600 | 0 | const auto flags = Wrap(std::get<0>(desc), effective_flag_width); |
3601 | 0 | const auto info_sum = helpParams.helpindent + groupindent; |
3602 | 0 | const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0; |
3603 | 0 | const auto info = Wrap(std::get<1>(desc), effective_info_width); |
3604 | 0 |
|
3605 | 0 | std::string::size_type flagssize = 0; |
3606 | 0 | for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit) |
3607 | 0 | { |
3608 | 0 | if (flagsit != std::begin(flags)) |
3609 | 0 | { |
3610 | 0 | help_ << '\n'; |
3611 | 0 | } |
3612 | 0 | help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit; |
3613 | 0 | flagssize = Glyphs(*flagsit); |
3614 | 0 | } |
3615 | 0 |
|
3616 | 0 | auto infoit = std::begin(info); |
3617 | 0 | // groupindent is on both sides of this inequality, and therefore can be removed |
3618 | 0 | if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription) |
3619 | 0 | { |
3620 | 0 | help_ << '\n'; |
3621 | 0 | } else |
3622 | 0 | { |
3623 | 0 | // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here |
3624 | 0 | const auto indent_sum = helpParams.flagindent + flagssize; |
3625 | 0 | const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0; |
3626 | 0 | help_ << std::string(effective_space, ' ') << *infoit << '\n'; |
3627 | 0 | ++infoit; |
3628 | 0 | } |
3629 | 0 | for (; infoit != std::end(info); ++infoit) |
3630 | 0 | { |
3631 | 0 | help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n'; |
3632 | 0 | } |
3633 | 0 | } |
3634 | 0 | if (hasoptions && hasarguments && helpParams.showTerminator) |
3635 | 0 | { |
3636 | 0 | lastDescriptionIsNewline = false; |
3637 | 0 | const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0; |
3638 | 0 | for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", effective_term_width)) |
3639 | 0 | { |
3640 | 0 | help_ << std::string(helpParams.flagindent, ' ') << item << '\n'; |
3641 | 0 | } |
3642 | 0 | } |
3643 | 0 |
|
3644 | 0 | if (!lastDescriptionIsNewline) |
3645 | 0 | { |
3646 | 0 | help_ << "\n"; |
3647 | 0 | } |
3648 | 0 |
|
3649 | 0 | for (const auto &line: epilog_text) |
3650 | 0 | { |
3651 | 0 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; |
3652 | 0 | } |
3653 | 0 | } |
3654 | | |
3655 | | /** Generate a help menu as a string. |
3656 | | * |
3657 | | * \return the help text as a single string |
3658 | | */ |
3659 | | std::string Help() const |
3660 | 0 | { |
3661 | 0 | std::ostringstream help_; |
3662 | 0 | Help(help_); |
3663 | 0 | return help_.str(); |
3664 | 0 | } |
3665 | | |
3666 | | virtual void Reset() noexcept override |
3667 | 972 | { |
3668 | 972 | Command::Reset(); |
3669 | 972 | matched = true; |
3670 | 972 | readCompletion = false; |
3671 | 972 | } |
3672 | | |
3673 | | /** Parse all arguments. |
3674 | | * |
3675 | | * \param begin an iterator to the beginning of the argument list |
3676 | | * \param end an iterator to the past-the-end element of the argument list |
3677 | | * \return the iterator after the last parsed value. Only useful for kick-out |
3678 | | */ |
3679 | | template <typename It> |
3680 | | It ParseArgs(It begin, It end) |
3681 | 972 | { |
3682 | | // Reset all Matched statuses and errors |
3683 | 972 | Reset(); |
3684 | | #ifdef ARGS_NOEXCEPT |
3685 | | error = GetError(); |
3686 | | if (error != Error::None) |
3687 | | { |
3688 | | return end; |
3689 | | } |
3690 | | #endif |
3691 | 972 | return Parse(begin, end); |
3692 | 972 | } std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> args::ArgumentParser::ParseArgs<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*>) Line | Count | Source | 3681 | 972 | { | 3682 | | // Reset all Matched statuses and errors | 3683 | 972 | Reset(); | 3684 | | #ifdef ARGS_NOEXCEPT | 3685 | | error = GetError(); | 3686 | | if (error != Error::None) | 3687 | | { | 3688 | | return end; | 3689 | | } | 3690 | | #endif | 3691 | 972 | return Parse(begin, end); | 3692 | 972 | } |
Unexecuted instantiation: std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> args::ArgumentParser::ParseArgs<std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*> >(std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>, std::__1::__wrap_iter<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const*>) |
3693 | | |
3694 | | /** Parse all arguments. |
3695 | | * |
3696 | | * \param args an iterable of the arguments |
3697 | | * \return the iterator after the last parsed value. Only useful for kick-out |
3698 | | */ |
3699 | | template <typename T> |
3700 | | auto ParseArgs(const T &args) -> decltype(std::begin(args)) |
3701 | 0 | { |
3702 | 0 | return ParseArgs(std::begin(args), std::end(args)); |
3703 | 0 | } |
3704 | | |
3705 | | /** Convenience function to parse the CLI from argc and argv |
3706 | | * |
3707 | | * Just assigns the program name and vectorizes arguments for passing into ParseArgs() |
3708 | | * |
3709 | | * \return whether or not all arguments were parsed. This works for detecting kick-out, but is generally useless as it can't do anything with it. |
3710 | | */ |
3711 | | bool ParseCLI(const int argc, const char * const * argv) |
3712 | 0 | { |
3713 | 0 | if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty()) |
3714 | 0 | { |
3715 | 0 | Prog(argv[0]); |
3716 | 0 | } |
3717 | 0 |
|
3718 | 0 | std::vector<std::string> args; |
3719 | 0 | if (argc > 1 && argv != nullptr) |
3720 | 0 | { |
3721 | 0 | args.assign(argv + 1, argv + argc); |
3722 | 0 | } |
3723 | 0 |
|
3724 | 0 | return ParseArgs(args) == std::end(args); |
3725 | 0 | } |
3726 | | |
3727 | | template <typename T> |
3728 | | bool ParseCLI(const T &args) |
3729 | | { |
3730 | | return ParseArgs(args) == std::end(args); |
3731 | | } |
3732 | | }; |
3733 | | |
3734 | | inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_) |
3735 | 0 | : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser) |
3736 | 0 | { |
3737 | 0 | command.subparser = &parser; |
3738 | 0 | } |
3739 | | |
3740 | 0 | inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams ¶ms_): command(command_), parser(command, params_), oldSubparser(command.subparser) |
3741 | 0 | { |
3742 | 0 | command.subparser = &parser; |
3743 | 0 | } |
3744 | | |
3745 | | inline void Subparser::Parse() |
3746 | 0 | { |
3747 | 0 | isParsed = true; |
3748 | 0 | Reset(); |
3749 | 0 | command.subparserDescription = GetDescription(helpParams, 0); |
3750 | 0 | command.subparserHasFlag = HasFlag(); |
3751 | 0 | command.subparserHasPositional = HasPositional(); |
3752 | 0 | command.subparserHasCommand = HasCommand(); |
3753 | 0 | command.subparserProgramLine = GetProgramLine(helpParams); |
3754 | 0 | if (parser == nullptr) |
3755 | 0 | { |
3756 | 0 | #ifndef ARGS_NOEXCEPT |
3757 | 0 | throw args::SubparserError(); |
3758 | 0 | #else |
3759 | 0 | error = Error::Subparser; |
3760 | 0 | return; |
3761 | 0 | #endif |
3762 | 0 | } |
3763 | 0 |
|
3764 | 0 | auto it = parser->Parse(args.begin(), args.end()); |
3765 | 0 | command.Validate(parser->ShortPrefix(), parser->LongPrefix()); |
3766 | 0 | kicked.assign(it, args.end()); |
3767 | 0 |
|
3768 | 0 | #ifdef ARGS_NOEXCEPT |
3769 | 0 | command.subparserError = GetError(); |
3770 | 0 | #endif |
3771 | 0 | } |
3772 | | |
3773 | | inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser) |
3774 | 0 | { |
3775 | 0 | parser.Help(os); |
3776 | 0 | return os; |
3777 | 0 | } |
3778 | | |
3779 | | /** Boolean argument matcher |
3780 | | */ |
3781 | | class Flag : public FlagBase |
3782 | | { |
3783 | | public: |
3784 | 2.91k | Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_) |
3785 | 2.91k | { |
3786 | 2.91k | group_.Add(*this); |
3787 | 2.91k | } |
3788 | | |
3789 | 1.94k | Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false): Flag(group_, name_, help_, std::move(matcher_), extraError_ ? Options::Single : Options::None) |
3790 | 1.94k | { |
3791 | 1.94k | } |
3792 | | |
3793 | 0 | virtual ~Flag() {} |
3794 | | |
3795 | | /** Get whether this was matched |
3796 | | */ |
3797 | | bool Get() const |
3798 | 0 | { |
3799 | 0 | return Matched(); |
3800 | 0 | } |
3801 | | |
3802 | | virtual Nargs NumberOfArguments() const noexcept override |
3803 | 11.0k | { |
3804 | 11.0k | return 0; |
3805 | 11.0k | } |
3806 | | |
3807 | | virtual void ParseValue(const std::vector<std::string>&) override |
3808 | 11.0k | { |
3809 | 11.0k | } |
3810 | | }; |
3811 | | |
3812 | | /** Help flag class |
3813 | | * |
3814 | | * Works like a regular flag, but throws an instance of Help when it is matched |
3815 | | */ |
3816 | | class HelpFlag : public Flag |
3817 | | { |
3818 | | public: |
3819 | 972 | HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {} |
3820 | | |
3821 | 0 | virtual ~HelpFlag() {} |
3822 | | |
3823 | | virtual void ParseValue(const std::vector<std::string> &) |
3824 | 18 | { |
3825 | | #ifdef ARGS_NOEXCEPT |
3826 | | error = Error::Help; |
3827 | | errorMsg = Name(); |
3828 | | #else |
3829 | 18 | throw Help(Name()); |
3830 | 18 | #endif |
3831 | 18 | } |
3832 | | |
3833 | | /** Get whether this was matched |
3834 | | */ |
3835 | | bool Get() const noexcept |
3836 | 0 | { |
3837 | 0 | return Matched(); |
3838 | 0 | } |
3839 | | }; |
3840 | | |
3841 | | /** A flag class that simply counts the number of times it's matched |
3842 | | */ |
3843 | | class CounterFlag : public Flag |
3844 | | { |
3845 | | private: |
3846 | | const int startcount; |
3847 | | int count; |
3848 | | |
3849 | | public: |
3850 | | CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}): |
3851 | 0 | Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {} |
3852 | | |
3853 | 0 | virtual ~CounterFlag() {} |
3854 | | |
3855 | | virtual FlagBase *Match(const EitherFlag &arg) override |
3856 | 0 | { |
3857 | 0 | auto me = FlagBase::Match(arg); |
3858 | 0 | if (me) |
3859 | 0 | { |
3860 | 0 | #ifdef ARGS_NOEXCEPT |
3861 | 0 | // Suppress increment when FlagBase::Match recorded an |
3862 | 0 | // error on this same call (e.g. Options::Single violated). |
3863 | 0 | // In non-noexcept mode that path would have thrown before |
3864 | 0 | // reaching here and the count would not have advanced. |
3865 | 0 | if (GetError() != Error::None) |
3866 | 0 | { |
3867 | 0 | return me; |
3868 | 0 | } |
3869 | 0 | #endif |
3870 | 0 | ++count; |
3871 | 0 | } |
3872 | 0 | return me; |
3873 | 0 | } |
3874 | | |
3875 | | /** Get the count |
3876 | | */ |
3877 | | int &Get() noexcept |
3878 | 0 | { |
3879 | 0 | return count; |
3880 | 0 | } |
3881 | | |
3882 | 0 | int &operator *() noexcept { |
3883 | 0 | return count; |
3884 | 0 | } |
3885 | | |
3886 | 0 | const int &operator *() const noexcept { |
3887 | 0 | return count; |
3888 | 0 | } |
3889 | | |
3890 | | virtual void Reset() noexcept override |
3891 | 0 | { |
3892 | 0 | FlagBase::Reset(); |
3893 | 0 | count = startcount; |
3894 | 0 | } |
3895 | | }; |
3896 | | |
3897 | | /** A flag class that calls a function when it's matched |
3898 | | */ |
3899 | | class ActionFlag : public FlagBase |
3900 | | { |
3901 | | private: |
3902 | | std::function<void(const std::vector<std::string> &)> action; |
3903 | | Nargs nargs; |
3904 | | |
3905 | | public: |
3906 | | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function<void(const std::vector<std::string> &)> action_, Options options_ = {}): |
3907 | | FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_) |
3908 | 0 | { |
3909 | 0 | group_.Add(*this); |
3910 | 0 | } |
3911 | | |
3912 | | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}): |
3913 | | FlagBase(name_, help_, std::move(matcher_), options_), nargs(1) |
3914 | 0 | { |
3915 | 0 | group_.Add(*this); |
3916 | 0 | action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); }; |
3917 | 0 | } |
3918 | | |
3919 | | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}): |
3920 | | FlagBase(name_, help_, std::move(matcher_), options_), nargs(0) |
3921 | 0 | { |
3922 | 0 | group_.Add(*this); |
3923 | 0 | action = [action_](const std::vector<std::string> &) { return action_(); }; |
3924 | 0 | } |
3925 | | |
3926 | | virtual Nargs NumberOfArguments() const noexcept override |
3927 | 0 | { return nargs; } |
3928 | | |
3929 | | virtual void ParseValue(const std::vector<std::string> &value) override |
3930 | 0 | { action(value); } |
3931 | | }; |
3932 | | |
3933 | | /** A default Reader class for argument classes |
3934 | | * |
3935 | | * If destination type is assignable to std::string it uses an assignment to std::string. |
3936 | | * Otherwise ValueReader simply uses a std::istringstream to read into the destination type, and |
3937 | | * raises a ParseError if there are any characters left. |
3938 | | */ |
3939 | | struct ValueReader |
3940 | | { |
3941 | | private: |
3942 | | template <typename T> |
3943 | | static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type |
3944 | | HasUnsignedNegativeSign(const std::string &value) |
3945 | | { |
3946 | | const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c) |
3947 | | { |
3948 | | return std::isspace(static_cast<unsigned char>(c)) != 0; |
3949 | | }); |
3950 | | |
3951 | | return firstNonSpace != value.end() && *firstNonSpace == '-'; |
3952 | | } |
3953 | | |
3954 | | template <typename T> |
3955 | | static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type |
3956 | | HasUnsignedNegativeSign(const std::string &) |
3957 | | { |
3958 | | return false; |
3959 | | } |
3960 | | |
3961 | | public: |
3962 | | template <typename T> |
3963 | | typename std::enable_if< |
3964 | | std::is_integral<T>::value && |
3965 | | !std::is_same<T, bool>::value && |
3966 | | !std::is_same<T, char>::value && |
3967 | | !std::is_same<T, signed char>::value && |
3968 | | !std::is_same<T, unsigned char>::value, |
3969 | | bool>::type |
3970 | | ParseNumericValue(const std::string &value, T &destination) |
3971 | | { |
3972 | | if (HasUnsignedNegativeSign<T>(value)) |
3973 | | { |
3974 | | return false; |
3975 | | } |
3976 | | |
3977 | | const char *begin = value.c_str(); |
3978 | | // The true end of the value, derived from its length rather than |
3979 | | // from the first NUL. strtoull/strtoll treat the buffer as a C |
3980 | | // string and stop at an embedded '\0', so checking `*end == '\0'` |
3981 | | // for "no trailing data" is defeated by a value like "12\0junk": |
3982 | | // end lands on the embedded NUL and the junk after it is silently |
3983 | | // accepted. Comparing against `stop` validates the whole string |
3984 | | // and matches the istringstream-based reader used for other types. |
3985 | | const char *const stop = begin + value.size(); |
3986 | | |
3987 | | // C++11-compatible: use strtoull/strtoll. Hardening retained from |
3988 | | // the original from_chars draft (errno save/restore, ERANGE check, |
3989 | | // narrowing range check, trailing-whitespace tolerance). No |
3990 | | // unconditional dependency on <charconv> / C++17. |
3991 | | const int saved_errno = errno; |
3992 | | errno = 0; |
3993 | | |
3994 | | char *end = nullptr; |
3995 | | |
3996 | | if (std::is_unsigned<T>::value) |
3997 | | { |
3998 | | const unsigned long long parsed = std::strtoull(begin, &end, 0); |
3999 | | if (end == begin) |
4000 | | { |
4001 | | errno = saved_errno; |
4002 | | return false; |
4003 | | } |
4004 | | while (end != stop && std::isspace(static_cast<unsigned char>(*end))) |
4005 | | { |
4006 | | ++end; |
4007 | | } |
4008 | | if (end != stop || errno == ERANGE || |
4009 | | parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max())) |
4010 | | { |
4011 | | errno = saved_errno; |
4012 | | return false; |
4013 | | } |
4014 | | |
4015 | | destination = static_cast<T>(parsed); |
4016 | | } |
4017 | | else |
4018 | | { |
4019 | | const long long parsed = std::strtoll(begin, &end, 0); |
4020 | | if (end == begin) |
4021 | | { |
4022 | | errno = saved_errno; |
4023 | | return false; |
4024 | | } |
4025 | | while (end != stop && std::isspace(static_cast<unsigned char>(*end))) |
4026 | | { |
4027 | | ++end; |
4028 | | } |
4029 | | if (end != stop || errno == ERANGE || |
4030 | | parsed < static_cast<long long>(std::numeric_limits<T>::min()) || |
4031 | | parsed > static_cast<long long>(std::numeric_limits<T>::max())) |
4032 | | { |
4033 | | errno = saved_errno; |
4034 | | return false; |
4035 | | } |
4036 | | |
4037 | | destination = static_cast<T>(parsed); |
4038 | | } |
4039 | | |
4040 | | errno = saved_errno; |
4041 | | return true; |
4042 | | } |
4043 | | |
4044 | | template <typename T> |
4045 | | typename std::enable_if< |
4046 | | !std::is_integral<T>::value || |
4047 | | std::is_same<T, bool>::value || |
4048 | | std::is_same<T, char>::value || |
4049 | | std::is_same<T, signed char>::value || |
4050 | | std::is_same<T, unsigned char>::value, |
4051 | | bool>::type |
4052 | | ParseNumericValue(const std::string &value, T &destination) |
4053 | | { |
4054 | | std::istringstream ss(value); |
4055 | | // Pin parsing to the C locale so that the decimal separator and |
4056 | | // thousands grouping behavior do not silently depend on whatever |
4057 | | // std::locale::global was last set to elsewhere in the process. |
4058 | | // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in |
4059 | | // any locale whose numpunct facet treats ',' as the decimal point. |
4060 | | ss.imbue(std::locale::classic()); |
4061 | | ss >> destination; |
4062 | | if (ss.fail()) |
4063 | | { |
4064 | | return false; |
4065 | | } |
4066 | | |
4067 | | // Check for trailing garbage by attempting to extract any remaining characters. |
4068 | | // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit |
4069 | | // on EOF, causing false rejection of valid input. |
4070 | | char extra = '\0'; |
4071 | | ss >> std::ws >> extra; |
4072 | | // If extraction succeeded, there's trailing garbage (return false). |
4073 | | // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true). |
4074 | | // If extraction failed for other reasons, it's invalid (return false). |
4075 | | if (ss.fail()) |
4076 | | { |
4077 | | // Clear the failbit to check if EOF is the only issue |
4078 | | ss.clear(ss.rdstate() & ~std::ios::failbit); |
4079 | | return ss.eof(); |
4080 | | } |
4081 | | // Extraction succeeded, meaning there's trailing garbage |
4082 | | return false; |
4083 | | } |
4084 | | |
4085 | | template <typename T> |
4086 | | typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type |
4087 | | operator ()(const std::string &name, const std::string &value, T &destination) |
4088 | | { |
4089 | | const bool success = ParseNumericValue(value, destination); |
4090 | | if (!success) |
4091 | | { |
4092 | | #ifdef ARGS_NOEXCEPT |
4093 | | (void)name; |
4094 | | return false; |
4095 | | #else |
4096 | | std::ostringstream problem; |
4097 | | problem << "Argument '" << name << "' received invalid value type '" << value << "'"; |
4098 | | throw ParseError(problem.str()); |
4099 | | #endif |
4100 | | } |
4101 | | return true; |
4102 | | } |
4103 | | |
4104 | | template <typename T> |
4105 | | typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type |
4106 | | operator()(const std::string &, const std::string &value, T &destination) |
4107 | | { |
4108 | | destination = value; |
4109 | | return true; |
4110 | | } |
4111 | | }; |
4112 | | |
4113 | | /** An argument-accepting flag class |
4114 | | * |
4115 | | * \tparam T the type to extract the argument as |
4116 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4117 | | */ |
4118 | | template < |
4119 | | typename T, |
4120 | | typename Reader = ValueReader> |
4121 | | class ValueFlag : public ValueFlagBase |
4122 | | { |
4123 | | protected: |
4124 | | T value; |
4125 | | T defaultValue; |
4126 | | |
4127 | | virtual std::string GetDefaultString(const HelpParams&) const override |
4128 | | { |
4129 | | return detail::ToString(defaultValue); |
4130 | | } |
4131 | | |
4132 | | private: |
4133 | | Reader reader; |
4134 | | |
4135 | | public: |
4136 | | |
4137 | | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_) |
4138 | | { |
4139 | | group_.Add(*this); |
4140 | | } |
4141 | | |
4142 | | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, extraError_ ? Options::Single : Options::None) |
4143 | | { |
4144 | | } |
4145 | | |
4146 | | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_) |
4147 | | { |
4148 | | } |
4149 | | |
4150 | | virtual ~ValueFlag() {} |
4151 | | |
4152 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4153 | | { |
4154 | | const std::string &value_ = values_.at(0); |
4155 | | |
4156 | | #ifdef ARGS_NOEXCEPT |
4157 | | if (!reader(name, value_, this->value)) |
4158 | | { |
4159 | | error = Error::Parse; |
4160 | | } |
4161 | | #else |
4162 | | reader(name, value_, this->value); |
4163 | | #endif |
4164 | | } |
4165 | | |
4166 | | virtual void Reset() noexcept override |
4167 | | { |
4168 | | ValueFlagBase::Reset(); |
4169 | | value = defaultValue; |
4170 | | } |
4171 | | |
4172 | | /** Get the value |
4173 | | */ |
4174 | | T &Get() noexcept |
4175 | | { |
4176 | | return value; |
4177 | | } |
4178 | | |
4179 | | /** Get the value |
4180 | | */ |
4181 | | T &operator *() noexcept |
4182 | | { |
4183 | | return value; |
4184 | | } |
4185 | | |
4186 | | /** Get the value |
4187 | | */ |
4188 | | const T &operator *() const noexcept |
4189 | | { |
4190 | | return value; |
4191 | | } |
4192 | | |
4193 | | /** Get the value |
4194 | | */ |
4195 | | T *operator ->() noexcept |
4196 | | { |
4197 | | return &value; |
4198 | | } |
4199 | | |
4200 | | /** Get the value |
4201 | | */ |
4202 | | const T *operator ->() const noexcept |
4203 | | { |
4204 | | return &value; |
4205 | | } |
4206 | | |
4207 | | /** Get the default value |
4208 | | */ |
4209 | | const T &GetDefault() noexcept |
4210 | | { |
4211 | | return defaultValue; |
4212 | | } |
4213 | | }; |
4214 | | |
4215 | | /** An optional argument-accepting flag class |
4216 | | * |
4217 | | * \tparam T the type to extract the argument as |
4218 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4219 | | */ |
4220 | | template < |
4221 | | typename T, |
4222 | | typename Reader = ValueReader> |
4223 | | class ImplicitValueFlag : public ValueFlag<T, Reader> |
4224 | | { |
4225 | | protected: |
4226 | | T implicitValue; |
4227 | | |
4228 | | public: |
4229 | | |
4230 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {}) |
4231 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_) |
4232 | | { |
4233 | | } |
4234 | | |
4235 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {}) |
4236 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_) |
4237 | | { |
4238 | | } |
4239 | | |
4240 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) |
4241 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue() |
4242 | | { |
4243 | | } |
4244 | | |
4245 | | virtual ~ImplicitValueFlag() {} |
4246 | | |
4247 | | virtual Nargs NumberOfArguments() const noexcept override |
4248 | | { |
4249 | | return {0, 1}; |
4250 | | } |
4251 | | |
4252 | | virtual void ParseValue(const std::vector<std::string> &value_) override |
4253 | | { |
4254 | | if (value_.empty()) |
4255 | | { |
4256 | | this->value = implicitValue; |
4257 | | } else |
4258 | | { |
4259 | | ValueFlag<T, Reader>::ParseValue(value_); |
4260 | | } |
4261 | | } |
4262 | | }; |
4263 | | |
4264 | | /** A boolean flag containing a retrievable constant. |
4265 | | * |
4266 | | * \tparam T the type of the constant |
4267 | | */ |
4268 | | template <typename T> |
4269 | | class ConstantFlag : public Flag |
4270 | | { |
4271 | | T value; |
4272 | | |
4273 | | public: |
4274 | | |
4275 | | ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_, const T& value_): |
4276 | | Flag(group_, name_, help_, std::move(matcher_), options_), |
4277 | | value(value_) |
4278 | | {} |
4279 | | |
4280 | | ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T& value_, bool extraError_ = false): |
4281 | | Flag(group_, name_, help_, std::move(matcher_), extraError_), |
4282 | | value(value_) |
4283 | | {} |
4284 | | |
4285 | | T operator * () const noexcept |
4286 | | { |
4287 | | return value; |
4288 | | } |
4289 | | |
4290 | | T Get() const noexcept |
4291 | | { |
4292 | | return value; |
4293 | | } |
4294 | | |
4295 | | const T *operator -> () const noexcept |
4296 | | { |
4297 | | return &value; |
4298 | | } |
4299 | | }; |
4300 | | |
4301 | | /** A variadic arguments accepting flag class |
4302 | | * |
4303 | | * \tparam T the type to extract the argument as |
4304 | | * \tparam List the list type that houses the values |
4305 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4306 | | */ |
4307 | | template < |
4308 | | typename T, |
4309 | | template <typename...> class List = detail::vector, |
4310 | | typename Reader = ValueReader> |
4311 | | class NargsValueFlag : public FlagBase |
4312 | | { |
4313 | | protected: |
4314 | | |
4315 | | List<T> values; |
4316 | | const List<T> defaultValues; |
4317 | | Nargs nargs; |
4318 | | Reader reader; |
4319 | | |
4320 | | public: |
4321 | | |
4322 | | typedef List<T> Container; |
4323 | | typedef T value_type; |
4324 | | typedef typename Container::allocator_type allocator_type; |
4325 | | typedef typename Container::pointer pointer; |
4326 | | typedef typename Container::const_pointer const_pointer; |
4327 | | typedef T& reference; |
4328 | | typedef const T& const_reference; |
4329 | | typedef typename Container::size_type size_type; |
4330 | | typedef typename Container::difference_type difference_type; |
4331 | | typedef typename Container::iterator iterator; |
4332 | | typedef typename Container::const_iterator const_iterator; |
4333 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4334 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4335 | | |
4336 | | NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {}) |
4337 | | : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_) |
4338 | | { |
4339 | | group_.Add(*this); |
4340 | | } |
4341 | | |
4342 | | virtual ~NargsValueFlag() {} |
4343 | | |
4344 | | virtual Nargs NumberOfArguments() const noexcept override |
4345 | | { |
4346 | | return nargs; |
4347 | | } |
4348 | | |
4349 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4350 | | { |
4351 | | values.clear(); |
4352 | | |
4353 | | for (const std::string &value : values_) |
4354 | | { |
4355 | | T v {}; |
4356 | | #ifdef ARGS_NOEXCEPT |
4357 | | if (!reader(name, value, v)) |
4358 | | { |
4359 | | error = Error::Parse; |
4360 | | return; |
4361 | | } |
4362 | | #else |
4363 | | reader(name, value, v); |
4364 | | #endif |
4365 | | values.insert(std::end(values), v); |
4366 | | } |
4367 | | } |
4368 | | |
4369 | | List<T> &Get() noexcept |
4370 | | { |
4371 | | return values; |
4372 | | } |
4373 | | |
4374 | | /** Get the value |
4375 | | */ |
4376 | | List<T> &operator *() noexcept |
4377 | | { |
4378 | | return values; |
4379 | | } |
4380 | | |
4381 | | /** Get the values |
4382 | | */ |
4383 | | const List<T> &operator *() const noexcept |
4384 | | { |
4385 | | return values; |
4386 | | } |
4387 | | |
4388 | | /** Get the values |
4389 | | */ |
4390 | | List<T> *operator ->() noexcept |
4391 | | { |
4392 | | return &values; |
4393 | | } |
4394 | | |
4395 | | /** Get the values |
4396 | | */ |
4397 | | const List<T> *operator ->() const noexcept |
4398 | | { |
4399 | | return &values; |
4400 | | } |
4401 | | |
4402 | | iterator begin() noexcept |
4403 | | { |
4404 | | return values.begin(); |
4405 | | } |
4406 | | |
4407 | | const_iterator begin() const noexcept |
4408 | | { |
4409 | | return values.begin(); |
4410 | | } |
4411 | | |
4412 | | const_iterator cbegin() const noexcept |
4413 | | { |
4414 | | return values.cbegin(); |
4415 | | } |
4416 | | |
4417 | | iterator end() noexcept |
4418 | | { |
4419 | | return values.end(); |
4420 | | } |
4421 | | |
4422 | | const_iterator end() const noexcept |
4423 | | { |
4424 | | return values.end(); |
4425 | | } |
4426 | | |
4427 | | const_iterator cend() const noexcept |
4428 | | { |
4429 | | return values.cend(); |
4430 | | } |
4431 | | |
4432 | | virtual void Reset() noexcept override |
4433 | | { |
4434 | | FlagBase::Reset(); |
4435 | | values = defaultValues; |
4436 | | } |
4437 | | |
4438 | | virtual FlagBase *Match(const EitherFlag &arg) override |
4439 | | { |
4440 | | const bool wasMatched = Matched(); |
4441 | | auto me = FlagBase::Match(arg); |
4442 | | if (me && !wasMatched) |
4443 | | { |
4444 | | values.clear(); |
4445 | | } |
4446 | | return me; |
4447 | | } |
4448 | | }; |
4449 | | |
4450 | | /** An argument-accepting flag class that pushes the found values into a list |
4451 | | * |
4452 | | * \tparam T the type to extract the argument as |
4453 | | * \tparam List the list type that houses the values |
4454 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4455 | | */ |
4456 | | template < |
4457 | | typename T, |
4458 | | template <typename...> class List = detail::vector, |
4459 | | typename Reader = ValueReader> |
4460 | | class ValueFlagList : public ValueFlagBase |
4461 | | { |
4462 | | private: |
4463 | | using Container = List<T>; |
4464 | | Container values; |
4465 | | const Container defaultValues; |
4466 | | Reader reader; |
4467 | | |
4468 | | public: |
4469 | | |
4470 | | typedef T value_type; |
4471 | | typedef typename Container::allocator_type allocator_type; |
4472 | | typedef typename Container::pointer pointer; |
4473 | | typedef typename Container::const_pointer const_pointer; |
4474 | | typedef T& reference; |
4475 | | typedef const T& const_reference; |
4476 | | typedef typename Container::size_type size_type; |
4477 | | typedef typename Container::difference_type difference_type; |
4478 | | typedef typename Container::iterator iterator; |
4479 | | typedef typename Container::const_iterator const_iterator; |
4480 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4481 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4482 | | |
4483 | | ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}): |
4484 | | ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_) |
4485 | | { |
4486 | | group_.Add(*this); |
4487 | | } |
4488 | | |
4489 | | virtual ~ValueFlagList() {} |
4490 | | |
4491 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4492 | | { |
4493 | | const std::string &value_ = values_.at(0); |
4494 | | |
4495 | | T v{}; |
4496 | | #ifdef ARGS_NOEXCEPT |
4497 | | if (!reader(name, value_, v)) |
4498 | | { |
4499 | | error = Error::Parse; |
4500 | | return; |
4501 | | } |
4502 | | #else |
4503 | | reader(name, value_, v); |
4504 | | #endif |
4505 | | values.insert(std::end(values), v); |
4506 | | } |
4507 | | |
4508 | | /** Get the values |
4509 | | */ |
4510 | | Container &Get() noexcept |
4511 | | { |
4512 | | return values; |
4513 | | } |
4514 | | |
4515 | | /** Get the value |
4516 | | */ |
4517 | | Container &operator *() noexcept |
4518 | | { |
4519 | | return values; |
4520 | | } |
4521 | | |
4522 | | /** Get the values |
4523 | | */ |
4524 | | const Container &operator *() const noexcept |
4525 | | { |
4526 | | return values; |
4527 | | } |
4528 | | |
4529 | | /** Get the values |
4530 | | */ |
4531 | | Container *operator ->() noexcept |
4532 | | { |
4533 | | return &values; |
4534 | | } |
4535 | | |
4536 | | /** Get the values |
4537 | | */ |
4538 | | const Container *operator ->() const noexcept |
4539 | | { |
4540 | | return &values; |
4541 | | } |
4542 | | |
4543 | | virtual std::string Name() const override |
4544 | | { |
4545 | | return name + std::string("..."); |
4546 | | } |
4547 | | |
4548 | | virtual void Reset() noexcept override |
4549 | | { |
4550 | | ValueFlagBase::Reset(); |
4551 | | values = defaultValues; |
4552 | | } |
4553 | | |
4554 | | virtual FlagBase *Match(const EitherFlag &arg) override |
4555 | | { |
4556 | | const bool wasMatched = Matched(); |
4557 | | auto me = FlagBase::Match(arg); |
4558 | | if (me && !wasMatched) |
4559 | | { |
4560 | | values.clear(); |
4561 | | } |
4562 | | return me; |
4563 | | } |
4564 | | |
4565 | | iterator begin() noexcept |
4566 | | { |
4567 | | return values.begin(); |
4568 | | } |
4569 | | |
4570 | | const_iterator begin() const noexcept |
4571 | | { |
4572 | | return values.begin(); |
4573 | | } |
4574 | | |
4575 | | const_iterator cbegin() const noexcept |
4576 | | { |
4577 | | return values.cbegin(); |
4578 | | } |
4579 | | |
4580 | | iterator end() noexcept |
4581 | | { |
4582 | | return values.end(); |
4583 | | } |
4584 | | |
4585 | | const_iterator end() const noexcept |
4586 | | { |
4587 | | return values.end(); |
4588 | | } |
4589 | | |
4590 | | const_iterator cend() const noexcept |
4591 | | { |
4592 | | return values.cend(); |
4593 | | } |
4594 | | }; |
4595 | | |
4596 | | /** A mapping value flag class |
4597 | | * |
4598 | | * \tparam K the type to extract the argument as |
4599 | | * \tparam T the type to store the result as |
4600 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4601 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
4602 | | */ |
4603 | | template < |
4604 | | typename K, |
4605 | | typename T, |
4606 | | typename Reader = ValueReader, |
4607 | | template <typename...> class Map = detail::unordered_map> |
4608 | | class MapFlag : public ValueFlagBase |
4609 | | { |
4610 | | private: |
4611 | | const Map<K, T> map; |
4612 | | T value; |
4613 | | const T defaultValue; |
4614 | | Reader reader; |
4615 | | |
4616 | | protected: |
4617 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
4618 | | { |
4619 | | return detail::MapKeysToStrings(map); |
4620 | | } |
4621 | | |
4622 | | public: |
4623 | | |
4624 | | MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_), defaultValue(defaultValue_) |
4625 | | { |
4626 | | group_.Add(*this); |
4627 | | } |
4628 | | |
4629 | | MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_ = T(), const bool extraError_ = false): MapFlag(group_, name_, help_, std::move(matcher_), map_, defaultValue_, extraError_ ? Options::Single : Options::None) |
4630 | | { |
4631 | | } |
4632 | | |
4633 | | MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, Options options_): MapFlag(group_, name_, help_, std::move(matcher_), map_, T(), options_) |
4634 | | { |
4635 | | } |
4636 | | |
4637 | | virtual ~MapFlag() {} |
4638 | | |
4639 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4640 | | { |
4641 | | const std::string &value_ = values_.at(0); |
4642 | | |
4643 | | K key{}; |
4644 | | #ifdef ARGS_NOEXCEPT |
4645 | | if (!reader(name, value_, key)) |
4646 | | { |
4647 | | error = Error::Parse; |
4648 | | return; |
4649 | | } |
4650 | | #else |
4651 | | reader(name, value_, key); |
4652 | | #endif |
4653 | | auto it = map.find(key); |
4654 | | if (it == std::end(map)) |
4655 | | { |
4656 | | std::ostringstream problem; |
4657 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
4658 | | #ifdef ARGS_NOEXCEPT |
4659 | | error = Error::Map; |
4660 | | errorMsg = problem.str(); |
4661 | | #else |
4662 | | throw MapError(problem.str()); |
4663 | | #endif |
4664 | | } else |
4665 | | { |
4666 | | this->value = it->second; |
4667 | | } |
4668 | | } |
4669 | | |
4670 | | /** Get the value |
4671 | | */ |
4672 | | T &Get() noexcept |
4673 | | { |
4674 | | return value; |
4675 | | } |
4676 | | |
4677 | | /** Get the value |
4678 | | */ |
4679 | | T &operator *() noexcept |
4680 | | { |
4681 | | return value; |
4682 | | } |
4683 | | |
4684 | | /** Get the value |
4685 | | */ |
4686 | | const T &operator *() const noexcept |
4687 | | { |
4688 | | return value; |
4689 | | } |
4690 | | |
4691 | | /** Get the value |
4692 | | */ |
4693 | | T *operator ->() noexcept |
4694 | | { |
4695 | | return &value; |
4696 | | } |
4697 | | |
4698 | | /** Get the value |
4699 | | */ |
4700 | | const T *operator ->() const noexcept |
4701 | | { |
4702 | | return &value; |
4703 | | } |
4704 | | |
4705 | | virtual void Reset() noexcept override |
4706 | | { |
4707 | | ValueFlagBase::Reset(); |
4708 | | value = defaultValue; |
4709 | | } |
4710 | | }; |
4711 | | |
4712 | | /** A mapping value flag list class |
4713 | | * |
4714 | | * \tparam K the type to extract the argument as |
4715 | | * \tparam T the type to store the result as |
4716 | | * \tparam List the list type that houses the values |
4717 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4718 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
4719 | | */ |
4720 | | template < |
4721 | | typename K, |
4722 | | typename T, |
4723 | | template <typename...> class List = detail::vector, |
4724 | | typename Reader = ValueReader, |
4725 | | template <typename...> class Map = detail::unordered_map> |
4726 | | class MapFlagList : public ValueFlagBase |
4727 | | { |
4728 | | private: |
4729 | | using Container = List<T>; |
4730 | | const Map<K, T> map; |
4731 | | Container values; |
4732 | | const Container defaultValues; |
4733 | | Reader reader; |
4734 | | |
4735 | | protected: |
4736 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
4737 | | { |
4738 | | return detail::MapKeysToStrings(map); |
4739 | | } |
4740 | | |
4741 | | public: |
4742 | | typedef T value_type; |
4743 | | typedef typename Container::allocator_type allocator_type; |
4744 | | typedef typename Container::pointer pointer; |
4745 | | typedef typename Container::const_pointer const_pointer; |
4746 | | typedef T& reference; |
4747 | | typedef const T& const_reference; |
4748 | | typedef typename Container::size_type size_type; |
4749 | | typedef typename Container::difference_type difference_type; |
4750 | | typedef typename Container::iterator iterator; |
4751 | | typedef typename Container::const_iterator const_iterator; |
4752 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4753 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4754 | | |
4755 | | MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}): |
4756 | | ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_) |
4757 | | { |
4758 | | group_.Add(*this); |
4759 | | } |
4760 | | |
4761 | | virtual ~MapFlagList() {} |
4762 | | |
4763 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4764 | | { |
4765 | | const std::string &value_ = values_.at(0); |
4766 | | |
4767 | | K key{}; |
4768 | | #ifdef ARGS_NOEXCEPT |
4769 | | if (!reader(name, value_, key)) |
4770 | | { |
4771 | | error = Error::Parse; |
4772 | | return; |
4773 | | } |
4774 | | #else |
4775 | | reader(name, value_, key); |
4776 | | #endif |
4777 | | auto it = map.find(key); |
4778 | | if (it == std::end(map)) |
4779 | | { |
4780 | | std::ostringstream problem; |
4781 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
4782 | | #ifdef ARGS_NOEXCEPT |
4783 | | error = Error::Map; |
4784 | | errorMsg = problem.str(); |
4785 | | #else |
4786 | | throw MapError(problem.str()); |
4787 | | #endif |
4788 | | } else |
4789 | | { |
4790 | | detail::AppendMappedValue(values, it->second, 0); |
4791 | | } |
4792 | | } |
4793 | | |
4794 | | /** Get the value |
4795 | | */ |
4796 | | Container &Get() noexcept |
4797 | | { |
4798 | | return values; |
4799 | | } |
4800 | | |
4801 | | /** Get the value |
4802 | | */ |
4803 | | Container &operator *() noexcept |
4804 | | { |
4805 | | return values; |
4806 | | } |
4807 | | |
4808 | | /** Get the values |
4809 | | */ |
4810 | | const Container &operator *() const noexcept |
4811 | | { |
4812 | | return values; |
4813 | | } |
4814 | | |
4815 | | /** Get the values |
4816 | | */ |
4817 | | Container *operator ->() noexcept |
4818 | | { |
4819 | | return &values; |
4820 | | } |
4821 | | |
4822 | | /** Get the values |
4823 | | */ |
4824 | | const Container *operator ->() const noexcept |
4825 | | { |
4826 | | return &values; |
4827 | | } |
4828 | | |
4829 | | virtual std::string Name() const override |
4830 | | { |
4831 | | return name + std::string("..."); |
4832 | | } |
4833 | | |
4834 | | virtual void Reset() noexcept override |
4835 | | { |
4836 | | ValueFlagBase::Reset(); |
4837 | | values = defaultValues; |
4838 | | } |
4839 | | |
4840 | | virtual FlagBase *Match(const EitherFlag &arg) override |
4841 | | { |
4842 | | const bool wasMatched = Matched(); |
4843 | | auto me = FlagBase::Match(arg); |
4844 | | if (me && !wasMatched) |
4845 | | { |
4846 | | values.clear(); |
4847 | | } |
4848 | | return me; |
4849 | | } |
4850 | | |
4851 | | iterator begin() noexcept |
4852 | | { |
4853 | | return values.begin(); |
4854 | | } |
4855 | | |
4856 | | const_iterator begin() const noexcept |
4857 | | { |
4858 | | return values.begin(); |
4859 | | } |
4860 | | |
4861 | | const_iterator cbegin() const noexcept |
4862 | | { |
4863 | | return values.cbegin(); |
4864 | | } |
4865 | | |
4866 | | iterator end() noexcept |
4867 | | { |
4868 | | return values.end(); |
4869 | | } |
4870 | | |
4871 | | const_iterator end() const noexcept |
4872 | | { |
4873 | | return values.end(); |
4874 | | } |
4875 | | |
4876 | | const_iterator cend() const noexcept |
4877 | | { |
4878 | | return values.cend(); |
4879 | | } |
4880 | | }; |
4881 | | |
4882 | | /** A positional argument class |
4883 | | * |
4884 | | * \tparam T the type to extract the argument as |
4885 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4886 | | */ |
4887 | | template < |
4888 | | typename T, |
4889 | | typename Reader = ValueReader> |
4890 | | class Positional : public PositionalBase |
4891 | | { |
4892 | | private: |
4893 | | T value; |
4894 | | const T defaultValue; |
4895 | | Reader reader; |
4896 | | public: |
4897 | | Positional(Group &group_, const std::string &name_, const std::string &help_, const T &defaultValue_ = T(), Options options_ = {}): PositionalBase(name_, help_, options_), value(defaultValue_), defaultValue(defaultValue_) |
4898 | | { |
4899 | | group_.Add(*this); |
4900 | | } |
4901 | | |
4902 | | Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_) |
4903 | | { |
4904 | | } |
4905 | | |
4906 | | virtual ~Positional() {} |
4907 | | |
4908 | | virtual void ParseValue(const std::string &value_) override |
4909 | | { |
4910 | | #ifdef ARGS_NOEXCEPT |
4911 | | if (!reader(name, value_, this->value)) |
4912 | | { |
4913 | | error = Error::Parse; |
4914 | | return; |
4915 | | } |
4916 | | #else |
4917 | | reader(name, value_, this->value); |
4918 | | #endif |
4919 | | ready = false; |
4920 | | matched = true; |
4921 | | } |
4922 | | |
4923 | | /** Get the value |
4924 | | */ |
4925 | | T &Get() noexcept |
4926 | | { |
4927 | | return value; |
4928 | | } |
4929 | | |
4930 | | /** Get the value |
4931 | | */ |
4932 | | T &operator *() noexcept |
4933 | | { |
4934 | | return value; |
4935 | | } |
4936 | | |
4937 | | /** Get the value |
4938 | | */ |
4939 | | const T &operator *() const noexcept |
4940 | | { |
4941 | | return value; |
4942 | | } |
4943 | | |
4944 | | /** Get the value |
4945 | | */ |
4946 | | T *operator ->() noexcept |
4947 | | { |
4948 | | return &value; |
4949 | | } |
4950 | | |
4951 | | /** Get the value |
4952 | | */ |
4953 | | const T *operator ->() const noexcept |
4954 | | { |
4955 | | return &value; |
4956 | | } |
4957 | | |
4958 | | virtual void Reset() noexcept override |
4959 | | { |
4960 | | PositionalBase::Reset(); |
4961 | | value = defaultValue; |
4962 | | } |
4963 | | }; |
4964 | | |
4965 | | /** A positional argument class that pushes the found values into a list |
4966 | | * |
4967 | | * \tparam T the type to extract the argument as |
4968 | | * \tparam List the list type that houses the values |
4969 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
4970 | | */ |
4971 | | template < |
4972 | | typename T, |
4973 | | template <typename...> class List = detail::vector, |
4974 | | typename Reader = ValueReader> |
4975 | | class PositionalList : public PositionalBase |
4976 | | { |
4977 | | private: |
4978 | | using Container = List<T>; |
4979 | | Container values; |
4980 | | const Container defaultValues; |
4981 | | Reader reader; |
4982 | | |
4983 | | public: |
4984 | | typedef T value_type; |
4985 | | typedef typename Container::allocator_type allocator_type; |
4986 | | typedef typename Container::pointer pointer; |
4987 | | typedef typename Container::const_pointer const_pointer; |
4988 | | typedef T& reference; |
4989 | | typedef const T& const_reference; |
4990 | | typedef typename Container::size_type size_type; |
4991 | | typedef typename Container::difference_type difference_type; |
4992 | | typedef typename Container::iterator iterator; |
4993 | | typedef typename Container::const_iterator const_iterator; |
4994 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4995 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4996 | | |
4997 | | PositionalList(Group &group_, const std::string &name_, const std::string &help_, const Container &defaultValues_ = Container(), Options options_ = {}): PositionalBase(name_, help_, options_), values(defaultValues_), defaultValues(defaultValues_) |
4998 | | { |
4999 | | group_.Add(*this); |
5000 | | } |
5001 | | |
5002 | | PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_) |
5003 | | { |
5004 | | } |
5005 | | |
5006 | | virtual ~PositionalList() {} |
5007 | | |
5008 | | virtual void ParseValue(const std::string &value_) override |
5009 | | { |
5010 | | T v{}; |
5011 | | #ifdef ARGS_NOEXCEPT |
5012 | | if (!reader(name, value_, v)) |
5013 | | { |
5014 | | error = Error::Parse; |
5015 | | return; |
5016 | | } |
5017 | | #else |
5018 | | reader(name, value_, v); |
5019 | | #endif |
5020 | | values.insert(std::end(values), v); |
5021 | | matched = true; |
5022 | | } |
5023 | | |
5024 | | virtual std::string Name() const override |
5025 | | { |
5026 | | return name + std::string("..."); |
5027 | | } |
5028 | | |
5029 | | /** Get the values |
5030 | | */ |
5031 | | Container &Get() noexcept |
5032 | | { |
5033 | | return values; |
5034 | | } |
5035 | | |
5036 | | /** Get the value |
5037 | | */ |
5038 | | Container &operator *() noexcept |
5039 | | { |
5040 | | return values; |
5041 | | } |
5042 | | |
5043 | | /** Get the values |
5044 | | */ |
5045 | | const Container &operator *() const noexcept |
5046 | | { |
5047 | | return values; |
5048 | | } |
5049 | | |
5050 | | /** Get the values |
5051 | | */ |
5052 | | Container *operator ->() noexcept |
5053 | | { |
5054 | | return &values; |
5055 | | } |
5056 | | |
5057 | | /** Get the values |
5058 | | */ |
5059 | | const Container *operator ->() const noexcept |
5060 | | { |
5061 | | return &values; |
5062 | | } |
5063 | | |
5064 | | virtual void Reset() noexcept override |
5065 | | { |
5066 | | PositionalBase::Reset(); |
5067 | | values = defaultValues; |
5068 | | } |
5069 | | |
5070 | | virtual PositionalBase *GetNextPositional() override |
5071 | | { |
5072 | | const bool wasMatched = Matched(); |
5073 | | auto me = PositionalBase::GetNextPositional(); |
5074 | | if (me && !wasMatched) |
5075 | | { |
5076 | | values.clear(); |
5077 | | } |
5078 | | return me; |
5079 | | } |
5080 | | |
5081 | | iterator begin() noexcept |
5082 | | { |
5083 | | return values.begin(); |
5084 | | } |
5085 | | |
5086 | | const_iterator begin() const noexcept |
5087 | | { |
5088 | | return values.begin(); |
5089 | | } |
5090 | | |
5091 | | const_iterator cbegin() const noexcept |
5092 | | { |
5093 | | return values.cbegin(); |
5094 | | } |
5095 | | |
5096 | | iterator end() noexcept |
5097 | | { |
5098 | | return values.end(); |
5099 | | } |
5100 | | |
5101 | | const_iterator end() const noexcept |
5102 | | { |
5103 | | return values.end(); |
5104 | | } |
5105 | | |
5106 | | const_iterator cend() const noexcept |
5107 | | { |
5108 | | return values.cend(); |
5109 | | } |
5110 | | }; |
5111 | | |
5112 | | /** A positional argument mapping class |
5113 | | * |
5114 | | * \tparam K the type to extract the argument as |
5115 | | * \tparam T the type to store the result as |
5116 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
5117 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
5118 | | */ |
5119 | | template < |
5120 | | typename K, |
5121 | | typename T, |
5122 | | typename Reader = ValueReader, |
5123 | | template <typename...> class Map = detail::unordered_map> |
5124 | | class MapPositional : public PositionalBase |
5125 | | { |
5126 | | private: |
5127 | | const Map<K, T> map; |
5128 | | T value; |
5129 | | const T defaultValue; |
5130 | | Reader reader; |
5131 | | |
5132 | | protected: |
5133 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
5134 | | { |
5135 | | return detail::MapKeysToStrings(map); |
5136 | | } |
5137 | | |
5138 | | public: |
5139 | | |
5140 | | MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}): |
5141 | | PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_) |
5142 | | { |
5143 | | group_.Add(*this); |
5144 | | } |
5145 | | |
5146 | | virtual ~MapPositional() {} |
5147 | | |
5148 | | virtual void ParseValue(const std::string &value_) override |
5149 | | { |
5150 | | K key{}; |
5151 | | #ifdef ARGS_NOEXCEPT |
5152 | | if (!reader(name, value_, key)) |
5153 | | { |
5154 | | error = Error::Parse; |
5155 | | return; |
5156 | | } |
5157 | | #else |
5158 | | reader(name, value_, key); |
5159 | | #endif |
5160 | | auto it = map.find(key); |
5161 | | if (it == std::end(map)) |
5162 | | { |
5163 | | std::ostringstream problem; |
5164 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
5165 | | #ifdef ARGS_NOEXCEPT |
5166 | | error = Error::Map; |
5167 | | errorMsg = problem.str(); |
5168 | | #else |
5169 | | throw MapError(problem.str()); |
5170 | | #endif |
5171 | | } else |
5172 | | { |
5173 | | this->value = it->second; |
5174 | | ready = false; |
5175 | | matched = true; |
5176 | | } |
5177 | | } |
5178 | | |
5179 | | /** Get the value |
5180 | | */ |
5181 | | T &Get() noexcept |
5182 | | { |
5183 | | return value; |
5184 | | } |
5185 | | |
5186 | | /** Get the value |
5187 | | */ |
5188 | | T &operator *() noexcept |
5189 | | { |
5190 | | return value; |
5191 | | } |
5192 | | |
5193 | | /** Get the value |
5194 | | */ |
5195 | | const T &operator *() const noexcept |
5196 | | { |
5197 | | return value; |
5198 | | } |
5199 | | |
5200 | | /** Get the value |
5201 | | */ |
5202 | | T *operator ->() noexcept |
5203 | | { |
5204 | | return &value; |
5205 | | } |
5206 | | |
5207 | | /** Get the value |
5208 | | */ |
5209 | | const T *operator ->() const noexcept |
5210 | | { |
5211 | | return &value; |
5212 | | } |
5213 | | |
5214 | | virtual void Reset() noexcept override |
5215 | | { |
5216 | | PositionalBase::Reset(); |
5217 | | value = defaultValue; |
5218 | | } |
5219 | | }; |
5220 | | |
5221 | | /** A positional argument mapping list class |
5222 | | * |
5223 | | * \tparam K the type to extract the argument as |
5224 | | * \tparam T the type to store the result as |
5225 | | * \tparam List the list type that houses the values |
5226 | | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) |
5227 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
5228 | | */ |
5229 | | template < |
5230 | | typename K, |
5231 | | typename T, |
5232 | | template <typename...> class List = detail::vector, |
5233 | | typename Reader = ValueReader, |
5234 | | template <typename...> class Map = detail::unordered_map> |
5235 | | class MapPositionalList : public PositionalBase |
5236 | | { |
5237 | | private: |
5238 | | using Container = List<T>; |
5239 | | |
5240 | | const Map<K, T> map; |
5241 | | Container values; |
5242 | | const Container defaultValues; |
5243 | | Reader reader; |
5244 | | |
5245 | | protected: |
5246 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
5247 | | { |
5248 | | return detail::MapKeysToStrings(map); |
5249 | | } |
5250 | | |
5251 | | public: |
5252 | | typedef T value_type; |
5253 | | typedef typename Container::allocator_type allocator_type; |
5254 | | typedef typename Container::pointer pointer; |
5255 | | typedef typename Container::const_pointer const_pointer; |
5256 | | typedef T& reference; |
5257 | | typedef const T& const_reference; |
5258 | | typedef typename Container::size_type size_type; |
5259 | | typedef typename Container::difference_type difference_type; |
5260 | | typedef typename Container::iterator iterator; |
5261 | | typedef typename Container::const_iterator const_iterator; |
5262 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
5263 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
5264 | | |
5265 | | MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}): |
5266 | | PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_) |
5267 | | { |
5268 | | group_.Add(*this); |
5269 | | } |
5270 | | |
5271 | | virtual ~MapPositionalList() {} |
5272 | | |
5273 | | virtual void ParseValue(const std::string &value_) override |
5274 | | { |
5275 | | K key{}; |
5276 | | #ifdef ARGS_NOEXCEPT |
5277 | | if (!reader(name, value_, key)) |
5278 | | { |
5279 | | error = Error::Parse; |
5280 | | return; |
5281 | | } |
5282 | | #else |
5283 | | reader(name, value_, key); |
5284 | | #endif |
5285 | | auto it = map.find(key); |
5286 | | if (it == std::end(map)) |
5287 | | { |
5288 | | std::ostringstream problem; |
5289 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
5290 | | #ifdef ARGS_NOEXCEPT |
5291 | | error = Error::Map; |
5292 | | errorMsg = problem.str(); |
5293 | | #else |
5294 | | throw MapError(problem.str()); |
5295 | | #endif |
5296 | | } else |
5297 | | { |
5298 | | detail::AppendMappedValue(values, it->second, 0); |
5299 | | matched = true; |
5300 | | } |
5301 | | } |
5302 | | |
5303 | | /** Get the value |
5304 | | */ |
5305 | | Container &Get() noexcept |
5306 | | { |
5307 | | return values; |
5308 | | } |
5309 | | |
5310 | | /** Get the value |
5311 | | */ |
5312 | | Container &operator *() noexcept |
5313 | | { |
5314 | | return values; |
5315 | | } |
5316 | | |
5317 | | /** Get the values |
5318 | | */ |
5319 | | const Container &operator *() const noexcept |
5320 | | { |
5321 | | return values; |
5322 | | } |
5323 | | |
5324 | | /** Get the values |
5325 | | */ |
5326 | | Container *operator ->() noexcept |
5327 | | { |
5328 | | return &values; |
5329 | | } |
5330 | | |
5331 | | /** Get the values |
5332 | | */ |
5333 | | const Container *operator ->() const noexcept |
5334 | | { |
5335 | | return &values; |
5336 | | } |
5337 | | |
5338 | | virtual std::string Name() const override |
5339 | | { |
5340 | | return name + std::string("..."); |
5341 | | } |
5342 | | |
5343 | | virtual void Reset() noexcept override |
5344 | | { |
5345 | | PositionalBase::Reset(); |
5346 | | values = defaultValues; |
5347 | | } |
5348 | | |
5349 | | virtual PositionalBase *GetNextPositional() override |
5350 | | { |
5351 | | const bool wasMatched = Matched(); |
5352 | | auto me = PositionalBase::GetNextPositional(); |
5353 | | if (me && !wasMatched) |
5354 | | { |
5355 | | values.clear(); |
5356 | | } |
5357 | | return me; |
5358 | | } |
5359 | | |
5360 | | iterator begin() noexcept |
5361 | | { |
5362 | | return values.begin(); |
5363 | | } |
5364 | | |
5365 | | const_iterator begin() const noexcept |
5366 | | { |
5367 | | return values.begin(); |
5368 | | } |
5369 | | |
5370 | | const_iterator cbegin() const noexcept |
5371 | | { |
5372 | | return values.cbegin(); |
5373 | | } |
5374 | | |
5375 | | iterator end() noexcept |
5376 | | { |
5377 | | return values.end(); |
5378 | | } |
5379 | | |
5380 | | const_iterator end() const noexcept |
5381 | | { |
5382 | | return values.end(); |
5383 | | } |
5384 | | |
5385 | | const_iterator cend() const noexcept |
5386 | | { |
5387 | | return values.cend(); |
5388 | | } |
5389 | | }; |
5390 | | } |
5391 | | |
5392 | | #pragma pop_macro("min") |
5393 | | #pragma pop_macro("max") |
5394 | | #endif |