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 | 958 | 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 | 937 | 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 | 21 | 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.41k | EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {} |
574 | 2.94k | EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {} |
575 | 37.0k | 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.94k | { |
581 | 2.94k | std::unordered_set<std::string> longFlags; |
582 | 2.94k | for (const EitherFlag &flag: flags) |
583 | 5.89k | { |
584 | 5.89k | if (!flag.isShort) |
585 | 2.94k | { |
586 | 2.94k | longFlags.insert(flag.longFlag); |
587 | 2.94k | } |
588 | 5.89k | } |
589 | 2.94k | return longFlags; |
590 | 2.94k | } |
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.94k | { |
596 | 2.94k | std::unordered_set<char> shortFlags; |
597 | 2.94k | for (const EitherFlag &flag: flags) |
598 | 5.89k | { |
599 | 5.89k | if (flag.isShort) |
600 | 2.94k | { |
601 | 2.94k | shortFlags.insert(flag.shortFlag); |
602 | 2.94k | } |
603 | 5.89k | } |
604 | 2.94k | return shortFlags; |
605 | 2.94k | } |
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.94k | shortFlags(shortFlagsStart, shortFlagsEnd), |
640 | 2.94k | longFlags(longFlagsStart, longFlagsEnd) |
641 | 2.94k | { |
642 | 2.94k | 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.94k | } |
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.94k | Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn)) |
665 | 2.94k | {} |
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.94k | Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {} |
681 | | |
682 | 2.94k | Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags)) |
683 | 2.94k | {} |
684 | | |
685 | 5.89k | ~Matcher() {} |
686 | | |
687 | | /** (INTERNAL) Check if there is a match of a short flag |
688 | | */ |
689 | | bool Match(const char flag) const |
690 | 83.0k | { |
691 | 83.0k | return shortFlags.find(flag) != shortFlags.end(); |
692 | 83.0k | } |
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 | 84.4k | { |
705 | 84.4k | return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag); |
706 | 84.4k | } |
707 | | |
708 | | /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded |
709 | | */ |
710 | | std::vector<EitherFlag> GetFlagStrings() const |
711 | 5.89k | { |
712 | 5.89k | std::vector<EitherFlag> flagStrings; |
713 | 5.89k | flagStrings.reserve(shortFlags.size() + longFlags.size()); |
714 | 5.89k | for (const char flag: shortFlags) |
715 | 5.89k | { |
716 | 5.89k | flagStrings.emplace_back(flag); |
717 | 5.89k | } |
718 | 5.89k | for (const std::string &flag: longFlags) |
719 | 5.89k | { |
720 | 5.89k | flagStrings.emplace_back(flag); |
721 | 5.89k | } |
722 | 5.89k | return flagStrings; |
723 | 5.89k | } |
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 | 56.0k | { |
810 | 56.0k | return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs)); |
811 | 56.0k | } |
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 | 28.0k | Nargs(size_t num_) : min{num_}, max{num_} |
993 | 28.0k | { |
994 | 28.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.93k | Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {} |
1025 | 3.93k | virtual ~Base() {} |
1026 | | |
1027 | | Options GetOptions() const noexcept |
1028 | 28.0k | { |
1029 | 28.0k | return options; |
1030 | 28.0k | } |
1031 | | |
1032 | | bool IsRequired() const noexcept |
1033 | 37 | { |
1034 | 37 | return (GetOptions() & Options::Required) != Options::None; |
1035 | 37 | } |
1036 | | |
1037 | | virtual bool Matched() const noexcept |
1038 | 30.0k | { |
1039 | 30.0k | return matched; |
1040 | 30.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.94k | { |
1061 | 2.94k | return {}; |
1062 | 2.94k | } |
1063 | | |
1064 | | virtual bool IsGroup() const |
1065 | 75 | { |
1066 | 75 | return false; |
1067 | 75 | } |
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 | 774 | { |
1081 | 774 | return nullptr; |
1082 | 774 | } |
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 | 75 | { |
1101 | 75 | return false; |
1102 | 75 | } |
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 | 27.9k | { |
1125 | 27.9k | return (options & Options::KickOut) != Options::None; |
1126 | 27.9k | } |
1127 | | |
1128 | | virtual void Reset() noexcept |
1129 | 3.93k | { |
1130 | 3.93k | matched = false; |
1131 | | #ifdef ARGS_NOEXCEPT |
1132 | | error = Error::None; |
1133 | | errorMsg.clear(); |
1134 | | #endif |
1135 | 3.93k | } |
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.94k | NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {} |
1186 | 2.94k | 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 | 21 | { |
1235 | 21 | return name; |
1236 | 21 | } |
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 | | template <typename T> |
1282 | | std::vector<std::string> MapKeysToStrings(const T &map) |
1283 | | { |
1284 | | std::vector<std::string> res; |
1285 | | using K = typename std::decay<decltype(std::begin(map)->first)>::type; |
1286 | | if (IsConvertableToString<K>::value) |
1287 | | { |
1288 | | for (const auto &p : map) |
1289 | | { |
1290 | | res.push_back(detail::ToString(p.first)); |
1291 | | } |
1292 | | |
1293 | | std::sort(res.begin(), res.end()); |
1294 | | } |
1295 | | return res; |
1296 | | } |
1297 | | } |
1298 | | |
1299 | | /** Base class for all flag options |
1300 | | */ |
1301 | | class FlagBase : public NamedBase |
1302 | | { |
1303 | | protected: |
1304 | | const Matcher matcher; |
1305 | | |
1306 | | virtual std::string GetNameString(const HelpParams ¶ms) const override |
1307 | 0 | { |
1308 | 0 | const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name(); |
1309 | 0 | std::string flags; |
1310 | 0 | const auto flagStrings = matcher.GetFlagStrings(); |
1311 | 0 | const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce; |
1312 | 0 | for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it) |
1313 | 0 | { |
1314 | 0 | auto &flag = *it; |
1315 | 0 | if (it != flagStrings.begin()) |
1316 | 0 | { |
1317 | 0 | flags += ", "; |
1318 | 0 | } |
1319 | |
|
1320 | 0 | flags += flag.isShort ? params.shortPrefix : params.longPrefix; |
1321 | 0 | flags += flag.str(); |
1322 | |
|
1323 | 0 | if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) |
1324 | 0 | { |
1325 | 0 | flags += flag.isShort ? params.shortSeparator : params.longSeparator; |
1326 | 0 | flags += params.valueOpen + postfix + params.valueClose; |
1327 | 0 | } |
1328 | 0 | } |
1329 | |
|
1330 | 0 | return flags; |
1331 | 0 | } |
1332 | | |
1333 | | public: |
1334 | 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_)) {} |
1335 | | |
1336 | 2.94k | FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {} |
1337 | | |
1338 | 2.94k | virtual ~FlagBase() {} |
1339 | | |
1340 | | virtual bool IsFlag() const override |
1341 | 2.94k | { |
1342 | 2.94k | return true; |
1343 | 2.94k | } |
1344 | | |
1345 | | virtual FlagBase *Match(const EitherFlag &flag) override |
1346 | 84.4k | { |
1347 | 84.4k | if (matcher.Match(flag)) |
1348 | 28.0k | { |
1349 | 28.0k | if ((GetOptions() & Options::Single) != Options::None && matched) |
1350 | 0 | { |
1351 | 0 | std::ostringstream problem; |
1352 | 0 | problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once"; |
1353 | | #ifdef ARGS_NOEXCEPT |
1354 | | error = Error::Extra; |
1355 | | errorMsg = problem.str(); |
1356 | | #else |
1357 | 0 | throw ExtraError(problem.str()); |
1358 | 0 | #endif |
1359 | 0 | } |
1360 | 28.0k | matched = true; |
1361 | 28.0k | return this; |
1362 | 28.0k | } |
1363 | 56.4k | return nullptr; |
1364 | 84.4k | } |
1365 | | |
1366 | | virtual std::vector<FlagBase*> GetAllFlags() override |
1367 | 0 | { |
1368 | 0 | return { this }; |
1369 | 0 | } |
1370 | | |
1371 | | const Matcher &GetMatcher() const |
1372 | 5.89k | { |
1373 | 5.89k | return matcher; |
1374 | 5.89k | } |
1375 | | |
1376 | | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override |
1377 | 75 | { |
1378 | 75 | if (!Matched() && IsRequired()) |
1379 | 0 | { |
1380 | 0 | std::ostringstream problem; |
1381 | 0 | problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required"; |
1382 | | #ifdef ARGS_NOEXCEPT |
1383 | | error = Error::Required; |
1384 | | errorMsg = problem.str(); |
1385 | | #else |
1386 | 0 | throw RequiredError(problem.str()); |
1387 | 0 | #endif |
1388 | 0 | } |
1389 | 75 | } |
1390 | | |
1391 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1392 | 0 | { |
1393 | 0 | if (!params.proglineShowFlags) |
1394 | 0 | { |
1395 | 0 | return {}; |
1396 | 0 | } |
1397 | | |
1398 | 0 | const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name(); |
1399 | 0 | const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny(); |
1400 | 0 | std::string res = flag.str(params.shortPrefix, params.longPrefix); |
1401 | 0 | if (!postfix.empty()) |
1402 | 0 | { |
1403 | 0 | res += params.proglineValueOpen + postfix + params.proglineValueClose; |
1404 | 0 | } |
1405 | |
|
1406 | 0 | return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose |
1407 | 0 | : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose }; |
1408 | 0 | } |
1409 | | |
1410 | | virtual bool HasFlag() const override |
1411 | 0 | { |
1412 | 0 | return true; |
1413 | 0 | } |
1414 | | |
1415 | | #ifdef ARGS_NOEXCEPT |
1416 | | /// Only for ARGS_NOEXCEPT |
1417 | | bool usageError = false; |
1418 | | void SetUsageError() |
1419 | | { |
1420 | | usageError = true; |
1421 | | } |
1422 | | void ClearUsageError() |
1423 | | { |
1424 | | usageError = false; |
1425 | | } |
1426 | | virtual Error GetError() const override |
1427 | | { |
1428 | | if(usageError) |
1429 | | { |
1430 | | return Error::Usage; |
1431 | | } |
1432 | | const auto nargs = NumberOfArguments(); |
1433 | | if (nargs.min > nargs.max) |
1434 | | { |
1435 | | return Error::Usage; |
1436 | | } |
1437 | | |
1438 | | const auto matcherError = matcher.GetError(); |
1439 | | if (matcherError != Error::None) |
1440 | | { |
1441 | | return matcherError; |
1442 | | } |
1443 | | |
1444 | | return error; |
1445 | | } |
1446 | | #endif |
1447 | | |
1448 | | /** Defines how many values can be consumed by this option. |
1449 | | * |
1450 | | * \return closed interval [min, max] |
1451 | | */ |
1452 | | virtual Nargs NumberOfArguments() const noexcept = 0; |
1453 | | |
1454 | | /** Parse values of this option. |
1455 | | * |
1456 | | * \param value Vector of values. It's size must be in NumberOfArguments() interval. |
1457 | | */ |
1458 | | virtual void ParseValue(const std::vector<std::string> &value) = 0; |
1459 | | }; |
1460 | | |
1461 | | /** Base class for value-accepting flag options |
1462 | | */ |
1463 | | class ValueFlagBase : public FlagBase |
1464 | | { |
1465 | | public: |
1466 | 0 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {} |
1467 | 0 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {} |
1468 | 0 | virtual ~ValueFlagBase() {} |
1469 | | |
1470 | | virtual Nargs NumberOfArguments() const noexcept override |
1471 | 0 | { |
1472 | 0 | return 1; |
1473 | 0 | } |
1474 | | }; |
1475 | | |
1476 | | class CompletionFlag : public ValueFlagBase |
1477 | | { |
1478 | | public: |
1479 | | std::vector<std::string> reply; |
1480 | | size_t cword = 0; |
1481 | | std::string syntax; |
1482 | | |
1483 | | template <typename GroupClass> |
1484 | | CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden) |
1485 | | { |
1486 | | group_.AddCompletion(*this); |
1487 | | } |
1488 | | |
1489 | 0 | virtual ~CompletionFlag() {} |
1490 | | |
1491 | | virtual Nargs NumberOfArguments() const noexcept override |
1492 | 0 | { |
1493 | 0 | return 2; |
1494 | 0 | } |
1495 | | |
1496 | | virtual void ParseValue(const std::vector<std::string> &value_) override |
1497 | 0 | { |
1498 | 0 | syntax = value_.at(0); |
1499 | 0 | const std::string &raw = value_.at(1); |
1500 | 0 | bool failed = false; |
1501 | 0 |
|
1502 | 0 | const auto firstNonSpace = std::find_if_not(raw.begin(), raw.end(), [](char c) |
1503 | 0 | { |
1504 | 0 | return std::isspace(static_cast<unsigned char>(c)) != 0; |
1505 | 0 | }); |
1506 | 0 |
|
1507 | 0 | // Reject explicit signs: cword must be a plain non-negative |
1508 | 0 | // decimal index. istringstream would otherwise silently |
1509 | 0 | // accept "+1". |
1510 | 0 | if (firstNonSpace != raw.end() && (*firstNonSpace == '-' || *firstNonSpace == '+')) |
1511 | 0 | { |
1512 | 0 | failed = true; |
1513 | 0 | } |
1514 | 0 |
|
1515 | 0 | size_t parsed = 0; |
1516 | 0 | if (!failed) |
1517 | 0 | { |
1518 | 0 | std::istringstream ss(raw); |
1519 | 0 | // Use the C locale so that the cword index parses |
1520 | 0 | // consistently regardless of any std::locale::global call |
1521 | 0 | // elsewhere in the process. A locale with a non-empty |
1522 | 0 | // grouping facet would otherwise reject digit-only inputs |
1523 | 0 | // like "12" when grouping rules expect separators. |
1524 | 0 | ss.imbue(std::locale::classic()); |
1525 | 0 | ss >> parsed; |
1526 | 0 | if (ss.fail()) |
1527 | 0 | { |
1528 | 0 | failed = true; |
1529 | 0 | } |
1530 | 0 | else |
1531 | 0 | { |
1532 | 0 | char extra; |
1533 | 0 | if (ss >> extra) |
1534 | 0 | { |
1535 | 0 | failed = true; |
1536 | 0 | } |
1537 | 0 | else if (!ss.eof()) |
1538 | 0 | { |
1539 | 0 | failed = true; |
1540 | 0 | } |
1541 | 0 | } |
1542 | 0 | } |
1543 | 0 |
|
1544 | 0 | if (failed) |
1545 | 0 | { |
1546 | 0 | #ifdef ARGS_NOEXCEPT |
1547 | 0 | error = Error::Parse; |
1548 | 0 | errorMsg = "Argument 'completion' received invalid value type '" + raw + "'"; |
1549 | 0 | #else |
1550 | 0 | std::ostringstream problem; |
1551 | 0 | problem << "Argument 'completion' received invalid value type '" << raw << "'"; |
1552 | 0 | throw ParseError(problem.str()); |
1553 | 0 | #endif |
1554 | 0 | return; |
1555 | 0 | } |
1556 | 0 |
|
1557 | 0 | cword = parsed; |
1558 | 0 | } |
1559 | | |
1560 | | /** Get the completion reply |
1561 | | */ |
1562 | | std::string Get() noexcept |
1563 | 0 | { |
1564 | 0 | return detail::Join(reply, "\n"); |
1565 | 0 | } |
1566 | | |
1567 | | virtual void Reset() noexcept override |
1568 | 0 | { |
1569 | 0 | ValueFlagBase::Reset(); |
1570 | 0 | cword = 0; |
1571 | 0 | syntax.clear(); |
1572 | 0 | reply.clear(); |
1573 | 0 | } |
1574 | | }; |
1575 | | |
1576 | | |
1577 | | /** Base class for positional options |
1578 | | */ |
1579 | | class PositionalBase : public NamedBase |
1580 | | { |
1581 | | protected: |
1582 | | bool ready; |
1583 | | |
1584 | | public: |
1585 | 0 | PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {} |
1586 | 0 | virtual ~PositionalBase() {} |
1587 | | |
1588 | | bool Ready() |
1589 | 0 | { |
1590 | 0 | return ready; |
1591 | 0 | } |
1592 | | |
1593 | | virtual void ParseValue(const std::string &value_) = 0; |
1594 | | |
1595 | | virtual void Reset() noexcept override |
1596 | 0 | { |
1597 | 0 | matched = false; |
1598 | 0 | ready = true; |
1599 | 0 | #ifdef ARGS_NOEXCEPT |
1600 | 0 | error = Error::None; |
1601 | 0 | errorMsg.clear(); |
1602 | 0 | #endif |
1603 | 0 | } |
1604 | | |
1605 | | virtual PositionalBase *GetNextPositional() override |
1606 | 0 | { |
1607 | 0 | return Ready() ? this : nullptr; |
1608 | 0 | } |
1609 | | |
1610 | | virtual bool HasPositional() const override |
1611 | 0 | { |
1612 | 0 | return true; |
1613 | 0 | } |
1614 | | |
1615 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1616 | 0 | { |
1617 | 0 | return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose |
1618 | 0 | : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose }; |
1619 | 0 | } |
1620 | | |
1621 | | virtual void Validate(const std::string &, const std::string &) const override |
1622 | 0 | { |
1623 | 0 | if (IsRequired() && !Matched()) |
1624 | 0 | { |
1625 | 0 | std::ostringstream problem; |
1626 | 0 | problem << "Option '" << Name() << "' is required"; |
1627 | 0 | #ifdef ARGS_NOEXCEPT |
1628 | 0 | error = Error::Required; |
1629 | 0 | errorMsg = problem.str(); |
1630 | 0 | #else |
1631 | 0 | throw RequiredError(problem.str()); |
1632 | 0 | #endif |
1633 | 0 | } |
1634 | 0 | } |
1635 | | }; |
1636 | | |
1637 | | /** Class for all kinds of validating groups, including ArgumentParser |
1638 | | */ |
1639 | | class Group : public Base |
1640 | | { |
1641 | | private: |
1642 | | Group* parent; |
1643 | | std::vector<Base*> children; |
1644 | | std::function<bool(const Group &)> validator; |
1645 | | |
1646 | | public: |
1647 | | /** Default validators |
1648 | | */ |
1649 | | struct Validators |
1650 | | { |
1651 | | static bool Xor(const Group &group) |
1652 | 0 | { |
1653 | 0 | return group.MatchedChildren() == 1; |
1654 | 0 | } |
1655 | | |
1656 | | static bool AtLeastOne(const Group &group) |
1657 | 0 | { |
1658 | 0 | return group.MatchedChildren() >= 1; |
1659 | 0 | } |
1660 | | |
1661 | | static bool AtMostOne(const Group &group) |
1662 | 0 | { |
1663 | 0 | return group.MatchedChildren() <= 1; |
1664 | 0 | } |
1665 | | |
1666 | | static bool All(const Group &group) |
1667 | 0 | { |
1668 | 0 | return group.Children().size() == group.MatchedChildren(); |
1669 | 0 | } |
1670 | | |
1671 | | static bool AllOrNone(const Group &group) |
1672 | 0 | { |
1673 | 0 | return (All(group) || None(group)); |
1674 | 0 | } |
1675 | | |
1676 | | static bool AllChildGroups(const Group &group) |
1677 | 0 | { |
1678 | 0 | return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool { |
1679 | 0 | return child->IsGroup() && !child->Matched(); |
1680 | 0 | }); |
1681 | 0 | } |
1682 | | |
1683 | | static bool DontCare(const Group &) |
1684 | 0 | { |
1685 | 0 | return true; |
1686 | 0 | } |
1687 | | |
1688 | | static bool CareTooMuch(const Group &) |
1689 | 0 | { |
1690 | 0 | return false; |
1691 | 0 | } |
1692 | | |
1693 | | static bool None(const Group &group) |
1694 | 0 | { |
1695 | 0 | return group.MatchedChildren() == 0; |
1696 | 0 | } |
1697 | | }; |
1698 | | /// If help is empty, this group will not be printed in help output |
1699 | 983 | Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) |
1700 | 983 | { |
1701 | 983 | parent = nullptr; |
1702 | 983 | } |
1703 | | /// If help is empty, this group will not be printed in help output |
1704 | | 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_) |
1705 | 0 | { |
1706 | 0 | group_.Add(*this); |
1707 | 0 | parent = &group_; |
1708 | 0 | } |
1709 | 983 | virtual ~Group() {} |
1710 | | |
1711 | | /** Append a child to this Group. |
1712 | | */ |
1713 | | void Add(Base &child) |
1714 | 2.94k | { |
1715 | 2.94k | children.emplace_back(&child); |
1716 | | |
1717 | 2.94k | if(child.IsFlag()) { |
1718 | 2.94k | #ifndef ARGS_NOEXCEPT |
1719 | | // Detection runs from the child's own constructor, so a |
1720 | | // duplicate throws before that constructor completes and the |
1721 | | // child's storage is released while the stack unwinds. Undo |
1722 | | // the registration first, or a caller that catches the error |
1723 | | // leaves this group holding a pointer to a dead object. |
1724 | 2.94k | try |
1725 | 2.94k | { |
1726 | 2.94k | SignalDetectDuplicates(); |
1727 | 2.94k | } |
1728 | 2.94k | catch (...) |
1729 | 2.94k | { |
1730 | 0 | children.pop_back(); |
1731 | 0 | throw; |
1732 | 0 | } |
1733 | | #else |
1734 | | SignalDetectDuplicates(); |
1735 | | #endif |
1736 | 2.94k | } |
1737 | 2.94k | } |
1738 | | |
1739 | | /** Get all this group's children |
1740 | | */ |
1741 | | const std::vector<Base *> &Children() const |
1742 | 33.9k | { |
1743 | 33.9k | return children; |
1744 | 33.9k | } |
1745 | | |
1746 | | /** Return the first FlagBase that matches flag, or nullptr |
1747 | | * |
1748 | | * \param flag The flag with prefixes stripped |
1749 | | * \return the first matching FlagBase pointer, or nullptr if there is no match |
1750 | | */ |
1751 | | virtual FlagBase *Match(const EitherFlag &flag) override |
1752 | 28.6k | { |
1753 | 28.6k | for (Base *child: Children()) |
1754 | 84.4k | { |
1755 | 84.4k | if (FlagBase *match = child->Match(flag)) |
1756 | 28.0k | { |
1757 | 28.0k | return match; |
1758 | 28.0k | } |
1759 | 84.4k | } |
1760 | 662 | return nullptr; |
1761 | 28.6k | } |
1762 | | |
1763 | | virtual std::vector<FlagBase*> GetAllFlags() override |
1764 | 0 | { |
1765 | 0 | std::vector<FlagBase*> res; |
1766 | 0 | for (Base *child: Children()) |
1767 | 0 | { |
1768 | 0 | auto childRes = child->GetAllFlags(); |
1769 | 0 | res.insert(res.end(), childRes.begin(), childRes.end()); |
1770 | 0 | } |
1771 | 0 | return res; |
1772 | 0 | } |
1773 | | |
1774 | | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override |
1775 | 0 | { |
1776 | 0 | for (Base *child: Children()) |
1777 | 0 | { |
1778 | 0 | child->Validate(shortPrefix, longPrefix); |
1779 | 0 | } |
1780 | 0 | } |
1781 | | |
1782 | | /** Get the next ready positional, or nullptr if there is none |
1783 | | * |
1784 | | * \return the first ready PositionalBase pointer, or nullptr if there is no match |
1785 | | */ |
1786 | | virtual PositionalBase *GetNextPositional() override |
1787 | 258 | { |
1788 | 258 | for (Base *child: Children()) |
1789 | 774 | { |
1790 | 774 | if (auto next = child->GetNextPositional()) |
1791 | 0 | { |
1792 | 0 | return next; |
1793 | 0 | } |
1794 | 774 | } |
1795 | 258 | return nullptr; |
1796 | 258 | } |
1797 | | |
1798 | | /** Get whether this has any FlagBase children |
1799 | | * |
1800 | | * \return Whether or not there are any FlagBase children |
1801 | | */ |
1802 | | virtual bool HasFlag() const override |
1803 | 0 | { |
1804 | 0 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); }); |
1805 | 0 | } |
1806 | | |
1807 | | /** Get whether this has any PositionalBase children |
1808 | | * |
1809 | | * \return Whether or not there are any PositionalBase children |
1810 | | */ |
1811 | | virtual bool HasPositional() const override |
1812 | 0 | { |
1813 | 0 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); }); |
1814 | 0 | } |
1815 | | |
1816 | | /** Get whether this has any Command children |
1817 | | * |
1818 | | * \return Whether or not there are any Command children |
1819 | | */ |
1820 | | virtual bool HasCommand() const override |
1821 | 25 | { |
1822 | 75 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); }); |
1823 | 25 | } |
1824 | | |
1825 | | /** Count the number of matched children this group has |
1826 | | */ |
1827 | | std::vector<Base *>::size_type MatchedChildren() const |
1828 | 0 | { |
1829 | 0 | // Cast to avoid warnings from -Wsign-conversion |
1830 | 0 | return static_cast<std::vector<Base *>::size_type>( |
1831 | 0 | std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();})); |
1832 | 0 | } |
1833 | | |
1834 | | /** Get the list of children which were matched |
1835 | | */ |
1836 | | std::vector<Base *> GetMatchedChildren() const |
1837 | 0 | { |
1838 | 0 | // Could be replaced by C++ 20 filter, or a custom iterator. |
1839 | 0 | std::vector<Base*> matched_children; |
1840 | 0 | std::copy_if(children.begin(), children.end(), std::back_inserter(matched_children), [](Base* b){ |
1841 | 0 | return b->Matched(); |
1842 | 0 | }); |
1843 | 0 | return matched_children; |
1844 | 0 | } |
1845 | | |
1846 | | /** Gets the children which are a certain type. |
1847 | | * \tparam ChildType The type of child to select. |
1848 | | * \param matching Return only children of the type which matched (default false). |
1849 | | * \return Vector of children meeting the criteria. |
1850 | | */ |
1851 | | template <typename ChildType> |
1852 | | std::vector<ChildType *> GetFilteredChildren(bool matching = false) const |
1853 | | { |
1854 | | std::vector<ChildType *> filtered_children; |
1855 | | for(Base *child : children) { |
1856 | | if(!matching || child->Matched()) |
1857 | | { |
1858 | | ChildType* cast_result = dynamic_cast<ChildType*>(child); |
1859 | | if(cast_result != nullptr) |
1860 | | { |
1861 | | filtered_children.push_back(cast_result); |
1862 | | } |
1863 | | |
1864 | | } |
1865 | | } |
1866 | | return filtered_children; |
1867 | | } |
1868 | | |
1869 | | /** Whether or not this group matches validation |
1870 | | */ |
1871 | | virtual bool Matched() const noexcept override |
1872 | 0 | { |
1873 | 0 | return validator(*this); |
1874 | 0 | } |
1875 | | |
1876 | | /** Get validation |
1877 | | */ |
1878 | | bool Get() const |
1879 | 0 | { |
1880 | 0 | return Matched(); |
1881 | 0 | } |
1882 | | |
1883 | | /** Get all the child descriptions for help generation |
1884 | | */ |
1885 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override |
1886 | 0 | { |
1887 | 0 | std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions; |
1888 | | |
1889 | | // Push that group description on the back if not empty |
1890 | 0 | unsigned addindent = 0; |
1891 | 0 | if (!help.empty()) |
1892 | 0 | { |
1893 | 0 | descriptions.emplace_back(help, "", indent); |
1894 | 0 | addindent = 1; |
1895 | 0 | } |
1896 | |
|
1897 | 0 | for (Base *child: Children()) |
1898 | 0 | { |
1899 | 0 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) |
1900 | 0 | { |
1901 | 0 | continue; |
1902 | 0 | } |
1903 | | |
1904 | 0 | auto groupDescriptions = child->GetDescription(params, indent + addindent); |
1905 | 0 | descriptions.insert( |
1906 | 0 | std::end(descriptions), |
1907 | 0 | std::make_move_iterator(std::begin(groupDescriptions)), |
1908 | 0 | std::make_move_iterator(std::end(groupDescriptions))); |
1909 | 0 | } |
1910 | 0 | return descriptions; |
1911 | 0 | } |
1912 | | |
1913 | | /** Get the names of positional parameters |
1914 | | */ |
1915 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1916 | 0 | { |
1917 | 0 | std::vector <std::string> names; |
1918 | 0 | for (Base *child: Children()) |
1919 | 0 | { |
1920 | 0 | if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None) |
1921 | 0 | { |
1922 | 0 | continue; |
1923 | 0 | } |
1924 | | |
1925 | 0 | auto groupNames = child->GetProgramLine(params); |
1926 | 0 | names.insert( |
1927 | 0 | std::end(names), |
1928 | 0 | std::make_move_iterator(std::begin(groupNames)), |
1929 | 0 | std::make_move_iterator(std::end(groupNames))); |
1930 | 0 | } |
1931 | 0 | return names; |
1932 | 0 | } |
1933 | | |
1934 | | virtual std::vector<Command*> GetCommands() override |
1935 | 983 | { |
1936 | 983 | std::vector<Command*> res; |
1937 | 983 | for (const auto &child : Children()) |
1938 | 2.94k | { |
1939 | 2.94k | auto subparsers = child->GetCommands(); |
1940 | 2.94k | res.insert(std::end(res), std::begin(subparsers), std::end(subparsers)); |
1941 | 2.94k | } |
1942 | 983 | return res; |
1943 | 983 | } |
1944 | | |
1945 | | virtual bool IsGroup() const override |
1946 | 0 | { |
1947 | 0 | return true; |
1948 | 0 | } |
1949 | | |
1950 | | virtual void Reset() noexcept override |
1951 | 983 | { |
1952 | 983 | Base::Reset(); |
1953 | | |
1954 | 983 | for (auto &child: Children()) |
1955 | 2.94k | { |
1956 | 2.94k | child->Reset(); |
1957 | 2.94k | } |
1958 | | #ifdef ARGS_NOEXCEPT |
1959 | | error = Error::None; |
1960 | | errorMsg.clear(); |
1961 | | #endif |
1962 | 983 | } |
1963 | | |
1964 | | /** Sends a signal to the root of the tree to begin checking for |
1965 | | * duplicates. If this is the root, begins checking. |
1966 | | */ |
1967 | | void SignalDetectDuplicates() |
1968 | 2.94k | { |
1969 | 2.94k | if(parent != nullptr) parent->SignalDetectDuplicates(); |
1970 | 2.94k | else DetectDuplicateFlags(); |
1971 | 2.94k | } |
1972 | | |
1973 | | /** Detect duplicate flags. |
1974 | | * In a noexcept context, sets an error on the duplicate flag. |
1975 | | * In a normal context, throws a ParseError. |
1976 | | */ |
1977 | | void DetectDuplicateFlags() |
1978 | 2.94k | { |
1979 | 2.94k | std::unordered_set<char> usedShortFlags; |
1980 | 2.94k | std::unordered_set<std::string> usedLongFlags; |
1981 | 2.94k | DetectDuplicateFlags(usedShortFlags, usedLongFlags); |
1982 | 2.94k | } |
1983 | | |
1984 | | /** Used by parameterless DetectDuplicateFlags. |
1985 | | */ |
1986 | | void DetectDuplicateFlags(std::unordered_set<char> &usedShortFlags, std::unordered_set<std::string> &usedLongFlags) |
1987 | 2.94k | { |
1988 | 2.94k | for (Base *child: Children()) |
1989 | 5.89k | { |
1990 | 5.89k | if(auto flag = dynamic_cast<FlagBase*>(child)) |
1991 | 5.89k | { |
1992 | | // Check for duplicate flags, setting a usage error on the |
1993 | | // flag if a duplicate is detected. |
1994 | 5.89k | for(EitherFlag flagString: flag->GetMatcher().GetFlagStrings()) |
1995 | 11.7k | { |
1996 | 11.7k | if(flagString.isShort) |
1997 | 5.89k | { |
1998 | 5.89k | if(usedShortFlags.count(flagString.shortFlag)) |
1999 | 0 | { |
2000 | | #ifdef ARGS_NOEXCEPT |
2001 | | flag->SetUsageError(); |
2002 | | #else |
2003 | 0 | throw ParseError("duplicate short flag detected"); |
2004 | 0 | #endif |
2005 | 0 | } |
2006 | 5.89k | else |
2007 | 5.89k | { |
2008 | 5.89k | usedShortFlags.insert(flagString.shortFlag); |
2009 | 5.89k | } |
2010 | 5.89k | } |
2011 | 5.89k | else |
2012 | 5.89k | { |
2013 | 5.89k | if(usedLongFlags.count(flagString.longFlag)) |
2014 | 0 | { |
2015 | | #ifdef ARGS_NOEXCEPT |
2016 | | flag->SetUsageError(); |
2017 | | #else |
2018 | 0 | throw ParseError("duplicate long flag detected"); |
2019 | 0 | #endif |
2020 | 0 | } |
2021 | 5.89k | else |
2022 | 5.89k | { |
2023 | 5.89k | usedLongFlags.insert(flagString.longFlag); |
2024 | 5.89k | } |
2025 | 5.89k | } |
2026 | 11.7k | } |
2027 | 5.89k | } |
2028 | 0 | else if(auto group = dynamic_cast<Group*>(child)) |
2029 | 0 | { |
2030 | | // A command opens its own flag namespace and runs its |
2031 | | // own duplicate detection as a separate root, so a flag |
2032 | | // reused either side of a command boundary is not a |
2033 | | // genuine duplicate. Only descend into plain groups |
2034 | | // here; IsGroup() is false for a Command. |
2035 | 0 | if(group->IsGroup()) |
2036 | 0 | { |
2037 | 0 | group->DetectDuplicateFlags(usedShortFlags, usedLongFlags); |
2038 | 0 | } |
2039 | 0 | } |
2040 | 5.89k | } |
2041 | 2.94k | } |
2042 | | |
2043 | | #ifdef ARGS_NOEXCEPT |
2044 | | /// Only for ARGS_NOEXCEPT |
2045 | | virtual Error GetError() const override |
2046 | | { |
2047 | | if (error != Error::None) |
2048 | | { |
2049 | | return error; |
2050 | | } |
2051 | | |
2052 | | auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); |
2053 | | if (it == Children().end()) |
2054 | | { |
2055 | | return Error::None; |
2056 | | } else |
2057 | | { |
2058 | | return (*it)->GetError(); |
2059 | | } |
2060 | | } |
2061 | | |
2062 | | /// Only for ARGS_NOEXCEPT |
2063 | | virtual std::string GetErrorMsg() const override |
2064 | | { |
2065 | | if (error != Error::None) |
2066 | | { |
2067 | | return errorMsg; |
2068 | | } |
2069 | | |
2070 | | auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); |
2071 | | if (it == Children().end()) |
2072 | | { |
2073 | | return ""; |
2074 | | } else |
2075 | | { |
2076 | | return (*it)->GetErrorMsg(); |
2077 | | } |
2078 | | } |
2079 | | #endif |
2080 | | |
2081 | | }; |
2082 | | |
2083 | | /** Class for using global options in ArgumentParser. |
2084 | | */ |
2085 | | class GlobalOptions : public Group |
2086 | | { |
2087 | | public: |
2088 | | GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global) |
2089 | 0 | { |
2090 | 0 | Add(options_); |
2091 | 0 | } |
2092 | | }; |
2093 | | |
2094 | | /** Utility class for building subparsers with coroutines/callbacks. |
2095 | | * |
2096 | | * Brief example: |
2097 | | * \code |
2098 | | * Command command(argumentParser, "command", "my command", [](args::Subparser &s) |
2099 | | * { |
2100 | | * // your command flags/positionals |
2101 | | * s.Parse(); //required |
2102 | | * //your command code |
2103 | | * }); |
2104 | | * \endcode |
2105 | | * |
2106 | | * For ARGS_NOEXCEPT mode don't forget to check `s.GetError()` after `s.Parse()` |
2107 | | * and return if it isn't equals to args::Error::None. |
2108 | | * |
2109 | | * \sa Command |
2110 | | */ |
2111 | | class Subparser : public Group |
2112 | | { |
2113 | | private: |
2114 | | std::vector<std::string> args; |
2115 | | std::vector<std::string> kicked; |
2116 | | ArgumentParser *parser = nullptr; |
2117 | | const HelpParams &helpParams; |
2118 | | const Command &command; |
2119 | | bool isParsed = false; |
2120 | | |
2121 | | public: |
2122 | | Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_) |
2123 | 0 | : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_) |
2124 | 0 | { |
2125 | 0 | } |
2126 | | |
2127 | 0 | Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_) |
2128 | 0 | { |
2129 | 0 | } |
2130 | | |
2131 | | Subparser(const Subparser&) = delete; |
2132 | | Subparser(Subparser&&) = delete; |
2133 | | Subparser &operator = (const Subparser&) = delete; |
2134 | | Subparser &operator = (Subparser&&) = delete; |
2135 | | |
2136 | | const Command &GetCommand() |
2137 | 0 | { |
2138 | 0 | return command; |
2139 | 0 | } |
2140 | | |
2141 | | /** (INTERNAL) Determines whether Parse was called or not. |
2142 | | */ |
2143 | | bool IsParsed() const |
2144 | 0 | { |
2145 | 0 | return isParsed; |
2146 | 0 | } |
2147 | | |
2148 | | /** Continue parsing arguments for new command. |
2149 | | */ |
2150 | | void Parse(); |
2151 | | |
2152 | | /** Returns a vector of kicked out arguments. |
2153 | | * |
2154 | | * \sa Base::KickOut |
2155 | | */ |
2156 | | const std::vector<std::string> &KickedOut() const noexcept |
2157 | 0 | { |
2158 | 0 | return kicked; |
2159 | 0 | } |
2160 | | }; |
2161 | | |
2162 | | /** Main class for building subparsers. |
2163 | | * |
2164 | | * /sa Subparser |
2165 | | */ |
2166 | | class Command : public Group |
2167 | | { |
2168 | | private: |
2169 | | friend class Subparser; |
2170 | | |
2171 | | std::string name; |
2172 | | std::string help; |
2173 | | std::string description; |
2174 | | std::string epilog; |
2175 | | std::string proglinePostfix; |
2176 | | |
2177 | | std::function<void(Subparser&)> parserCoroutine; |
2178 | | bool commandIsRequired = true; |
2179 | | Command *selectedCommand = nullptr; |
2180 | | |
2181 | | mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription; |
2182 | | mutable std::vector<std::string> subparserProgramLine; |
2183 | | mutable bool subparserHasFlag = false; |
2184 | | mutable bool subparserHasPositional = false; |
2185 | | mutable bool subparserHasCommand = false; |
2186 | | #ifdef ARGS_NOEXCEPT |
2187 | | mutable Error subparserError = Error::None; |
2188 | | #endif |
2189 | | mutable Subparser *subparser = nullptr; |
2190 | | |
2191 | | protected: |
2192 | | |
2193 | | class RaiiSubparser |
2194 | | { |
2195 | | public: |
2196 | | RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_); |
2197 | | RaiiSubparser(const Command &command_, const HelpParams ¶ms_); |
2198 | | |
2199 | | ~RaiiSubparser() |
2200 | 0 | { |
2201 | 0 | command.subparser = oldSubparser; |
2202 | 0 | } |
2203 | | |
2204 | | Subparser &Parser() |
2205 | 0 | { |
2206 | 0 | return parser; |
2207 | 0 | } |
2208 | | |
2209 | | private: |
2210 | | const Command &command; |
2211 | | Subparser parser; |
2212 | | Subparser *oldSubparser; |
2213 | | }; |
2214 | | |
2215 | 983 | Command() = default; |
2216 | | |
2217 | | std::function<void(Subparser&)> &GetCoroutine() |
2218 | 0 | { |
2219 | 0 | return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine; |
2220 | 0 | } |
2221 | | |
2222 | | Command &SelectedCommand() |
2223 | 0 | { |
2224 | 0 | Command *res = this; |
2225 | 0 | while (res->selectedCommand != nullptr) |
2226 | 0 | { |
2227 | 0 | res = res->selectedCommand; |
2228 | 0 | } |
2229 | |
|
2230 | 0 | return *res; |
2231 | 0 | } |
2232 | | |
2233 | | const Command &SelectedCommand() const |
2234 | 0 | { |
2235 | 0 | const Command *res = this; |
2236 | 0 | while (res->selectedCommand != nullptr) |
2237 | 0 | { |
2238 | 0 | res = res->selectedCommand; |
2239 | 0 | } |
2240 | 0 |
|
2241 | 0 | return *res; |
2242 | 0 | } |
2243 | | |
2244 | | void UpdateSubparserHelp(const HelpParams ¶ms) const |
2245 | 0 | { |
2246 | 0 | if (parserCoroutine) |
2247 | 0 | { |
2248 | 0 | RaiiSubparser coro(*this, params); |
2249 | 0 | #ifndef ARGS_NOEXCEPT |
2250 | 0 | try |
2251 | 0 | { |
2252 | 0 | parserCoroutine(coro.Parser()); |
2253 | 0 | } |
2254 | 0 | catch (args::SubparserError&) |
2255 | 0 | { |
2256 | 0 | } |
2257 | | #else |
2258 | | parserCoroutine(coro.Parser()); |
2259 | | #endif |
2260 | 0 | } |
2261 | 0 | } |
2262 | | |
2263 | | public: |
2264 | | Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {}) |
2265 | | : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_)) |
2266 | 0 | { |
2267 | 0 | base_.Add(*this); |
2268 | 0 | } |
2269 | | |
2270 | | /** The description that appears on the prog line after options |
2271 | | */ |
2272 | | const std::string &ProglinePostfix() const |
2273 | 0 | { return proglinePostfix; } |
2274 | | |
2275 | | /** The description that appears on the prog line after options |
2276 | | */ |
2277 | | void ProglinePostfix(const std::string &proglinePostfix_) |
2278 | 0 | { this->proglinePostfix = proglinePostfix_; } |
2279 | | |
2280 | | /** The description that appears above options |
2281 | | */ |
2282 | | const std::string &Description() const |
2283 | 0 | { return description; } |
2284 | | /** The description that appears above options |
2285 | | */ |
2286 | | |
2287 | | void Description(const std::string &description_) |
2288 | 983 | { this->description = description_; } |
2289 | | |
2290 | | /** The description that appears below options |
2291 | | */ |
2292 | | const std::string &Epilog() const |
2293 | 0 | { return epilog; } |
2294 | | |
2295 | | /** The description that appears below options |
2296 | | */ |
2297 | | void Epilog(const std::string &epilog_) |
2298 | 983 | { this->epilog = epilog_; } |
2299 | | |
2300 | | /** The name of command |
2301 | | */ |
2302 | | const std::string &Name() const |
2303 | 0 | { return name; } |
2304 | | |
2305 | | /** The description of command |
2306 | | */ |
2307 | | const std::string &Help() const |
2308 | 0 | { return help; } |
2309 | | |
2310 | | /** If value is true, parser will fail if no command was parsed. |
2311 | | * |
2312 | | * Default: true. |
2313 | | */ |
2314 | | void RequireCommand(bool value) |
2315 | 0 | { commandIsRequired = value; } |
2316 | | |
2317 | | virtual bool IsGroup() const override |
2318 | 0 | { return false; } |
2319 | | |
2320 | | virtual bool Matched() const noexcept override |
2321 | 29.9k | { return Base::Matched(); } |
2322 | | |
2323 | | operator bool() const noexcept |
2324 | 0 | { return Matched(); } |
2325 | | |
2326 | | void Match() noexcept |
2327 | 0 | { matched = true; } |
2328 | | |
2329 | | void SelectCommand(Command *c) noexcept |
2330 | 0 | { |
2331 | 0 | selectedCommand = c; |
2332 | |
|
2333 | 0 | if (c != nullptr) |
2334 | 0 | { |
2335 | 0 | c->Match(); |
2336 | 0 | } |
2337 | 0 | } |
2338 | | |
2339 | | virtual FlagBase *Match(const EitherFlag &flag) override |
2340 | 28.6k | { |
2341 | 28.6k | if (selectedCommand != nullptr) |
2342 | 0 | { |
2343 | 0 | if (auto *res = selectedCommand->Match(flag)) |
2344 | 0 | { |
2345 | 0 | return res; |
2346 | 0 | } |
2347 | | |
2348 | 0 | for (auto *child: Children()) |
2349 | 0 | { |
2350 | 0 | if ((child->GetOptions() & Options::Global) != Options::None) |
2351 | 0 | { |
2352 | 0 | if (auto *res = child->Match(flag)) |
2353 | 0 | { |
2354 | 0 | return res; |
2355 | 0 | } |
2356 | 0 | } |
2357 | 0 | } |
2358 | | |
2359 | 0 | return nullptr; |
2360 | 0 | } |
2361 | | |
2362 | 28.6k | if (subparser != nullptr) |
2363 | 0 | { |
2364 | 0 | return subparser->Match(flag); |
2365 | 0 | } |
2366 | | |
2367 | 28.6k | return Matched() ? Group::Match(flag) : nullptr; |
2368 | 28.6k | } |
2369 | | |
2370 | | virtual std::vector<FlagBase*> GetAllFlags() override |
2371 | 0 | { |
2372 | 0 | std::vector<FlagBase*> res; |
2373 | |
|
2374 | 0 | if (!Matched()) |
2375 | 0 | { |
2376 | 0 | return res; |
2377 | 0 | } |
2378 | | |
2379 | 0 | for (auto *child: Children()) |
2380 | 0 | { |
2381 | 0 | if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None) |
2382 | 0 | { |
2383 | 0 | auto childFlags = child->GetAllFlags(); |
2384 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
2385 | 0 | } |
2386 | 0 | } |
2387 | |
|
2388 | 0 | if (selectedCommand != nullptr) |
2389 | 0 | { |
2390 | 0 | auto childFlags = selectedCommand->GetAllFlags(); |
2391 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
2392 | 0 | } |
2393 | |
|
2394 | 0 | if (subparser != nullptr) |
2395 | 0 | { |
2396 | 0 | auto childFlags = subparser->GetAllFlags(); |
2397 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
2398 | 0 | } |
2399 | |
|
2400 | 0 | return res; |
2401 | 0 | } |
2402 | | |
2403 | | virtual PositionalBase *GetNextPositional() override |
2404 | 258 | { |
2405 | 258 | if (selectedCommand != nullptr) |
2406 | 0 | { |
2407 | 0 | if (auto *res = selectedCommand->GetNextPositional()) |
2408 | 0 | { |
2409 | 0 | return res; |
2410 | 0 | } |
2411 | | |
2412 | 0 | for (auto *child: Children()) |
2413 | 0 | { |
2414 | 0 | if ((child->GetOptions() & Options::Global) != Options::None) |
2415 | 0 | { |
2416 | 0 | if (auto *res = child->GetNextPositional()) |
2417 | 0 | { |
2418 | 0 | return res; |
2419 | 0 | } |
2420 | 0 | } |
2421 | 0 | } |
2422 | | |
2423 | 0 | return nullptr; |
2424 | 0 | } |
2425 | | |
2426 | 258 | if (subparser != nullptr) |
2427 | 0 | { |
2428 | 0 | return subparser->GetNextPositional(); |
2429 | 0 | } |
2430 | | |
2431 | 258 | return Matched() ? Group::GetNextPositional() : nullptr; |
2432 | 258 | } |
2433 | | |
2434 | | virtual bool HasFlag() const override |
2435 | 0 | { |
2436 | 0 | return subparserHasFlag || Group::HasFlag(); |
2437 | 0 | } |
2438 | | |
2439 | | virtual bool HasPositional() const override |
2440 | 0 | { |
2441 | 0 | return subparserHasPositional || Group::HasPositional(); |
2442 | 0 | } |
2443 | | |
2444 | | virtual bool HasCommand() const override |
2445 | 0 | { |
2446 | 0 | return true; |
2447 | 0 | } |
2448 | | |
2449 | | std::vector<std::string> GetCommandProgramLine(const HelpParams ¶ms) const |
2450 | 0 | { |
2451 | 0 | UpdateSubparserHelp(params); |
2452 | |
|
2453 | 0 | std::vector<std::string> res; |
2454 | |
|
2455 | 0 | if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags) |
2456 | 0 | { |
2457 | 0 | res.push_back(params.proglineOptions); |
2458 | 0 | } |
2459 | |
|
2460 | 0 | auto group_res = Group::GetProgramLine(params); |
2461 | 0 | std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res)); |
2462 | |
|
2463 | 0 | res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end()); |
2464 | |
|
2465 | 0 | if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand)) |
2466 | 0 | { |
2467 | 0 | res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]"); |
2468 | 0 | } |
2469 | |
|
2470 | 0 | if (!Name().empty()) |
2471 | 0 | { |
2472 | 0 | res.insert(res.begin(), Name()); |
2473 | 0 | } |
2474 | |
|
2475 | 0 | if (!ProglinePostfix().empty()) |
2476 | 0 | { |
2477 | 0 | std::string line; |
2478 | 0 | for (auto c : ProglinePostfix()) |
2479 | 0 | { |
2480 | 0 | if (std::isspace(static_cast<unsigned char>(c))) |
2481 | 0 | { |
2482 | 0 | if (!line.empty()) |
2483 | 0 | { |
2484 | 0 | res.push_back(line); |
2485 | 0 | line.clear(); |
2486 | 0 | } |
2487 | |
|
2488 | 0 | if (c == '\n') |
2489 | 0 | { |
2490 | 0 | res.push_back("\n"); |
2491 | 0 | } |
2492 | 0 | } |
2493 | 0 | else |
2494 | 0 | { |
2495 | 0 | line += c; |
2496 | 0 | } |
2497 | 0 | } |
2498 | |
|
2499 | 0 | if (!line.empty()) |
2500 | 0 | { |
2501 | 0 | res.push_back(line); |
2502 | 0 | } |
2503 | 0 | } |
2504 | |
|
2505 | 0 | return res; |
2506 | 0 | } |
2507 | | |
2508 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
2509 | 0 | { |
2510 | 0 | if (!Matched()) |
2511 | 0 | { |
2512 | 0 | return {}; |
2513 | 0 | } |
2514 | | |
2515 | 0 | return GetCommandProgramLine(params); |
2516 | 0 | } |
2517 | | |
2518 | | virtual std::vector<Command*> GetCommands() override |
2519 | 983 | { |
2520 | 983 | if (selectedCommand != nullptr) |
2521 | 0 | { |
2522 | 0 | return selectedCommand->GetCommands(); |
2523 | 0 | } |
2524 | | |
2525 | 983 | if (Matched()) |
2526 | 983 | { |
2527 | 983 | return Group::GetCommands(); |
2528 | 983 | } |
2529 | | |
2530 | 0 | return { this }; |
2531 | 983 | } |
2532 | | |
2533 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override |
2534 | 0 | { |
2535 | 0 | std::vector<std::tuple<std::string, std::string, unsigned>> descriptions; |
2536 | 0 | unsigned addindent = 0; |
2537 | |
|
2538 | 0 | UpdateSubparserHelp(params); |
2539 | |
|
2540 | 0 | if (!Matched()) |
2541 | 0 | { |
2542 | 0 | if (params.showCommandFullHelp) |
2543 | 0 | { |
2544 | 0 | std::ostringstream s; |
2545 | 0 | bool empty = true; |
2546 | 0 | for (const auto &progline: GetCommandProgramLine(params)) |
2547 | 0 | { |
2548 | 0 | if (!empty) |
2549 | 0 | { |
2550 | 0 | s << ' '; |
2551 | 0 | } |
2552 | 0 | else |
2553 | 0 | { |
2554 | 0 | empty = false; |
2555 | 0 | } |
2556 | |
|
2557 | 0 | s << progline; |
2558 | 0 | } |
2559 | |
|
2560 | 0 | descriptions.emplace_back(s.str(), "", indent); |
2561 | 0 | } |
2562 | 0 | else |
2563 | 0 | { |
2564 | 0 | descriptions.emplace_back(Name(), help, indent); |
2565 | 0 | } |
2566 | |
|
2567 | 0 | if (!params.showCommandChildren && !params.showCommandFullHelp) |
2568 | 0 | { |
2569 | 0 | return descriptions; |
2570 | 0 | } |
2571 | | |
2572 | 0 | addindent = 1; |
2573 | 0 | } |
2574 | | |
2575 | 0 | if (params.showCommandFullHelp && !Matched()) |
2576 | 0 | { |
2577 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2578 | 0 | descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent); |
2579 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2580 | 0 | } |
2581 | |
|
2582 | 0 | for (Base *child: Children()) |
2583 | 0 | { |
2584 | 0 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) |
2585 | 0 | { |
2586 | 0 | continue; |
2587 | 0 | } |
2588 | | |
2589 | 0 | auto groupDescriptions = child->GetDescription(params, indent + addindent); |
2590 | 0 | descriptions.insert( |
2591 | 0 | std::end(descriptions), |
2592 | 0 | std::make_move_iterator(std::begin(groupDescriptions)), |
2593 | 0 | std::make_move_iterator(std::end(groupDescriptions))); |
2594 | 0 | } |
2595 | |
|
2596 | 0 | for (auto childDescription: subparserDescription) |
2597 | 0 | { |
2598 | 0 | std::get<2>(childDescription) += indent + addindent; |
2599 | 0 | descriptions.push_back(std::move(childDescription)); |
2600 | 0 | } |
2601 | |
|
2602 | 0 | if (params.showCommandFullHelp && !Matched()) |
2603 | 0 | { |
2604 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2605 | 0 | if (!Epilog().empty()) |
2606 | 0 | { |
2607 | 0 | descriptions.emplace_back(Epilog(), "", indent + addindent); |
2608 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2609 | 0 | } |
2610 | 0 | } |
2611 | |
|
2612 | 0 | return descriptions; |
2613 | 0 | } |
2614 | | |
2615 | | virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override |
2616 | 25 | { |
2617 | 25 | if (!Matched()) |
2618 | 0 | { |
2619 | 0 | return; |
2620 | 0 | } |
2621 | | |
2622 | 25 | auto onValidationError = [&] |
2623 | 25 | { |
2624 | 0 | std::ostringstream problem; |
2625 | 0 | problem << "Group validation failed somewhere!"; |
2626 | | #ifdef ARGS_NOEXCEPT |
2627 | | error = Error::Validation; |
2628 | | errorMsg = problem.str(); |
2629 | | #else |
2630 | 0 | throw ValidationError(problem.str()); |
2631 | 0 | #endif |
2632 | 0 | }; |
2633 | | |
2634 | 25 | for (Base *child: Children()) |
2635 | 75 | { |
2636 | 75 | if (child->IsGroup() && !child->Matched()) |
2637 | 0 | { |
2638 | 0 | onValidationError(); |
2639 | 0 | } |
2640 | | |
2641 | 75 | child->Validate(shortprefix, longprefix); |
2642 | 75 | } |
2643 | | |
2644 | 25 | if (subparser != nullptr) |
2645 | 0 | { |
2646 | 0 | subparser->Validate(shortprefix, longprefix); |
2647 | 0 | if (!subparser->Matched()) |
2648 | 0 | { |
2649 | 0 | onValidationError(); |
2650 | 0 | } |
2651 | 0 | } |
2652 | | |
2653 | 25 | if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand)) |
2654 | 0 | { |
2655 | 0 | std::ostringstream problem; |
2656 | 0 | problem << "Command is required"; |
2657 | | #ifdef ARGS_NOEXCEPT |
2658 | | error = Error::Validation; |
2659 | | errorMsg = problem.str(); |
2660 | | #else |
2661 | 0 | throw ValidationError(problem.str()); |
2662 | 0 | #endif |
2663 | 0 | } |
2664 | 25 | } |
2665 | | |
2666 | | virtual void Reset() noexcept override |
2667 | 983 | { |
2668 | 983 | Group::Reset(); |
2669 | 983 | selectedCommand = nullptr; |
2670 | 983 | subparserProgramLine.clear(); |
2671 | 983 | subparserDescription.clear(); |
2672 | 983 | subparserHasFlag = false; |
2673 | 983 | subparserHasPositional = false; |
2674 | 983 | subparserHasCommand = false; |
2675 | | #ifdef ARGS_NOEXCEPT |
2676 | | subparserError = Error::None; |
2677 | | #endif |
2678 | 983 | } |
2679 | | |
2680 | | #ifdef ARGS_NOEXCEPT |
2681 | | /// Only for ARGS_NOEXCEPT |
2682 | | virtual Error GetError() const override |
2683 | | { |
2684 | | if (!Matched()) |
2685 | | { |
2686 | | return Error::None; |
2687 | | } |
2688 | | |
2689 | | if (error != Error::None) |
2690 | | { |
2691 | | return error; |
2692 | | } |
2693 | | |
2694 | | if (subparserError != Error::None) |
2695 | | { |
2696 | | return subparserError; |
2697 | | } |
2698 | | |
2699 | | return Group::GetError(); |
2700 | | } |
2701 | | #endif |
2702 | | }; |
2703 | | |
2704 | | /** The main user facing command line argument parser class |
2705 | | */ |
2706 | | class ArgumentParser : public Command |
2707 | | { |
2708 | | friend class Subparser; |
2709 | | |
2710 | | private: |
2711 | | std::string longprefix; |
2712 | | std::string shortprefix; |
2713 | | |
2714 | | std::string longseparator; |
2715 | | |
2716 | | std::string terminator; |
2717 | | |
2718 | | bool allowJoinedShortValue = true; |
2719 | | bool allowJoinedLongValue = true; |
2720 | | bool allowSeparateShortValue = true; |
2721 | | bool allowSeparateLongValue = true; |
2722 | | |
2723 | | bool readCompletion = false; |
2724 | | CompletionFlag *completion = nullptr; |
2725 | | |
2726 | | protected: |
2727 | | enum class OptionType |
2728 | | { |
2729 | | LongFlag, |
2730 | | ShortFlag, |
2731 | | Positional |
2732 | | }; |
2733 | | |
2734 | | OptionType ParseOption(const std::string &s, bool allowEmpty = false) |
2735 | 2.26k | { |
2736 | 2.26k | const bool matchesLong = s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length()); |
2737 | 2.26k | const bool matchesShort = s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length()); |
2738 | | |
2739 | | // A chunk can start with both prefixes when one is a prefix of |
2740 | | // the other, or when the long prefix is empty (every string |
2741 | | // starts with it). Resolve to the longer, more specific prefix: |
2742 | | // this keeps the default "--"/"-" preference for long flags |
2743 | | // while letting a short flag be recognised under an empty long |
2744 | | // prefix instead of being swallowed as a nameless long flag. |
2745 | 2.26k | if (matchesLong && matchesShort) |
2746 | 514 | { |
2747 | 514 | return longprefix.length() >= shortprefix.length() ? OptionType::LongFlag : OptionType::ShortFlag; |
2748 | 514 | } |
2749 | | |
2750 | 1.75k | if (matchesLong) |
2751 | 0 | { |
2752 | 0 | return OptionType::LongFlag; |
2753 | 0 | } |
2754 | | |
2755 | 1.75k | if (matchesShort) |
2756 | 1.24k | { |
2757 | 1.24k | return OptionType::ShortFlag; |
2758 | 1.24k | } |
2759 | | |
2760 | 512 | return OptionType::Positional; |
2761 | 1.75k | } |
2762 | | |
2763 | | template <typename It> |
2764 | | bool Complete(FlagBase &flag, It it, It end) |
2765 | 0 | { |
2766 | 0 | auto nextIt = it; |
2767 | 0 | if (!readCompletion || (++nextIt != end)) |
2768 | 0 | { |
2769 | 0 | return false; |
2770 | 0 | } |
2771 | | |
2772 | 0 | const auto &chunk = *it; |
2773 | 0 | for (auto &choice : flag.HelpChoices(helpParams)) |
2774 | 0 | { |
2775 | 0 | AddCompletionReply(chunk, choice); |
2776 | 0 | } |
2777 | |
|
2778 | 0 | #ifndef ARGS_NOEXCEPT |
2779 | 0 | throw Completion(completion->Get()); |
2780 | | #else |
2781 | | return true; |
2782 | | #endif |
2783 | 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*>) |
2784 | | |
2785 | | /** (INTERNAL) Parse flag's values |
2786 | | * |
2787 | | * \param arg The string to display in error message as a flag name |
2788 | | * \param[in, out] it The iterator to first value. It will point to the last value |
2789 | | * \param end The end iterator |
2790 | | * \param joinedArg Joined value (e.g. bar in --foo=bar) |
2791 | | * \param canDiscardJoined If true joined value can be parsed as flag not as a value (as in -abcd) |
2792 | | * \param[out] values The vector to store parsed arg's values |
2793 | | */ |
2794 | | template <typename It> |
2795 | | std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end, |
2796 | | const bool allowSeparate, const bool allowJoined, |
2797 | | const bool hasJoined, const std::string &joinedArg, |
2798 | | const bool canDiscardJoined, std::vector<std::string> &values) |
2799 | 28.0k | { |
2800 | 28.0k | values.clear(); |
2801 | | |
2802 | 28.0k | Nargs nargs = flag.NumberOfArguments(); |
2803 | | |
2804 | 28.0k | if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined)) |
2805 | 0 | { |
2806 | 0 | return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; |
2807 | 0 | } |
2808 | | |
2809 | 28.0k | if (hasJoined) |
2810 | 27.5k | { |
2811 | 27.5k | if (!canDiscardJoined || (allowJoined && nargs.max != 0)) |
2812 | 17 | { |
2813 | 17 | values.push_back(joinedArg); |
2814 | 17 | } |
2815 | 27.5k | } else if (!allowSeparate) |
2816 | 0 | { |
2817 | 0 | if (nargs.min != 0) |
2818 | 0 | { |
2819 | 0 | return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; |
2820 | 0 | } |
2821 | 0 | } |
2822 | | |
2823 | | // Only gather separate values when they are allowed. A joined |
2824 | | // value that was discarded rather than taken (short chunks such |
2825 | | // as -nf when joined short values are off) means this flag isn't |
2826 | | // taking an argument here, so the rest of the chunk is flags. |
2827 | 28.0k | if (allowSeparate && (!hasJoined || !values.empty())) |
2828 | 454 | { |
2829 | 454 | auto valueIt = it; |
2830 | 454 | ++valueIt; |
2831 | | |
2832 | 454 | while (valueIt != end && |
2833 | 423 | *valueIt != terminator && |
2834 | 422 | values.size() < nargs.max && |
2835 | 0 | (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional)) |
2836 | 0 | { |
2837 | 0 | if (Complete(flag, valueIt, end)) |
2838 | 0 | { |
2839 | | // Park `it` on the completion position rather than |
2840 | | // `end`. In ARGS_NOEXCEPT mode Complete returns |
2841 | | // true (no throw), so the caller's for-loop will |
2842 | | // run its ++it after we return; advancing an |
2843 | | // already-end iterator is undefined behavior and |
2844 | | // causes a subsequent out-of-bounds read of the |
2845 | | // arg vector. Since Complete only fires when |
2846 | | // ++nextIt == end, valueIt is the last element, |
2847 | | // and ++(it=valueIt) safely lands on end. |
2848 | 0 | it = valueIt; |
2849 | 0 | return ""; |
2850 | 0 | } |
2851 | | |
2852 | 0 | values.push_back(*valueIt); |
2853 | 0 | ++it; |
2854 | 0 | ++valueIt; |
2855 | 0 | } |
2856 | 454 | } |
2857 | | |
2858 | 28.0k | if (values.size() > nargs.max) |
2859 | 17 | { |
2860 | 17 | return "Passed an argument into a non-argument flag: " + arg; |
2861 | 27.9k | } else if (values.size() < nargs.min) |
2862 | 0 | { |
2863 | 0 | if (nargs.min == 1 && nargs.max == 1) |
2864 | 0 | { |
2865 | 0 | return "Flag '" + arg + "' requires an argument but received none"; |
2866 | 0 | } else if (nargs.min == 1) |
2867 | 0 | { |
2868 | 0 | return "Flag '" + arg + "' requires at least one argument but received none"; |
2869 | 0 | } else if (nargs.min != nargs.max) |
2870 | 0 | { |
2871 | 0 | return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + |
2872 | 0 | " arguments but received " + std::to_string(values.size()); |
2873 | 0 | } else |
2874 | 0 | { |
2875 | 0 | return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + |
2876 | 0 | " arguments but received " + std::to_string(values.size()); |
2877 | 0 | } |
2878 | 0 | } |
2879 | | |
2880 | 27.9k | return {}; |
2881 | 28.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 | 2799 | 28.0k | { | 2800 | 28.0k | values.clear(); | 2801 | | | 2802 | 28.0k | Nargs nargs = flag.NumberOfArguments(); | 2803 | | | 2804 | 28.0k | if (hasJoined && !allowJoined && (nargs.min != 0 || !canDiscardJoined)) | 2805 | 0 | { | 2806 | 0 | return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; | 2807 | 0 | } | 2808 | | | 2809 | 28.0k | if (hasJoined) | 2810 | 27.5k | { | 2811 | 27.5k | if (!canDiscardJoined || (allowJoined && nargs.max != 0)) | 2812 | 17 | { | 2813 | 17 | values.push_back(joinedArg); | 2814 | 17 | } | 2815 | 27.5k | } else if (!allowSeparate) | 2816 | 0 | { | 2817 | 0 | if (nargs.min != 0) | 2818 | 0 | { | 2819 | 0 | return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; | 2820 | 0 | } | 2821 | 0 | } | 2822 | | | 2823 | | // Only gather separate values when they are allowed. A joined | 2824 | | // value that was discarded rather than taken (short chunks such | 2825 | | // as -nf when joined short values are off) means this flag isn't | 2826 | | // taking an argument here, so the rest of the chunk is flags. | 2827 | 28.0k | if (allowSeparate && (!hasJoined || !values.empty())) | 2828 | 454 | { | 2829 | 454 | auto valueIt = it; | 2830 | 454 | ++valueIt; | 2831 | | | 2832 | 454 | while (valueIt != end && | 2833 | 423 | *valueIt != terminator && | 2834 | 422 | values.size() < nargs.max && | 2835 | 0 | (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional)) | 2836 | 0 | { | 2837 | 0 | if (Complete(flag, valueIt, end)) | 2838 | 0 | { | 2839 | | // Park `it` on the completion position rather than | 2840 | | // `end`. In ARGS_NOEXCEPT mode Complete returns | 2841 | | // true (no throw), so the caller's for-loop will | 2842 | | // run its ++it after we return; advancing an | 2843 | | // already-end iterator is undefined behavior and | 2844 | | // causes a subsequent out-of-bounds read of the | 2845 | | // arg vector. Since Complete only fires when | 2846 | | // ++nextIt == end, valueIt is the last element, | 2847 | | // and ++(it=valueIt) safely lands on end. | 2848 | 0 | it = valueIt; | 2849 | 0 | return ""; | 2850 | 0 | } | 2851 | | | 2852 | 0 | values.push_back(*valueIt); | 2853 | 0 | ++it; | 2854 | 0 | ++valueIt; | 2855 | 0 | } | 2856 | 454 | } | 2857 | | | 2858 | 28.0k | if (values.size() > nargs.max) | 2859 | 17 | { | 2860 | 17 | return "Passed an argument into a non-argument flag: " + arg; | 2861 | 27.9k | } else if (values.size() < nargs.min) | 2862 | 0 | { | 2863 | 0 | if (nargs.min == 1 && nargs.max == 1) | 2864 | 0 | { | 2865 | 0 | return "Flag '" + arg + "' requires an argument but received none"; | 2866 | 0 | } else if (nargs.min == 1) | 2867 | 0 | { | 2868 | 0 | return "Flag '" + arg + "' requires at least one argument but received none"; | 2869 | 0 | } else if (nargs.min != nargs.max) | 2870 | 0 | { | 2871 | 0 | return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + | 2872 | 0 | " arguments but received " + std::to_string(values.size()); | 2873 | 0 | } else | 2874 | 0 | { | 2875 | 0 | return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + | 2876 | 0 | " arguments but received " + std::to_string(values.size()); | 2877 | 0 | } | 2878 | 0 | } | 2879 | | | 2880 | 27.9k | return {}; | 2881 | 28.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> > > >&) |
2882 | | |
2883 | | template <typename It> |
2884 | | bool ParseLong(It &it, It end) |
2885 | 514 | { |
2886 | 514 | const auto &chunk = *it; |
2887 | 514 | const auto argchunk = chunk.substr(longprefix.size()); |
2888 | | // Try to separate it, in case of a separator: |
2889 | 514 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); |
2890 | | // If the separator is in the argument, separate it. |
2891 | 514 | const auto arg = (separator != argchunk.npos ? |
2892 | 241 | std::string(argchunk, 0, separator) |
2893 | 514 | : argchunk); |
2894 | 514 | const auto joined = (separator != argchunk.npos ? |
2895 | 241 | argchunk.substr(separator + longseparator.size()) |
2896 | 514 | : std::string()); |
2897 | | |
2898 | 514 | if (auto flag = Match(arg)) |
2899 | 95 | { |
2900 | | #ifdef ARGS_NOEXCEPT |
2901 | | // Match() may set the flag's error (e.g. Error::Extra when |
2902 | | // Options::Single is violated). In non-noexcept mode that |
2903 | | // path throws and parsing stops before the value is read; |
2904 | | // in noexcept mode we must mirror that and skip the value |
2905 | | // parsing so the previously-stored value is preserved. |
2906 | | if (flag->GetError() != Error::None) |
2907 | | { |
2908 | | return false; |
2909 | | } |
2910 | | #endif |
2911 | 95 | std::vector<std::string> values; |
2912 | 95 | const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, |
2913 | 95 | separator != argchunk.npos, joined, false, values); |
2914 | 95 | if (!errorMessage.empty()) |
2915 | 17 | { |
2916 | 17 | #ifndef ARGS_NOEXCEPT |
2917 | 17 | throw ParseError(errorMessage); |
2918 | | #else |
2919 | | error = Error::Parse; |
2920 | | errorMsg = errorMessage; |
2921 | | return false; |
2922 | | #endif |
2923 | 17 | } |
2924 | | |
2925 | 78 | if (!readCompletion) |
2926 | 78 | { |
2927 | 78 | flag->ParseValue(values); |
2928 | | #ifdef ARGS_NOEXCEPT |
2929 | | // Non-noexcept ParseValue paths throw on Help, reader |
2930 | | // failure, or Map miss, which halts parsing. Mirror |
2931 | | // that here so a later parser-level error (e.g. an |
2932 | | // unknown flag) cannot shadow the flag's error in |
2933 | | // ArgumentParser::GetError(). |
2934 | | if (flag->GetError() != Error::None) |
2935 | | { |
2936 | | return false; |
2937 | | } |
2938 | | #endif |
2939 | 78 | } |
2940 | | |
2941 | 78 | if (flag->KickOut()) |
2942 | 0 | { |
2943 | 0 | ++it; |
2944 | 0 | return false; |
2945 | 0 | } |
2946 | 78 | } else |
2947 | 419 | { |
2948 | 419 | const std::string errorMessage("Flag could not be matched: " + arg); |
2949 | 419 | #ifndef ARGS_NOEXCEPT |
2950 | 419 | throw ParseError(errorMessage); |
2951 | | #else |
2952 | | error = Error::Parse; |
2953 | | errorMsg = errorMessage; |
2954 | | return false; |
2955 | | #endif |
2956 | 419 | } |
2957 | | |
2958 | 78 | return true; |
2959 | 514 | } 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 | 2885 | 514 | { | 2886 | 514 | const auto &chunk = *it; | 2887 | 514 | const auto argchunk = chunk.substr(longprefix.size()); | 2888 | | // Try to separate it, in case of a separator: | 2889 | 514 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); | 2890 | | // If the separator is in the argument, separate it. | 2891 | 514 | const auto arg = (separator != argchunk.npos ? | 2892 | 241 | std::string(argchunk, 0, separator) | 2893 | 514 | : argchunk); | 2894 | 514 | const auto joined = (separator != argchunk.npos ? | 2895 | 241 | argchunk.substr(separator + longseparator.size()) | 2896 | 514 | : std::string()); | 2897 | | | 2898 | 514 | if (auto flag = Match(arg)) | 2899 | 95 | { | 2900 | | #ifdef ARGS_NOEXCEPT | 2901 | | // Match() may set the flag's error (e.g. Error::Extra when | 2902 | | // Options::Single is violated). In non-noexcept mode that | 2903 | | // path throws and parsing stops before the value is read; | 2904 | | // in noexcept mode we must mirror that and skip the value | 2905 | | // parsing so the previously-stored value is preserved. | 2906 | | if (flag->GetError() != Error::None) | 2907 | | { | 2908 | | return false; | 2909 | | } | 2910 | | #endif | 2911 | 95 | std::vector<std::string> values; | 2912 | 95 | const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, | 2913 | 95 | separator != argchunk.npos, joined, false, values); | 2914 | 95 | if (!errorMessage.empty()) | 2915 | 17 | { | 2916 | 17 | #ifndef ARGS_NOEXCEPT | 2917 | 17 | throw ParseError(errorMessage); | 2918 | | #else | 2919 | | error = Error::Parse; | 2920 | | errorMsg = errorMessage; | 2921 | | return false; | 2922 | | #endif | 2923 | 17 | } | 2924 | | | 2925 | 78 | if (!readCompletion) | 2926 | 78 | { | 2927 | 78 | flag->ParseValue(values); | 2928 | | #ifdef ARGS_NOEXCEPT | 2929 | | // Non-noexcept ParseValue paths throw on Help, reader | 2930 | | // failure, or Map miss, which halts parsing. Mirror | 2931 | | // that here so a later parser-level error (e.g. an | 2932 | | // unknown flag) cannot shadow the flag's error in | 2933 | | // ArgumentParser::GetError(). | 2934 | | if (flag->GetError() != Error::None) | 2935 | | { | 2936 | | return false; | 2937 | | } | 2938 | | #endif | 2939 | 78 | } | 2940 | | | 2941 | 78 | if (flag->KickOut()) | 2942 | 0 | { | 2943 | 0 | ++it; | 2944 | 0 | return false; | 2945 | 0 | } | 2946 | 78 | } else | 2947 | 419 | { | 2948 | 419 | const std::string errorMessage("Flag could not be matched: " + arg); | 2949 | 419 | #ifndef ARGS_NOEXCEPT | 2950 | 419 | throw ParseError(errorMessage); | 2951 | | #else | 2952 | | error = Error::Parse; | 2953 | | errorMsg = errorMessage; | 2954 | | return false; | 2955 | | #endif | 2956 | 419 | } | 2957 | | | 2958 | 78 | return true; | 2959 | 514 | } |
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*>) |
2960 | | |
2961 | | template <typename It> |
2962 | | bool ParseShort(It &it, It end) |
2963 | 621 | { |
2964 | 621 | const auto &chunk = *it; |
2965 | 621 | const auto argchunk = chunk.substr(shortprefix.size()); |
2966 | 28.5k | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) |
2967 | 28.1k | { |
2968 | 28.1k | const auto arg = *argit; |
2969 | | |
2970 | 28.1k | if (auto flag = Match(arg)) |
2971 | 27.9k | { |
2972 | | #ifdef ARGS_NOEXCEPT |
2973 | | // See ParseLong: if Match recorded an error |
2974 | | // (e.g. Options::Single violation), bail before the |
2975 | | // value is parsed so the prior value is preserved. |
2976 | | if (flag->GetError() != Error::None) |
2977 | | { |
2978 | | return false; |
2979 | | } |
2980 | | #endif |
2981 | 27.9k | const std::string value(argit + 1, std::end(argchunk)); |
2982 | 27.9k | std::vector<std::string> values; |
2983 | 27.9k | const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, |
2984 | 27.9k | allowSeparateShortValue, allowJoinedShortValue, |
2985 | 27.9k | !value.empty(), value, !value.empty(), values); |
2986 | | |
2987 | 27.9k | if (!errorMessage.empty()) |
2988 | 0 | { |
2989 | 0 | #ifndef ARGS_NOEXCEPT |
2990 | 0 | throw ParseError(errorMessage); |
2991 | | #else |
2992 | | error = Error::Parse; |
2993 | | errorMsg = errorMessage; |
2994 | | return false; |
2995 | | #endif |
2996 | 0 | } |
2997 | | |
2998 | 27.9k | if (!readCompletion) |
2999 | 27.9k | { |
3000 | 27.9k | flag->ParseValue(values); |
3001 | | #ifdef ARGS_NOEXCEPT |
3002 | | // See ParseLong: ensure a flag-level error from |
3003 | | // ParseValue (Help, Parse, Map) halts parsing so |
3004 | | // it cannot be shadowed by a later parser error. |
3005 | | if (flag->GetError() != Error::None) |
3006 | | { |
3007 | | return false; |
3008 | | } |
3009 | | #endif |
3010 | 27.9k | } |
3011 | | |
3012 | 27.9k | if (flag->KickOut()) |
3013 | 0 | { |
3014 | 0 | ++it; |
3015 | 0 | return false; |
3016 | 0 | } |
3017 | | |
3018 | 27.9k | if (!values.empty()) |
3019 | 0 | { |
3020 | 0 | break; |
3021 | 0 | } |
3022 | 27.9k | } else |
3023 | 243 | { |
3024 | 243 | const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); |
3025 | 243 | #ifndef ARGS_NOEXCEPT |
3026 | 243 | throw ParseError(errorMessage); |
3027 | | #else |
3028 | | error = Error::Parse; |
3029 | | errorMsg = errorMessage; |
3030 | | return false; |
3031 | | #endif |
3032 | 243 | } |
3033 | 28.1k | } |
3034 | | |
3035 | 378 | return true; |
3036 | 621 | } 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 | 2963 | 621 | { | 2964 | 621 | const auto &chunk = *it; | 2965 | 621 | const auto argchunk = chunk.substr(shortprefix.size()); | 2966 | 28.5k | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) | 2967 | 28.1k | { | 2968 | 28.1k | const auto arg = *argit; | 2969 | | | 2970 | 28.1k | if (auto flag = Match(arg)) | 2971 | 27.9k | { | 2972 | | #ifdef ARGS_NOEXCEPT | 2973 | | // See ParseLong: if Match recorded an error | 2974 | | // (e.g. Options::Single violation), bail before the | 2975 | | // value is parsed so the prior value is preserved. | 2976 | | if (flag->GetError() != Error::None) | 2977 | | { | 2978 | | return false; | 2979 | | } | 2980 | | #endif | 2981 | 27.9k | const std::string value(argit + 1, std::end(argchunk)); | 2982 | 27.9k | std::vector<std::string> values; | 2983 | 27.9k | const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, | 2984 | 27.9k | allowSeparateShortValue, allowJoinedShortValue, | 2985 | 27.9k | !value.empty(), value, !value.empty(), values); | 2986 | | | 2987 | 27.9k | if (!errorMessage.empty()) | 2988 | 0 | { | 2989 | 0 | #ifndef ARGS_NOEXCEPT | 2990 | 0 | throw ParseError(errorMessage); | 2991 | | #else | 2992 | | error = Error::Parse; | 2993 | | errorMsg = errorMessage; | 2994 | | return false; | 2995 | | #endif | 2996 | 0 | } | 2997 | | | 2998 | 27.9k | if (!readCompletion) | 2999 | 27.9k | { | 3000 | 27.9k | flag->ParseValue(values); | 3001 | | #ifdef ARGS_NOEXCEPT | 3002 | | // See ParseLong: ensure a flag-level error from | 3003 | | // ParseValue (Help, Parse, Map) halts parsing so | 3004 | | // it cannot be shadowed by a later parser error. | 3005 | | if (flag->GetError() != Error::None) | 3006 | | { | 3007 | | return false; | 3008 | | } | 3009 | | #endif | 3010 | 27.9k | } | 3011 | | | 3012 | 27.9k | if (flag->KickOut()) | 3013 | 0 | { | 3014 | 0 | ++it; | 3015 | 0 | return false; | 3016 | 0 | } | 3017 | | | 3018 | 27.9k | if (!values.empty()) | 3019 | 0 | { | 3020 | 0 | break; | 3021 | 0 | } | 3022 | 27.9k | } else | 3023 | 243 | { | 3024 | 243 | const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); | 3025 | 243 | #ifndef ARGS_NOEXCEPT | 3026 | 243 | throw ParseError(errorMessage); | 3027 | | #else | 3028 | | error = Error::Parse; | 3029 | | errorMsg = errorMessage; | 3030 | | return false; | 3031 | | #endif | 3032 | 243 | } | 3033 | 28.1k | } | 3034 | | | 3035 | 378 | return true; | 3036 | 621 | } |
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*>) |
3037 | | |
3038 | | bool AddCompletionReply(const std::string &cur, const std::string &choice) |
3039 | 0 | { |
3040 | 0 | if (cur.empty() || choice.find(cur) == 0) |
3041 | 0 | { |
3042 | 0 | if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos) |
3043 | 0 | { |
3044 | 0 | completion->reply.push_back(choice.substr(choice.find(longseparator) + longseparator.size())); |
3045 | 0 | } else |
3046 | 0 | { |
3047 | 0 | completion->reply.push_back(choice); |
3048 | 0 | } |
3049 | 0 | return true; |
3050 | 0 | } |
3051 | | |
3052 | 0 | return false; |
3053 | 0 | } |
3054 | | |
3055 | | template <typename It> |
3056 | | bool Complete(It it, It end, bool terminated) |
3057 | 1.39k | { |
3058 | 1.39k | auto nextIt = it; |
3059 | 1.39k | if (!readCompletion || (++nextIt != end)) |
3060 | 1.39k | { |
3061 | 1.39k | return false; |
3062 | 1.39k | } |
3063 | | |
3064 | 0 | const auto &chunk = *it; |
3065 | 0 | auto pos = GetNextPositional(); |
3066 | 0 | std::vector<Command *> commands = GetCommands(); |
3067 | 0 | const auto optionType = ParseOption(chunk, true); |
3068 | | |
3069 | | // Once the terminator has been seen the parser treats every |
3070 | | // following chunk as positional, so only positional choices are |
3071 | | // valid completions here. Suggesting flags or commands past the |
3072 | | // terminator offers candidates the parser would then reject. |
3073 | 0 | if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) |
3074 | 0 | { |
3075 | 0 | for (auto &cmd : commands) |
3076 | 0 | { |
3077 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
3078 | 0 | { |
3079 | 0 | AddCompletionReply(chunk, cmd->Name()); |
3080 | 0 | } |
3081 | 0 | } |
3082 | 0 | } else |
3083 | 0 | { |
3084 | 0 | bool hasPositionalCompletion = true; |
3085 | |
|
3086 | 0 | if (!terminated && !commands.empty()) |
3087 | 0 | { |
3088 | 0 | for (auto &cmd : commands) |
3089 | 0 | { |
3090 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
3091 | 0 | { |
3092 | 0 | AddCompletionReply(chunk, cmd->Name()); |
3093 | 0 | } |
3094 | 0 | } |
3095 | 0 | } else if (pos) |
3096 | 0 | { |
3097 | 0 | if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
3098 | 0 | { |
3099 | 0 | auto choices = pos->HelpChoices(helpParams); |
3100 | 0 | hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; |
3101 | 0 | for (auto &choice : choices) |
3102 | 0 | { |
3103 | 0 | AddCompletionReply(chunk, choice); |
3104 | 0 | } |
3105 | 0 | } |
3106 | 0 | } |
3107 | |
|
3108 | 0 | if (!terminated && hasPositionalCompletion) |
3109 | 0 | { |
3110 | 0 | auto flags = GetAllFlags(); |
3111 | 0 | for (auto flag : flags) |
3112 | 0 | { |
3113 | 0 | if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) |
3114 | 0 | { |
3115 | 0 | continue; |
3116 | 0 | } |
3117 | | |
3118 | 0 | auto &matcher = flag->GetMatcher(); |
3119 | 0 | if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) |
3120 | 0 | { |
3121 | 0 | for (auto &flagName : matcher.GetFlagStrings()) |
3122 | 0 | { |
3123 | 0 | if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) |
3124 | 0 | { |
3125 | 0 | break; |
3126 | 0 | } |
3127 | 0 | } |
3128 | 0 | } |
3129 | 0 | } |
3130 | |
|
3131 | 0 | if (optionType == OptionType::LongFlag && allowJoinedLongValue) |
3132 | 0 | { |
3133 | 0 | const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); |
3134 | | // Only attempt joined-value completion when the |
3135 | | // separator lies at or past the long prefix, so |
3136 | | // there is a (possibly empty) flag name between |
3137 | | // them. With a custom longseparator that overlaps |
3138 | | // the prefix (e.g. LongSeparator("-") under the |
3139 | | // default "--" prefix), an attacker-controlled |
3140 | | // completion word like "--x" puts the separator |
3141 | | // inside the prefix, making `arg` shorter than |
3142 | | // longprefix. arg.substr(longprefix.size()) would |
3143 | | // then throw std::out_of_range, which escapes the |
3144 | | // parser as a non-args exception (bypassing the |
3145 | | // documented catch(args::Error) idiom) and is |
3146 | | // thrown even under ARGS_NOEXCEPT. |
3147 | 0 | if (separator != chunk.npos && separator >= longprefix.size()) |
3148 | 0 | { |
3149 | 0 | std::string arg(chunk, 0, separator); |
3150 | 0 | if (auto flag = this->Match(arg.substr(longprefix.size()))) |
3151 | 0 | { |
3152 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) |
3153 | 0 | { |
3154 | 0 | AddCompletionReply(chunk, arg + longseparator + choice); |
3155 | 0 | } |
3156 | 0 | } |
3157 | 0 | } |
3158 | 0 | } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) |
3159 | 0 | { |
3160 | 0 | if (chunk.size() > shortprefix.size() + 1) |
3161 | 0 | { |
3162 | 0 | auto arg = chunk.at(shortprefix.size()); |
3163 | | //TODO: support -abcVALUE where a and b take no value |
3164 | 0 | if (auto flag = this->Match(arg)) |
3165 | 0 | { |
3166 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) |
3167 | 0 | { |
3168 | 0 | AddCompletionReply(chunk, shortprefix + arg + choice); |
3169 | 0 | } |
3170 | 0 | } |
3171 | 0 | } |
3172 | 0 | } |
3173 | 0 | } |
3174 | 0 | } |
3175 | |
|
3176 | 0 | #ifndef ARGS_NOEXCEPT |
3177 | 0 | throw Completion(completion->Get()); |
3178 | | #else |
3179 | | return true; |
3180 | | #endif |
3181 | 1.39k | } 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 | 3057 | 1.39k | { | 3058 | 1.39k | auto nextIt = it; | 3059 | 1.39k | if (!readCompletion || (++nextIt != end)) | 3060 | 1.39k | { | 3061 | 1.39k | return false; | 3062 | 1.39k | } | 3063 | | | 3064 | 0 | const auto &chunk = *it; | 3065 | 0 | auto pos = GetNextPositional(); | 3066 | 0 | std::vector<Command *> commands = GetCommands(); | 3067 | 0 | const auto optionType = ParseOption(chunk, true); | 3068 | | | 3069 | | // Once the terminator has been seen the parser treats every | 3070 | | // following chunk as positional, so only positional choices are | 3071 | | // valid completions here. Suggesting flags or commands past the | 3072 | | // terminator offers candidates the parser would then reject. | 3073 | 0 | if (!terminated && !commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) | 3074 | 0 | { | 3075 | 0 | for (auto &cmd : commands) | 3076 | 0 | { | 3077 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 3078 | 0 | { | 3079 | 0 | AddCompletionReply(chunk, cmd->Name()); | 3080 | 0 | } | 3081 | 0 | } | 3082 | 0 | } else | 3083 | 0 | { | 3084 | 0 | bool hasPositionalCompletion = true; | 3085 | |
| 3086 | 0 | if (!terminated && !commands.empty()) | 3087 | 0 | { | 3088 | 0 | for (auto &cmd : commands) | 3089 | 0 | { | 3090 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 3091 | 0 | { | 3092 | 0 | AddCompletionReply(chunk, cmd->Name()); | 3093 | 0 | } | 3094 | 0 | } | 3095 | 0 | } else if (pos) | 3096 | 0 | { | 3097 | 0 | if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 3098 | 0 | { | 3099 | 0 | auto choices = pos->HelpChoices(helpParams); | 3100 | 0 | hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; | 3101 | 0 | for (auto &choice : choices) | 3102 | 0 | { | 3103 | 0 | AddCompletionReply(chunk, choice); | 3104 | 0 | } | 3105 | 0 | } | 3106 | 0 | } | 3107 | |
| 3108 | 0 | if (!terminated && hasPositionalCompletion) | 3109 | 0 | { | 3110 | 0 | auto flags = GetAllFlags(); | 3111 | 0 | for (auto flag : flags) | 3112 | 0 | { | 3113 | 0 | if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) | 3114 | 0 | { | 3115 | 0 | continue; | 3116 | 0 | } | 3117 | | | 3118 | 0 | auto &matcher = flag->GetMatcher(); | 3119 | 0 | if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) | 3120 | 0 | { | 3121 | 0 | for (auto &flagName : matcher.GetFlagStrings()) | 3122 | 0 | { | 3123 | 0 | if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) | 3124 | 0 | { | 3125 | 0 | break; | 3126 | 0 | } | 3127 | 0 | } | 3128 | 0 | } | 3129 | 0 | } | 3130 | |
| 3131 | 0 | if (optionType == OptionType::LongFlag && allowJoinedLongValue) | 3132 | 0 | { | 3133 | 0 | const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); | 3134 | | // Only attempt joined-value completion when the | 3135 | | // separator lies at or past the long prefix, so | 3136 | | // there is a (possibly empty) flag name between | 3137 | | // them. With a custom longseparator that overlaps | 3138 | | // the prefix (e.g. LongSeparator("-") under the | 3139 | | // default "--" prefix), an attacker-controlled | 3140 | | // completion word like "--x" puts the separator | 3141 | | // inside the prefix, making `arg` shorter than | 3142 | | // longprefix. arg.substr(longprefix.size()) would | 3143 | | // then throw std::out_of_range, which escapes the | 3144 | | // parser as a non-args exception (bypassing the | 3145 | | // documented catch(args::Error) idiom) and is | 3146 | | // thrown even under ARGS_NOEXCEPT. | 3147 | 0 | if (separator != chunk.npos && separator >= longprefix.size()) | 3148 | 0 | { | 3149 | 0 | std::string arg(chunk, 0, separator); | 3150 | 0 | if (auto flag = this->Match(arg.substr(longprefix.size()))) | 3151 | 0 | { | 3152 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) | 3153 | 0 | { | 3154 | 0 | AddCompletionReply(chunk, arg + longseparator + choice); | 3155 | 0 | } | 3156 | 0 | } | 3157 | 0 | } | 3158 | 0 | } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) | 3159 | 0 | { | 3160 | 0 | if (chunk.size() > shortprefix.size() + 1) | 3161 | 0 | { | 3162 | 0 | auto arg = chunk.at(shortprefix.size()); | 3163 | | //TODO: support -abcVALUE where a and b take no value | 3164 | 0 | if (auto flag = this->Match(arg)) | 3165 | 0 | { | 3166 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) | 3167 | 0 | { | 3168 | 0 | AddCompletionReply(chunk, shortprefix + arg + choice); | 3169 | 0 | } | 3170 | 0 | } | 3171 | 0 | } | 3172 | 0 | } | 3173 | 0 | } | 3174 | 0 | } | 3175 | |
| 3176 | 0 | #ifndef ARGS_NOEXCEPT | 3177 | 0 | throw Completion(completion->Get()); | 3178 | | #else | 3179 | | return true; | 3180 | | #endif | 3181 | 1.39k | } |
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) |
3182 | | |
3183 | | template <typename It> |
3184 | | It Parse(It begin, It end) |
3185 | 983 | { |
3186 | 983 | bool terminated = false; |
3187 | 983 | std::vector<Command *> commands = GetCommands(); |
3188 | | |
3189 | | // Check all arg chunks |
3190 | 2.12k | for (auto it = begin; it != end; ++it) |
3191 | 1.39k | { |
3192 | 1.39k | if (Complete(it, end, terminated)) |
3193 | 0 | { |
3194 | 0 | return end; |
3195 | 0 | } |
3196 | | |
3197 | 1.39k | const auto &chunk = *it; |
3198 | | |
3199 | 1.39k | if (!terminated && chunk == terminator) |
3200 | 3 | { |
3201 | 3 | terminated = true; |
3202 | 1.39k | } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) |
3203 | 514 | { |
3204 | 514 | if (!ParseLong(it, end)) |
3205 | 0 | { |
3206 | 0 | return it; |
3207 | 0 | } |
3208 | 879 | } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) |
3209 | 621 | { |
3210 | 621 | if (!ParseShort(it, end)) |
3211 | 0 | { |
3212 | 0 | return it; |
3213 | 0 | } |
3214 | 621 | } else if (!terminated && !commands.empty()) |
3215 | 0 | { |
3216 | 0 | auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); |
3217 | 0 | if (itCommand == commands.end()) |
3218 | 0 | { |
3219 | 0 | const std::string errorMessage("Unknown command: " + chunk); |
3220 | 0 | #ifndef ARGS_NOEXCEPT |
3221 | 0 | throw ParseError(errorMessage); |
3222 | | #else |
3223 | | error = Error::Parse; |
3224 | | errorMsg = errorMessage; |
3225 | | return it; |
3226 | | #endif |
3227 | 0 | } |
3228 | | |
3229 | 0 | SelectCommand(*itCommand); |
3230 | |
|
3231 | 0 | if (const auto &coroutine = GetCoroutine()) |
3232 | 0 | { |
3233 | 0 | ++it; |
3234 | 0 | RaiiSubparser coro(*this, std::vector<std::string>(it, end)); |
3235 | 0 | coroutine(coro.Parser()); |
3236 | | #ifdef ARGS_NOEXCEPT |
3237 | | error = GetError(); |
3238 | | if (error != Error::None) |
3239 | | { |
3240 | | return end; |
3241 | | } |
3242 | | |
3243 | | if (!coro.Parser().IsParsed()) |
3244 | | { |
3245 | | error = Error::Usage; |
3246 | | return end; |
3247 | | } |
3248 | | #else |
3249 | 0 | if (!coro.Parser().IsParsed()) |
3250 | 0 | { |
3251 | 0 | throw UsageError("Subparser::Parse was not called"); |
3252 | 0 | } |
3253 | 0 | #endif |
3254 | | |
3255 | 0 | break; |
3256 | 0 | } |
3257 | | |
3258 | 0 | commands = GetCommands(); |
3259 | 0 | } else |
3260 | 258 | { |
3261 | 258 | auto pos = GetNextPositional(); |
3262 | 258 | if (pos) |
3263 | 0 | { |
3264 | 0 | pos->ParseValue(chunk); |
3265 | | #ifdef ARGS_NOEXCEPT |
3266 | | if (pos->GetError() != Error::None) |
3267 | | { |
3268 | | return it; |
3269 | | } |
3270 | | #endif |
3271 | |
|
3272 | 0 | if (pos->KickOut()) |
3273 | 0 | { |
3274 | 0 | return ++it; |
3275 | 0 | } |
3276 | 0 | } else |
3277 | 258 | { |
3278 | 258 | const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); |
3279 | 258 | #ifndef ARGS_NOEXCEPT |
3280 | 258 | throw ParseError(errorMessage); |
3281 | | #else |
3282 | | error = Error::Parse; |
3283 | | errorMsg = errorMessage; |
3284 | | return it; |
3285 | | #endif |
3286 | 258 | } |
3287 | 258 | } |
3288 | | |
3289 | 1.13k | if (!readCompletion && completion != nullptr && completion->Matched()) |
3290 | 0 | { |
3291 | | #ifdef ARGS_NOEXCEPT |
3292 | | if (completion->GetError() != Error::None) |
3293 | | { |
3294 | | error = completion->GetError(); |
3295 | | if (errorMsg.empty()) |
3296 | | { |
3297 | | errorMsg = completion->GetErrorMsg(); |
3298 | | } |
3299 | | return it; |
3300 | | } |
3301 | | |
3302 | | error = Error::Completion; |
3303 | | #endif |
3304 | 0 | readCompletion = true; |
3305 | 0 | ++it; |
3306 | 0 | const auto argsLeft = static_cast<size_t>(std::distance(it, end)); |
3307 | 0 | if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) |
3308 | 0 | { |
3309 | 0 | #ifndef ARGS_NOEXCEPT |
3310 | 0 | throw Completion(""); |
3311 | | #else |
3312 | | return end; |
3313 | | #endif |
3314 | 0 | } |
3315 | | |
3316 | 0 | ++it; |
3317 | 0 | std::vector<std::string> curArgs; |
3318 | 0 | curArgs.reserve(completion->cword); |
3319 | 0 | auto curIt = it; |
3320 | 0 | for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt) |
3321 | 0 | { |
3322 | 0 | curArgs.push_back(*curIt); |
3323 | 0 | } |
3324 | |
|
3325 | 0 | if (completion->syntax == "bash") |
3326 | 0 | { |
3327 | | // bash tokenizes --flag=value as --flag=value |
3328 | | // Security fix: Use size_t arithmetic throughout to avoid conversion issues |
3329 | 0 | for (size_t idx = 0; idx < curArgs.size(); ) |
3330 | 0 | { |
3331 | 0 | if (idx > 0 && curArgs[idx] == "=") |
3332 | 0 | { |
3333 | 0 | size_t prev_idx = idx - 1; // Safe since we checked idx > 0 |
3334 | 0 | curArgs[prev_idx] += "="; |
3335 | 0 | size_t next_idx = 0; |
3336 | 0 | if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size()) |
3337 | 0 | { |
3338 | 0 | curArgs[prev_idx] += curArgs[next_idx]; |
3339 | | // Erase the '=' token and the following value token. |
3340 | 0 | size_t erase_end = 0; |
3341 | 0 | if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end)) |
3342 | 0 | { |
3343 | 0 | typedef std::vector<std::string>::difference_type diff_t; |
3344 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx), |
3345 | 0 | curArgs.begin() + static_cast<diff_t>(erase_end)); |
3346 | 0 | } |
3347 | 0 | } else |
3348 | 0 | { |
3349 | | // Safe erase of single '=' token at the end |
3350 | 0 | typedef std::vector<std::string>::difference_type diff_t; |
3351 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx)); |
3352 | 0 | } |
3353 | | // Do not increment idx - next element slides into current position |
3354 | 0 | } else |
3355 | 0 | { |
3356 | 0 | ++idx; |
3357 | 0 | } |
3358 | 0 | } |
3359 | |
|
3360 | 0 | } |
3361 | 0 | #ifndef ARGS_NOEXCEPT |
3362 | 0 | try |
3363 | 0 | { |
3364 | 0 | Parse(curArgs.begin(), curArgs.end()); |
3365 | 0 | throw Completion(""); |
3366 | 0 | } |
3367 | 0 | catch (Completion &) |
3368 | 0 | { |
3369 | 0 | throw; |
3370 | 0 | } |
3371 | 0 | catch (args::Error&) |
3372 | 0 | { |
3373 | 0 | throw Completion(""); |
3374 | 0 | } |
3375 | | #else |
3376 | | // Discard the nested Parse's return value: it points |
3377 | | // into the local curArgs vector, which is destroyed |
3378 | | // when this function returns, leaving the caller with |
3379 | | // a dangling iterator that would be compared against |
3380 | | // the outer `end` in ParseCLI. Return the outer |
3381 | | // `end` instead so the iterator stays in the caller's |
3382 | | // container. |
3383 | | Parse(curArgs.begin(), curArgs.end()); |
3384 | | error = Error::Completion; |
3385 | | errorMsg.clear(); |
3386 | | return end; |
3387 | | #endif |
3388 | 0 | } |
3389 | 1.13k | } |
3390 | | |
3391 | 725 | Validate(shortprefix, longprefix); |
3392 | 725 | return end; |
3393 | 983 | } 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 | 3185 | 983 | { | 3186 | 983 | bool terminated = false; | 3187 | 983 | std::vector<Command *> commands = GetCommands(); | 3188 | | | 3189 | | // Check all arg chunks | 3190 | 2.12k | for (auto it = begin; it != end; ++it) | 3191 | 1.39k | { | 3192 | 1.39k | if (Complete(it, end, terminated)) | 3193 | 0 | { | 3194 | 0 | return end; | 3195 | 0 | } | 3196 | | | 3197 | 1.39k | const auto &chunk = *it; | 3198 | | | 3199 | 1.39k | if (!terminated && chunk == terminator) | 3200 | 3 | { | 3201 | 3 | terminated = true; | 3202 | 1.39k | } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) | 3203 | 514 | { | 3204 | 514 | if (!ParseLong(it, end)) | 3205 | 0 | { | 3206 | 0 | return it; | 3207 | 0 | } | 3208 | 879 | } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) | 3209 | 621 | { | 3210 | 621 | if (!ParseShort(it, end)) | 3211 | 0 | { | 3212 | 0 | return it; | 3213 | 0 | } | 3214 | 621 | } else if (!terminated && !commands.empty()) | 3215 | 0 | { | 3216 | 0 | auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); | 3217 | 0 | if (itCommand == commands.end()) | 3218 | 0 | { | 3219 | 0 | const std::string errorMessage("Unknown command: " + chunk); | 3220 | 0 | #ifndef ARGS_NOEXCEPT | 3221 | 0 | throw ParseError(errorMessage); | 3222 | | #else | 3223 | | error = Error::Parse; | 3224 | | errorMsg = errorMessage; | 3225 | | return it; | 3226 | | #endif | 3227 | 0 | } | 3228 | | | 3229 | 0 | SelectCommand(*itCommand); | 3230 | |
| 3231 | 0 | if (const auto &coroutine = GetCoroutine()) | 3232 | 0 | { | 3233 | 0 | ++it; | 3234 | 0 | RaiiSubparser coro(*this, std::vector<std::string>(it, end)); | 3235 | 0 | coroutine(coro.Parser()); | 3236 | | #ifdef ARGS_NOEXCEPT | 3237 | | error = GetError(); | 3238 | | if (error != Error::None) | 3239 | | { | 3240 | | return end; | 3241 | | } | 3242 | | | 3243 | | if (!coro.Parser().IsParsed()) | 3244 | | { | 3245 | | error = Error::Usage; | 3246 | | return end; | 3247 | | } | 3248 | | #else | 3249 | 0 | if (!coro.Parser().IsParsed()) | 3250 | 0 | { | 3251 | 0 | throw UsageError("Subparser::Parse was not called"); | 3252 | 0 | } | 3253 | 0 | #endif | 3254 | | | 3255 | 0 | break; | 3256 | 0 | } | 3257 | | | 3258 | 0 | commands = GetCommands(); | 3259 | 0 | } else | 3260 | 258 | { | 3261 | 258 | auto pos = GetNextPositional(); | 3262 | 258 | if (pos) | 3263 | 0 | { | 3264 | 0 | pos->ParseValue(chunk); | 3265 | | #ifdef ARGS_NOEXCEPT | 3266 | | if (pos->GetError() != Error::None) | 3267 | | { | 3268 | | return it; | 3269 | | } | 3270 | | #endif | 3271 | |
| 3272 | 0 | if (pos->KickOut()) | 3273 | 0 | { | 3274 | 0 | return ++it; | 3275 | 0 | } | 3276 | 0 | } else | 3277 | 258 | { | 3278 | 258 | const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); | 3279 | 258 | #ifndef ARGS_NOEXCEPT | 3280 | 258 | throw ParseError(errorMessage); | 3281 | | #else | 3282 | | error = Error::Parse; | 3283 | | errorMsg = errorMessage; | 3284 | | return it; | 3285 | | #endif | 3286 | 258 | } | 3287 | 258 | } | 3288 | | | 3289 | 1.13k | if (!readCompletion && completion != nullptr && completion->Matched()) | 3290 | 0 | { | 3291 | | #ifdef ARGS_NOEXCEPT | 3292 | | if (completion->GetError() != Error::None) | 3293 | | { | 3294 | | error = completion->GetError(); | 3295 | | if (errorMsg.empty()) | 3296 | | { | 3297 | | errorMsg = completion->GetErrorMsg(); | 3298 | | } | 3299 | | return it; | 3300 | | } | 3301 | | | 3302 | | error = Error::Completion; | 3303 | | #endif | 3304 | 0 | readCompletion = true; | 3305 | 0 | ++it; | 3306 | 0 | const auto argsLeft = static_cast<size_t>(std::distance(it, end)); | 3307 | 0 | if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) | 3308 | 0 | { | 3309 | 0 | #ifndef ARGS_NOEXCEPT | 3310 | 0 | throw Completion(""); | 3311 | | #else | 3312 | | return end; | 3313 | | #endif | 3314 | 0 | } | 3315 | | | 3316 | 0 | ++it; | 3317 | 0 | std::vector<std::string> curArgs; | 3318 | 0 | curArgs.reserve(completion->cword); | 3319 | 0 | auto curIt = it; | 3320 | 0 | for (size_t idx = 0; idx < completion->cword && curIt != end; ++idx, ++curIt) | 3321 | 0 | { | 3322 | 0 | curArgs.push_back(*curIt); | 3323 | 0 | } | 3324 | |
| 3325 | 0 | if (completion->syntax == "bash") | 3326 | 0 | { | 3327 | | // bash tokenizes --flag=value as --flag=value | 3328 | | // Security fix: Use size_t arithmetic throughout to avoid conversion issues | 3329 | 0 | for (size_t idx = 0; idx < curArgs.size(); ) | 3330 | 0 | { | 3331 | 0 | if (idx > 0 && curArgs[idx] == "=") | 3332 | 0 | { | 3333 | 0 | size_t prev_idx = idx - 1; // Safe since we checked idx > 0 | 3334 | 0 | curArgs[prev_idx] += "="; | 3335 | 0 | size_t next_idx = 0; | 3336 | 0 | if (SafeAdd<size_t>(idx, static_cast<size_t>(1), next_idx) && next_idx < curArgs.size()) | 3337 | 0 | { | 3338 | 0 | curArgs[prev_idx] += curArgs[next_idx]; | 3339 | | // Erase the '=' token and the following value token. | 3340 | 0 | size_t erase_end = 0; | 3341 | 0 | if (SafeAdd<size_t>(next_idx, static_cast<size_t>(1), erase_end)) | 3342 | 0 | { | 3343 | 0 | typedef std::vector<std::string>::difference_type diff_t; | 3344 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx), | 3345 | 0 | curArgs.begin() + static_cast<diff_t>(erase_end)); | 3346 | 0 | } | 3347 | 0 | } else | 3348 | 0 | { | 3349 | | // Safe erase of single '=' token at the end | 3350 | 0 | typedef std::vector<std::string>::difference_type diff_t; | 3351 | 0 | curArgs.erase(curArgs.begin() + static_cast<diff_t>(idx)); | 3352 | 0 | } | 3353 | | // Do not increment idx - next element slides into current position | 3354 | 0 | } else | 3355 | 0 | { | 3356 | 0 | ++idx; | 3357 | 0 | } | 3358 | 0 | } | 3359 | |
| 3360 | 0 | } | 3361 | 0 | #ifndef ARGS_NOEXCEPT | 3362 | 0 | try | 3363 | 0 | { | 3364 | 0 | Parse(curArgs.begin(), curArgs.end()); | 3365 | 0 | throw Completion(""); | 3366 | 0 | } | 3367 | 0 | catch (Completion &) | 3368 | 0 | { | 3369 | 0 | throw; | 3370 | 0 | } | 3371 | 0 | catch (args::Error&) | 3372 | 0 | { | 3373 | 0 | throw Completion(""); | 3374 | 0 | } | 3375 | | #else | 3376 | | // Discard the nested Parse's return value: it points | 3377 | | // into the local curArgs vector, which is destroyed | 3378 | | // when this function returns, leaving the caller with | 3379 | | // a dangling iterator that would be compared against | 3380 | | // the outer `end` in ParseCLI. Return the outer | 3381 | | // `end` instead so the iterator stays in the caller's | 3382 | | // container. | 3383 | | Parse(curArgs.begin(), curArgs.end()); | 3384 | | error = Error::Completion; | 3385 | | errorMsg.clear(); | 3386 | | return end; | 3387 | | #endif | 3388 | 0 | } | 3389 | 1.13k | } | 3390 | | | 3391 | 725 | Validate(shortprefix, longprefix); | 3392 | 725 | return end; | 3393 | 983 | } |
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*>) |
3394 | | |
3395 | | public: |
3396 | | HelpParams helpParams; |
3397 | | |
3398 | | ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string()) |
3399 | 983 | { |
3400 | 983 | Description(description_); |
3401 | 983 | Epilog(epilog_); |
3402 | 983 | LongPrefix("--"); |
3403 | 983 | ShortPrefix("-"); |
3404 | 983 | LongSeparator("="); |
3405 | 983 | Terminator("--"); |
3406 | 983 | SetArgumentSeparations(true, true, true, true); |
3407 | 983 | matched = true; |
3408 | 983 | } |
3409 | | |
3410 | | void AddCompletion(CompletionFlag &completionFlag) |
3411 | 0 | { |
3412 | 0 | // Only take the pointer once registration has succeeded: Add() |
3413 | 0 | // throws on a duplicate flag, and the flag it was handed is |
3414 | 0 | // gone by the time that error reaches the caller. |
3415 | 0 | Add(completionFlag); |
3416 | 0 | completion = &completionFlag; |
3417 | 0 | } |
3418 | | |
3419 | | /** The program name for help generation |
3420 | | */ |
3421 | | const std::string &Prog() const |
3422 | 0 | { return helpParams.programName; } |
3423 | | /** The program name for help generation |
3424 | | */ |
3425 | | void Prog(const std::string &prog_) |
3426 | 0 | { this->helpParams.programName = prog_; } |
3427 | | |
3428 | | /** The prefix for long flags |
3429 | | */ |
3430 | | const std::string &LongPrefix() const |
3431 | 0 | { return longprefix; } |
3432 | | /** The prefix for long flags |
3433 | | */ |
3434 | | void LongPrefix(const std::string &longprefix_) |
3435 | 983 | { |
3436 | 983 | this->longprefix = longprefix_; |
3437 | 983 | this->helpParams.longPrefix = longprefix_; |
3438 | 983 | } |
3439 | | |
3440 | | /** The prefix for short flags |
3441 | | */ |
3442 | | const std::string &ShortPrefix() const |
3443 | 0 | { return shortprefix; } |
3444 | | /** The prefix for short flags |
3445 | | */ |
3446 | | void ShortPrefix(const std::string &shortprefix_) |
3447 | 983 | { |
3448 | 983 | this->shortprefix = shortprefix_; |
3449 | 983 | this->helpParams.shortPrefix = shortprefix_; |
3450 | 983 | } |
3451 | | |
3452 | | /** The separator for long flags |
3453 | | */ |
3454 | | const std::string &LongSeparator() const |
3455 | 0 | { return longseparator; } |
3456 | | /** The separator for long flags |
3457 | | */ |
3458 | | void LongSeparator(const std::string &longseparator_) |
3459 | 983 | { |
3460 | 983 | if (longseparator_.empty()) |
3461 | 0 | { |
3462 | 0 | const std::string errorMessage("longseparator can not be set to empty"); |
3463 | | #ifdef ARGS_NOEXCEPT |
3464 | | error = Error::Usage; |
3465 | | errorMsg = errorMessage; |
3466 | | #else |
3467 | 0 | throw UsageError(errorMessage); |
3468 | 0 | #endif |
3469 | 0 | } else |
3470 | 983 | { |
3471 | 983 | this->longseparator = longseparator_; |
3472 | 983 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " "; |
3473 | 983 | } |
3474 | 983 | } |
3475 | | |
3476 | | /** The terminator that forcibly separates flags from positionals |
3477 | | */ |
3478 | | const std::string &Terminator() const |
3479 | 0 | { return terminator; } |
3480 | | /** The terminator that forcibly separates flags from positionals |
3481 | | */ |
3482 | | void Terminator(const std::string &terminator_) |
3483 | 983 | { this->terminator = terminator_; } |
3484 | | |
3485 | | /** Get the current argument separation parameters. |
3486 | | * |
3487 | | * See SetArgumentSeparations for details on what each one means. |
3488 | | */ |
3489 | | void GetArgumentSeparations( |
3490 | | bool &allowJoinedShortValue_, |
3491 | | bool &allowJoinedLongValue_, |
3492 | | bool &allowSeparateShortValue_, |
3493 | | bool &allowSeparateLongValue_) const |
3494 | 0 | { |
3495 | 0 | allowJoinedShortValue_ = this->allowJoinedShortValue; |
3496 | 0 | allowJoinedLongValue_ = this->allowJoinedLongValue; |
3497 | 0 | allowSeparateShortValue_ = this->allowSeparateShortValue; |
3498 | 0 | allowSeparateLongValue_ = this->allowSeparateLongValue; |
3499 | 0 | } |
3500 | | |
3501 | | /** Change allowed option separation. |
3502 | | * |
3503 | | * \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) |
3504 | | * \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) |
3505 | | * \param allowSeparateShortValue_ Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) |
3506 | | * \param allowSeparateLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) |
3507 | | */ |
3508 | | void SetArgumentSeparations( |
3509 | | const bool allowJoinedShortValue_, |
3510 | | const bool allowJoinedLongValue_, |
3511 | | const bool allowSeparateShortValue_, |
3512 | | const bool allowSeparateLongValue_) |
3513 | 983 | { |
3514 | 983 | this->allowJoinedShortValue = allowJoinedShortValue_; |
3515 | 983 | this->allowJoinedLongValue = allowJoinedLongValue_; |
3516 | 983 | this->allowSeparateShortValue = allowSeparateShortValue_; |
3517 | 983 | this->allowSeparateLongValue = allowSeparateLongValue_; |
3518 | | |
3519 | 983 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " "; |
3520 | 983 | this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " "; |
3521 | 983 | } |
3522 | | |
3523 | | /** Pass the help menu into an ostream |
3524 | | */ |
3525 | | void Help(std::ostream &help_) const |
3526 | 0 | { |
3527 | 0 | auto &command = SelectedCommand(); |
3528 | 0 | const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description(); |
3529 | 0 | const auto desc_indent = helpParams.descriptionindent; |
3530 | 0 | const auto effective_desc_width = (helpParams.width > desc_indent) ? helpParams.width - desc_indent : 0; |
3531 | 0 | const auto description_text = Wrap(commandDescription, effective_desc_width); |
3532 | 0 | const auto epilog_text = Wrap(command.Epilog(), effective_desc_width); |
3533 | 0 |
|
3534 | 0 | const bool hasoptions = command.HasFlag(); |
3535 | 0 | const bool hasarguments = command.HasPositional(); |
3536 | 0 |
|
3537 | 0 | std::vector<std::string> prognameline; |
3538 | 0 | prognameline.push_back(helpParams.usageString); |
3539 | 0 | prognameline.push_back(Prog()); |
3540 | 0 | auto commandProgLine = command.GetProgramLine(helpParams); |
3541 | 0 | prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end()); |
3542 | 0 |
|
3543 | 0 | const auto prog_sum = helpParams.progindent + helpParams.progtailindent; |
3544 | 0 | const auto effective_prog_width = (helpParams.width > prog_sum) ? helpParams.width - prog_sum : 0; |
3545 | 0 | const auto effective_prog_first = (helpParams.width > helpParams.progindent) ? helpParams.width - helpParams.progindent : 0; |
3546 | 0 | const auto proglines = Wrap(prognameline.begin(), prognameline.end(), |
3547 | 0 | effective_prog_width, |
3548 | 0 | effective_prog_first); |
3549 | 0 | auto progit = std::begin(proglines); |
3550 | 0 | if (progit != std::end(proglines)) |
3551 | 0 | { |
3552 | 0 | help_ << std::string(helpParams.progindent, ' ') << *progit << '\n'; |
3553 | 0 | ++progit; |
3554 | 0 | } |
3555 | 0 | for (; progit != std::end(proglines); ++progit) |
3556 | 0 | { |
3557 | 0 | help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n'; |
3558 | 0 | } |
3559 | 0 |
|
3560 | 0 | help_ << '\n'; |
3561 | 0 |
|
3562 | 0 | if (!description_text.empty()) |
3563 | 0 | { |
3564 | 0 | for (const auto &line: description_text) |
3565 | 0 | { |
3566 | 0 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; |
3567 | 0 | } |
3568 | 0 | help_ << "\n"; |
3569 | 0 | } |
3570 | 0 |
|
3571 | 0 | bool lastDescriptionIsNewline = false; |
3572 | 0 |
|
3573 | 0 | if (!helpParams.optionsString.empty()) |
3574 | 0 | { |
3575 | 0 | help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n"; |
3576 | 0 | } |
3577 | 0 |
|
3578 | 0 | for (const auto &desc: command.GetDescription(helpParams, 0)) |
3579 | 0 | { |
3580 | 0 | lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty(); |
3581 | 0 | const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent; |
3582 | 0 | const auto flag_sum = helpParams.flagindent + helpParams.helpindent + helpParams.gutter; |
3583 | 0 | const auto effective_flag_width = (helpParams.width > flag_sum) ? helpParams.width - flag_sum : 0; |
3584 | 0 | const auto flags = Wrap(std::get<0>(desc), effective_flag_width); |
3585 | 0 | const auto info_sum = helpParams.helpindent + groupindent; |
3586 | 0 | const auto effective_info_width = (helpParams.width > info_sum) ? helpParams.width - info_sum : 0; |
3587 | 0 | const auto info = Wrap(std::get<1>(desc), effective_info_width); |
3588 | 0 |
|
3589 | 0 | std::string::size_type flagssize = 0; |
3590 | 0 | for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit) |
3591 | 0 | { |
3592 | 0 | if (flagsit != std::begin(flags)) |
3593 | 0 | { |
3594 | 0 | help_ << '\n'; |
3595 | 0 | } |
3596 | 0 | help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit; |
3597 | 0 | flagssize = Glyphs(*flagsit); |
3598 | 0 | } |
3599 | 0 |
|
3600 | 0 | auto infoit = std::begin(info); |
3601 | 0 | // groupindent is on both sides of this inequality, and therefore can be removed |
3602 | 0 | if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription) |
3603 | 0 | { |
3604 | 0 | help_ << '\n'; |
3605 | 0 | } else |
3606 | 0 | { |
3607 | 0 | // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here |
3608 | 0 | const auto indent_sum = helpParams.flagindent + flagssize; |
3609 | 0 | const auto effective_space = (helpParams.helpindent > indent_sum) ? helpParams.helpindent - indent_sum : 0; |
3610 | 0 | help_ << std::string(effective_space, ' ') << *infoit << '\n'; |
3611 | 0 | ++infoit; |
3612 | 0 | } |
3613 | 0 | for (; infoit != std::end(info); ++infoit) |
3614 | 0 | { |
3615 | 0 | help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n'; |
3616 | 0 | } |
3617 | 0 | } |
3618 | 0 | if (hasoptions && hasarguments && helpParams.showTerminator) |
3619 | 0 | { |
3620 | 0 | lastDescriptionIsNewline = false; |
3621 | 0 | const auto effective_term_width = (helpParams.width > helpParams.flagindent) ? helpParams.width - helpParams.flagindent : 0; |
3622 | 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)) |
3623 | 0 | { |
3624 | 0 | help_ << std::string(helpParams.flagindent, ' ') << item << '\n'; |
3625 | 0 | } |
3626 | 0 | } |
3627 | 0 |
|
3628 | 0 | if (!lastDescriptionIsNewline) |
3629 | 0 | { |
3630 | 0 | help_ << "\n"; |
3631 | 0 | } |
3632 | 0 |
|
3633 | 0 | for (const auto &line: epilog_text) |
3634 | 0 | { |
3635 | 0 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; |
3636 | 0 | } |
3637 | 0 | } |
3638 | | |
3639 | | /** Generate a help menu as a string. |
3640 | | * |
3641 | | * \return the help text as a single string |
3642 | | */ |
3643 | | std::string Help() const |
3644 | 0 | { |
3645 | 0 | std::ostringstream help_; |
3646 | 0 | Help(help_); |
3647 | 0 | return help_.str(); |
3648 | 0 | } |
3649 | | |
3650 | | virtual void Reset() noexcept override |
3651 | 983 | { |
3652 | 983 | Command::Reset(); |
3653 | 983 | matched = true; |
3654 | 983 | readCompletion = false; |
3655 | 983 | } |
3656 | | |
3657 | | /** Parse all arguments. |
3658 | | * |
3659 | | * \param begin an iterator to the beginning of the argument list |
3660 | | * \param end an iterator to the past-the-end element of the argument list |
3661 | | * \return the iterator after the last parsed value. Only useful for kick-out |
3662 | | */ |
3663 | | template <typename It> |
3664 | | It ParseArgs(It begin, It end) |
3665 | 983 | { |
3666 | | // Reset all Matched statuses and errors |
3667 | 983 | Reset(); |
3668 | | #ifdef ARGS_NOEXCEPT |
3669 | | error = GetError(); |
3670 | | if (error != Error::None) |
3671 | | { |
3672 | | return end; |
3673 | | } |
3674 | | #endif |
3675 | 983 | return Parse(begin, end); |
3676 | 983 | } 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 | 3665 | 983 | { | 3666 | | // Reset all Matched statuses and errors | 3667 | 983 | Reset(); | 3668 | | #ifdef ARGS_NOEXCEPT | 3669 | | error = GetError(); | 3670 | | if (error != Error::None) | 3671 | | { | 3672 | | return end; | 3673 | | } | 3674 | | #endif | 3675 | 983 | return Parse(begin, end); | 3676 | 983 | } |
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*>) |
3677 | | |
3678 | | /** Parse all arguments. |
3679 | | * |
3680 | | * \param args an iterable of the arguments |
3681 | | * \return the iterator after the last parsed value. Only useful for kick-out |
3682 | | */ |
3683 | | template <typename T> |
3684 | | auto ParseArgs(const T &args) -> decltype(std::begin(args)) |
3685 | 0 | { |
3686 | 0 | return ParseArgs(std::begin(args), std::end(args)); |
3687 | 0 | } |
3688 | | |
3689 | | /** Convenience function to parse the CLI from argc and argv |
3690 | | * |
3691 | | * Just assigns the program name and vectorizes arguments for passing into ParseArgs() |
3692 | | * |
3693 | | * \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. |
3694 | | */ |
3695 | | bool ParseCLI(const int argc, const char * const * argv) |
3696 | 0 | { |
3697 | 0 | if (argc > 0 && argv != nullptr && argv[0] != nullptr && Prog().empty()) |
3698 | 0 | { |
3699 | 0 | Prog(argv[0]); |
3700 | 0 | } |
3701 | 0 |
|
3702 | 0 | std::vector<std::string> args; |
3703 | 0 | if (argc > 1 && argv != nullptr) |
3704 | 0 | { |
3705 | 0 | args.assign(argv + 1, argv + argc); |
3706 | 0 | } |
3707 | 0 |
|
3708 | 0 | return ParseArgs(args) == std::end(args); |
3709 | 0 | } |
3710 | | |
3711 | | template <typename T> |
3712 | | bool ParseCLI(const T &args) |
3713 | | { |
3714 | | return ParseArgs(args) == std::end(args); |
3715 | | } |
3716 | | }; |
3717 | | |
3718 | | inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_) |
3719 | 0 | : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser) |
3720 | 0 | { |
3721 | 0 | command.subparser = &parser; |
3722 | 0 | } |
3723 | | |
3724 | 0 | inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams ¶ms_): command(command_), parser(command, params_), oldSubparser(command.subparser) |
3725 | 0 | { |
3726 | 0 | command.subparser = &parser; |
3727 | 0 | } |
3728 | | |
3729 | | inline void Subparser::Parse() |
3730 | 0 | { |
3731 | 0 | isParsed = true; |
3732 | 0 | Reset(); |
3733 | 0 | command.subparserDescription = GetDescription(helpParams, 0); |
3734 | 0 | command.subparserHasFlag = HasFlag(); |
3735 | 0 | command.subparserHasPositional = HasPositional(); |
3736 | 0 | command.subparserHasCommand = HasCommand(); |
3737 | 0 | command.subparserProgramLine = GetProgramLine(helpParams); |
3738 | 0 | if (parser == nullptr) |
3739 | 0 | { |
3740 | 0 | #ifndef ARGS_NOEXCEPT |
3741 | 0 | throw args::SubparserError(); |
3742 | 0 | #else |
3743 | 0 | error = Error::Subparser; |
3744 | 0 | return; |
3745 | 0 | #endif |
3746 | 0 | } |
3747 | 0 |
|
3748 | 0 | auto it = parser->Parse(args.begin(), args.end()); |
3749 | 0 | command.Validate(parser->ShortPrefix(), parser->LongPrefix()); |
3750 | 0 | kicked.assign(it, args.end()); |
3751 | 0 |
|
3752 | 0 | #ifdef ARGS_NOEXCEPT |
3753 | 0 | command.subparserError = GetError(); |
3754 | 0 | #endif |
3755 | 0 | } |
3756 | | |
3757 | | inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser) |
3758 | 0 | { |
3759 | 0 | parser.Help(os); |
3760 | 0 | return os; |
3761 | 0 | } |
3762 | | |
3763 | | /** Boolean argument matcher |
3764 | | */ |
3765 | | class Flag : public FlagBase |
3766 | | { |
3767 | | public: |
3768 | 2.94k | Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_) |
3769 | 2.94k | { |
3770 | 2.94k | group_.Add(*this); |
3771 | 2.94k | } |
3772 | | |
3773 | 1.96k | 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) |
3774 | 1.96k | { |
3775 | 1.96k | } |
3776 | | |
3777 | 0 | virtual ~Flag() {} |
3778 | | |
3779 | | /** Get whether this was matched |
3780 | | */ |
3781 | | bool Get() const |
3782 | 0 | { |
3783 | 0 | return Matched(); |
3784 | 0 | } |
3785 | | |
3786 | | virtual Nargs NumberOfArguments() const noexcept override |
3787 | 28.0k | { |
3788 | 28.0k | return 0; |
3789 | 28.0k | } |
3790 | | |
3791 | | virtual void ParseValue(const std::vector<std::string>&) override |
3792 | 27.9k | { |
3793 | 27.9k | } |
3794 | | }; |
3795 | | |
3796 | | /** Help flag class |
3797 | | * |
3798 | | * Works like a regular flag, but throws an instance of Help when it is matched |
3799 | | */ |
3800 | | class HelpFlag : public Flag |
3801 | | { |
3802 | | public: |
3803 | 983 | HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {} |
3804 | | |
3805 | 0 | virtual ~HelpFlag() {} |
3806 | | |
3807 | | virtual void ParseValue(const std::vector<std::string> &) |
3808 | 21 | { |
3809 | | #ifdef ARGS_NOEXCEPT |
3810 | | error = Error::Help; |
3811 | | errorMsg = Name(); |
3812 | | #else |
3813 | 21 | throw Help(Name()); |
3814 | 21 | #endif |
3815 | 21 | } |
3816 | | |
3817 | | /** Get whether this was matched |
3818 | | */ |
3819 | | bool Get() const noexcept |
3820 | 0 | { |
3821 | 0 | return Matched(); |
3822 | 0 | } |
3823 | | }; |
3824 | | |
3825 | | /** A flag class that simply counts the number of times it's matched |
3826 | | */ |
3827 | | class CounterFlag : public Flag |
3828 | | { |
3829 | | private: |
3830 | | const int startcount; |
3831 | | int count; |
3832 | | |
3833 | | public: |
3834 | | CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}): |
3835 | 0 | Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {} |
3836 | | |
3837 | 0 | virtual ~CounterFlag() {} |
3838 | | |
3839 | | virtual FlagBase *Match(const EitherFlag &arg) override |
3840 | 0 | { |
3841 | 0 | auto me = FlagBase::Match(arg); |
3842 | 0 | if (me) |
3843 | 0 | { |
3844 | 0 | #ifdef ARGS_NOEXCEPT |
3845 | 0 | // Suppress increment when FlagBase::Match recorded an |
3846 | 0 | // error on this same call (e.g. Options::Single violated). |
3847 | 0 | // In non-noexcept mode that path would have thrown before |
3848 | 0 | // reaching here and the count would not have advanced. |
3849 | 0 | if (GetError() != Error::None) |
3850 | 0 | { |
3851 | 0 | return me; |
3852 | 0 | } |
3853 | 0 | #endif |
3854 | 0 | ++count; |
3855 | 0 | } |
3856 | 0 | return me; |
3857 | 0 | } |
3858 | | |
3859 | | /** Get the count |
3860 | | */ |
3861 | | int &Get() noexcept |
3862 | 0 | { |
3863 | 0 | return count; |
3864 | 0 | } |
3865 | | |
3866 | 0 | int &operator *() noexcept { |
3867 | 0 | return count; |
3868 | 0 | } |
3869 | | |
3870 | 0 | const int &operator *() const noexcept { |
3871 | 0 | return count; |
3872 | 0 | } |
3873 | | |
3874 | | virtual void Reset() noexcept override |
3875 | 0 | { |
3876 | 0 | FlagBase::Reset(); |
3877 | 0 | count = startcount; |
3878 | 0 | } |
3879 | | }; |
3880 | | |
3881 | | /** A flag class that calls a function when it's matched |
3882 | | */ |
3883 | | class ActionFlag : public FlagBase |
3884 | | { |
3885 | | private: |
3886 | | std::function<void(const std::vector<std::string> &)> action; |
3887 | | Nargs nargs; |
3888 | | |
3889 | | public: |
3890 | | 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_ = {}): |
3891 | | FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_) |
3892 | 0 | { |
3893 | 0 | group_.Add(*this); |
3894 | 0 | } |
3895 | | |
3896 | | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}): |
3897 | | FlagBase(name_, help_, std::move(matcher_), options_), nargs(1) |
3898 | 0 | { |
3899 | 0 | group_.Add(*this); |
3900 | 0 | action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); }; |
3901 | 0 | } |
3902 | | |
3903 | | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}): |
3904 | | FlagBase(name_, help_, std::move(matcher_), options_), nargs(0) |
3905 | 0 | { |
3906 | 0 | group_.Add(*this); |
3907 | 0 | action = [action_](const std::vector<std::string> &) { return action_(); }; |
3908 | 0 | } |
3909 | | |
3910 | | virtual Nargs NumberOfArguments() const noexcept override |
3911 | 0 | { return nargs; } |
3912 | | |
3913 | | virtual void ParseValue(const std::vector<std::string> &value) override |
3914 | 0 | { action(value); } |
3915 | | }; |
3916 | | |
3917 | | /** A default Reader class for argument classes |
3918 | | * |
3919 | | * If destination type is assignable to std::string it uses an assignment to std::string. |
3920 | | * Otherwise ValueReader simply uses a std::istringstream to read into the destination type, and |
3921 | | * raises a ParseError if there are any characters left. |
3922 | | */ |
3923 | | struct ValueReader |
3924 | | { |
3925 | | private: |
3926 | | template <typename T> |
3927 | | static typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, bool>::type |
3928 | | HasUnsignedNegativeSign(const std::string &value) |
3929 | | { |
3930 | | const auto firstNonSpace = std::find_if_not(value.begin(), value.end(), [](char c) |
3931 | | { |
3932 | | return std::isspace(static_cast<unsigned char>(c)) != 0; |
3933 | | }); |
3934 | | |
3935 | | return firstNonSpace != value.end() && *firstNonSpace == '-'; |
3936 | | } |
3937 | | |
3938 | | template <typename T> |
3939 | | static typename std::enable_if<!std::is_integral<T>::value || !std::is_unsigned<T>::value, bool>::type |
3940 | | HasUnsignedNegativeSign(const std::string &) |
3941 | | { |
3942 | | return false; |
3943 | | } |
3944 | | |
3945 | | public: |
3946 | | template <typename T> |
3947 | | typename std::enable_if< |
3948 | | std::is_integral<T>::value && |
3949 | | !std::is_same<T, bool>::value && |
3950 | | !std::is_same<T, char>::value && |
3951 | | !std::is_same<T, signed char>::value && |
3952 | | !std::is_same<T, unsigned char>::value, |
3953 | | bool>::type |
3954 | | ParseNumericValue(const std::string &value, T &destination) |
3955 | | { |
3956 | | if (HasUnsignedNegativeSign<T>(value)) |
3957 | | { |
3958 | | return false; |
3959 | | } |
3960 | | |
3961 | | const char *begin = value.c_str(); |
3962 | | // The true end of the value, derived from its length rather than |
3963 | | // from the first NUL. strtoull/strtoll treat the buffer as a C |
3964 | | // string and stop at an embedded '\0', so checking `*end == '\0'` |
3965 | | // for "no trailing data" is defeated by a value like "12\0junk": |
3966 | | // end lands on the embedded NUL and the junk after it is silently |
3967 | | // accepted. Comparing against `stop` validates the whole string |
3968 | | // and matches the istringstream-based reader used for other types. |
3969 | | const char *const stop = begin + value.size(); |
3970 | | |
3971 | | // C++11-compatible: use strtoull/strtoll. Hardening retained from |
3972 | | // the original from_chars draft (errno save/restore, ERANGE check, |
3973 | | // narrowing range check, trailing-whitespace tolerance). No |
3974 | | // unconditional dependency on <charconv> / C++17. |
3975 | | const int saved_errno = errno; |
3976 | | errno = 0; |
3977 | | |
3978 | | char *end = nullptr; |
3979 | | |
3980 | | if (std::is_unsigned<T>::value) |
3981 | | { |
3982 | | const unsigned long long parsed = std::strtoull(begin, &end, 0); |
3983 | | if (end == begin) |
3984 | | { |
3985 | | errno = saved_errno; |
3986 | | return false; |
3987 | | } |
3988 | | while (end != stop && std::isspace(static_cast<unsigned char>(*end))) |
3989 | | { |
3990 | | ++end; |
3991 | | } |
3992 | | if (end != stop || errno == ERANGE || |
3993 | | parsed > static_cast<unsigned long long>(std::numeric_limits<T>::max())) |
3994 | | { |
3995 | | errno = saved_errno; |
3996 | | return false; |
3997 | | } |
3998 | | |
3999 | | destination = static_cast<T>(parsed); |
4000 | | } |
4001 | | else |
4002 | | { |
4003 | | const long long parsed = std::strtoll(begin, &end, 0); |
4004 | | if (end == begin) |
4005 | | { |
4006 | | errno = saved_errno; |
4007 | | return false; |
4008 | | } |
4009 | | while (end != stop && std::isspace(static_cast<unsigned char>(*end))) |
4010 | | { |
4011 | | ++end; |
4012 | | } |
4013 | | if (end != stop || errno == ERANGE || |
4014 | | parsed < static_cast<long long>(std::numeric_limits<T>::min()) || |
4015 | | parsed > static_cast<long long>(std::numeric_limits<T>::max())) |
4016 | | { |
4017 | | errno = saved_errno; |
4018 | | return false; |
4019 | | } |
4020 | | |
4021 | | destination = static_cast<T>(parsed); |
4022 | | } |
4023 | | |
4024 | | errno = saved_errno; |
4025 | | return true; |
4026 | | } |
4027 | | |
4028 | | template <typename T> |
4029 | | typename std::enable_if< |
4030 | | !std::is_integral<T>::value || |
4031 | | std::is_same<T, bool>::value || |
4032 | | std::is_same<T, char>::value || |
4033 | | std::is_same<T, signed char>::value || |
4034 | | std::is_same<T, unsigned char>::value, |
4035 | | bool>::type |
4036 | | ParseNumericValue(const std::string &value, T &destination) |
4037 | | { |
4038 | | std::istringstream ss(value); |
4039 | | // Pin parsing to the C locale so that the decimal separator and |
4040 | | // thousands grouping behavior do not silently depend on whatever |
4041 | | // std::locale::global was last set to elsewhere in the process. |
4042 | | // Without this, e.g. "3.14" parses as 3 (with ".14" trailing) in |
4043 | | // any locale whose numpunct facet treats ',' as the decimal point. |
4044 | | ss.imbue(std::locale::classic()); |
4045 | | ss >> destination; |
4046 | | if (ss.fail()) |
4047 | | { |
4048 | | return false; |
4049 | | } |
4050 | | |
4051 | | // Check for trailing garbage by attempting to extract any remaining characters. |
4052 | | // Do not use 'ss >> std::ws' followed by peek(), as std::ws can set failbit |
4053 | | // on EOF, causing false rejection of valid input. |
4054 | | char extra = '\0'; |
4055 | | ss >> std::ws >> extra; |
4056 | | // If extraction succeeded, there's trailing garbage (return false). |
4057 | | // If extraction failed due to EOF only (goodbit after ws extraction), it's valid (return true). |
4058 | | // If extraction failed for other reasons, it's invalid (return false). |
4059 | | if (ss.fail()) |
4060 | | { |
4061 | | // Clear the failbit to check if EOF is the only issue |
4062 | | ss.clear(ss.rdstate() & ~std::ios::failbit); |
4063 | | return ss.eof(); |
4064 | | } |
4065 | | // Extraction succeeded, meaning there's trailing garbage |
4066 | | return false; |
4067 | | } |
4068 | | |
4069 | | template <typename T> |
4070 | | typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type |
4071 | | operator ()(const std::string &name, const std::string &value, T &destination) |
4072 | | { |
4073 | | const bool success = ParseNumericValue(value, destination); |
4074 | | if (!success) |
4075 | | { |
4076 | | #ifdef ARGS_NOEXCEPT |
4077 | | (void)name; |
4078 | | return false; |
4079 | | #else |
4080 | | std::ostringstream problem; |
4081 | | problem << "Argument '" << name << "' received invalid value type '" << value << "'"; |
4082 | | throw ParseError(problem.str()); |
4083 | | #endif |
4084 | | } |
4085 | | return true; |
4086 | | } |
4087 | | |
4088 | | template <typename T> |
4089 | | typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type |
4090 | | operator()(const std::string &, const std::string &value, T &destination) |
4091 | | { |
4092 | | destination = value; |
4093 | | return true; |
4094 | | } |
4095 | | }; |
4096 | | |
4097 | | /** An argument-accepting flag class |
4098 | | * |
4099 | | * \tparam T the type to extract the argument as |
4100 | | * \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) |
4101 | | */ |
4102 | | template < |
4103 | | typename T, |
4104 | | typename Reader = ValueReader> |
4105 | | class ValueFlag : public ValueFlagBase |
4106 | | { |
4107 | | protected: |
4108 | | T value; |
4109 | | T defaultValue; |
4110 | | |
4111 | | virtual std::string GetDefaultString(const HelpParams&) const override |
4112 | | { |
4113 | | return detail::ToString(defaultValue); |
4114 | | } |
4115 | | |
4116 | | private: |
4117 | | Reader reader; |
4118 | | |
4119 | | public: |
4120 | | |
4121 | | 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_) |
4122 | | { |
4123 | | group_.Add(*this); |
4124 | | } |
4125 | | |
4126 | | 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) |
4127 | | { |
4128 | | } |
4129 | | |
4130 | | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_) |
4131 | | { |
4132 | | } |
4133 | | |
4134 | | virtual ~ValueFlag() {} |
4135 | | |
4136 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4137 | | { |
4138 | | const std::string &value_ = values_.at(0); |
4139 | | |
4140 | | #ifdef ARGS_NOEXCEPT |
4141 | | if (!reader(name, value_, this->value)) |
4142 | | { |
4143 | | error = Error::Parse; |
4144 | | } |
4145 | | #else |
4146 | | reader(name, value_, this->value); |
4147 | | #endif |
4148 | | } |
4149 | | |
4150 | | virtual void Reset() noexcept override |
4151 | | { |
4152 | | ValueFlagBase::Reset(); |
4153 | | value = defaultValue; |
4154 | | } |
4155 | | |
4156 | | /** Get the value |
4157 | | */ |
4158 | | T &Get() noexcept |
4159 | | { |
4160 | | return value; |
4161 | | } |
4162 | | |
4163 | | /** Get the value |
4164 | | */ |
4165 | | T &operator *() noexcept |
4166 | | { |
4167 | | return value; |
4168 | | } |
4169 | | |
4170 | | /** Get the value |
4171 | | */ |
4172 | | const T &operator *() const noexcept |
4173 | | { |
4174 | | return value; |
4175 | | } |
4176 | | |
4177 | | /** Get the value |
4178 | | */ |
4179 | | T *operator ->() noexcept |
4180 | | { |
4181 | | return &value; |
4182 | | } |
4183 | | |
4184 | | /** Get the value |
4185 | | */ |
4186 | | const T *operator ->() const noexcept |
4187 | | { |
4188 | | return &value; |
4189 | | } |
4190 | | |
4191 | | /** Get the default value |
4192 | | */ |
4193 | | const T &GetDefault() noexcept |
4194 | | { |
4195 | | return defaultValue; |
4196 | | } |
4197 | | }; |
4198 | | |
4199 | | /** An optional argument-accepting flag class |
4200 | | * |
4201 | | * \tparam T the type to extract the argument as |
4202 | | * \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) |
4203 | | */ |
4204 | | template < |
4205 | | typename T, |
4206 | | typename Reader = ValueReader> |
4207 | | class ImplicitValueFlag : public ValueFlag<T, Reader> |
4208 | | { |
4209 | | protected: |
4210 | | T implicitValue; |
4211 | | |
4212 | | public: |
4213 | | |
4214 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {}) |
4215 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_) |
4216 | | { |
4217 | | } |
4218 | | |
4219 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {}) |
4220 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_) |
4221 | | { |
4222 | | } |
4223 | | |
4224 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) |
4225 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue() |
4226 | | { |
4227 | | } |
4228 | | |
4229 | | virtual ~ImplicitValueFlag() {} |
4230 | | |
4231 | | virtual Nargs NumberOfArguments() const noexcept override |
4232 | | { |
4233 | | return {0, 1}; |
4234 | | } |
4235 | | |
4236 | | virtual void ParseValue(const std::vector<std::string> &value_) override |
4237 | | { |
4238 | | if (value_.empty()) |
4239 | | { |
4240 | | this->value = implicitValue; |
4241 | | } else |
4242 | | { |
4243 | | ValueFlag<T, Reader>::ParseValue(value_); |
4244 | | } |
4245 | | } |
4246 | | }; |
4247 | | |
4248 | | /** A boolean flag containing a retrievable constant. |
4249 | | * |
4250 | | * \tparam T the type of the constant |
4251 | | */ |
4252 | | template <typename T> |
4253 | | class ConstantFlag : public Flag |
4254 | | { |
4255 | | T value; |
4256 | | |
4257 | | public: |
4258 | | |
4259 | | ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_, const T& value_): |
4260 | | Flag(group_, name_, help_, std::move(matcher_), options_), |
4261 | | value(value_) |
4262 | | {} |
4263 | | |
4264 | | ConstantFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T& value_, bool extraError_ = false): |
4265 | | Flag(group_, name_, help_, std::move(matcher_), extraError_), |
4266 | | value(value_) |
4267 | | {} |
4268 | | |
4269 | | T operator * () const noexcept |
4270 | | { |
4271 | | return value; |
4272 | | } |
4273 | | |
4274 | | T Get() const noexcept |
4275 | | { |
4276 | | return value; |
4277 | | } |
4278 | | |
4279 | | const T *operator -> () const noexcept |
4280 | | { |
4281 | | return &value; |
4282 | | } |
4283 | | }; |
4284 | | |
4285 | | /** A variadic arguments accepting flag class |
4286 | | * |
4287 | | * \tparam T the type to extract the argument as |
4288 | | * \tparam List the list type that houses the values |
4289 | | * \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) |
4290 | | */ |
4291 | | template < |
4292 | | typename T, |
4293 | | template <typename...> class List = detail::vector, |
4294 | | typename Reader = ValueReader> |
4295 | | class NargsValueFlag : public FlagBase |
4296 | | { |
4297 | | protected: |
4298 | | |
4299 | | List<T> values; |
4300 | | const List<T> defaultValues; |
4301 | | Nargs nargs; |
4302 | | Reader reader; |
4303 | | |
4304 | | public: |
4305 | | |
4306 | | typedef List<T> Container; |
4307 | | typedef T value_type; |
4308 | | typedef typename Container::allocator_type allocator_type; |
4309 | | typedef typename Container::pointer pointer; |
4310 | | typedef typename Container::const_pointer const_pointer; |
4311 | | typedef T& reference; |
4312 | | typedef const T& const_reference; |
4313 | | typedef typename Container::size_type size_type; |
4314 | | typedef typename Container::difference_type difference_type; |
4315 | | typedef typename Container::iterator iterator; |
4316 | | typedef typename Container::const_iterator const_iterator; |
4317 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4318 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4319 | | |
4320 | | NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {}) |
4321 | | : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_) |
4322 | | { |
4323 | | group_.Add(*this); |
4324 | | } |
4325 | | |
4326 | | virtual ~NargsValueFlag() {} |
4327 | | |
4328 | | virtual Nargs NumberOfArguments() const noexcept override |
4329 | | { |
4330 | | return nargs; |
4331 | | } |
4332 | | |
4333 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4334 | | { |
4335 | | values.clear(); |
4336 | | |
4337 | | for (const std::string &value : values_) |
4338 | | { |
4339 | | T v {}; |
4340 | | #ifdef ARGS_NOEXCEPT |
4341 | | if (!reader(name, value, v)) |
4342 | | { |
4343 | | error = Error::Parse; |
4344 | | return; |
4345 | | } |
4346 | | #else |
4347 | | reader(name, value, v); |
4348 | | #endif |
4349 | | values.insert(std::end(values), v); |
4350 | | } |
4351 | | } |
4352 | | |
4353 | | List<T> &Get() noexcept |
4354 | | { |
4355 | | return values; |
4356 | | } |
4357 | | |
4358 | | /** Get the value |
4359 | | */ |
4360 | | List<T> &operator *() noexcept |
4361 | | { |
4362 | | return values; |
4363 | | } |
4364 | | |
4365 | | /** Get the values |
4366 | | */ |
4367 | | const List<T> &operator *() const noexcept |
4368 | | { |
4369 | | return values; |
4370 | | } |
4371 | | |
4372 | | /** Get the values |
4373 | | */ |
4374 | | List<T> *operator ->() noexcept |
4375 | | { |
4376 | | return &values; |
4377 | | } |
4378 | | |
4379 | | /** Get the values |
4380 | | */ |
4381 | | const List<T> *operator ->() const noexcept |
4382 | | { |
4383 | | return &values; |
4384 | | } |
4385 | | |
4386 | | iterator begin() noexcept |
4387 | | { |
4388 | | return values.begin(); |
4389 | | } |
4390 | | |
4391 | | const_iterator begin() const noexcept |
4392 | | { |
4393 | | return values.begin(); |
4394 | | } |
4395 | | |
4396 | | const_iterator cbegin() const noexcept |
4397 | | { |
4398 | | return values.cbegin(); |
4399 | | } |
4400 | | |
4401 | | iterator end() noexcept |
4402 | | { |
4403 | | return values.end(); |
4404 | | } |
4405 | | |
4406 | | const_iterator end() const noexcept |
4407 | | { |
4408 | | return values.end(); |
4409 | | } |
4410 | | |
4411 | | const_iterator cend() const noexcept |
4412 | | { |
4413 | | return values.cend(); |
4414 | | } |
4415 | | |
4416 | | virtual void Reset() noexcept override |
4417 | | { |
4418 | | FlagBase::Reset(); |
4419 | | values = defaultValues; |
4420 | | } |
4421 | | |
4422 | | virtual FlagBase *Match(const EitherFlag &arg) override |
4423 | | { |
4424 | | const bool wasMatched = Matched(); |
4425 | | auto me = FlagBase::Match(arg); |
4426 | | if (me && !wasMatched) |
4427 | | { |
4428 | | values.clear(); |
4429 | | } |
4430 | | return me; |
4431 | | } |
4432 | | }; |
4433 | | |
4434 | | /** An argument-accepting flag class that pushes the found values into a list |
4435 | | * |
4436 | | * \tparam T the type to extract the argument as |
4437 | | * \tparam List the list type that houses the values |
4438 | | * \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) |
4439 | | */ |
4440 | | template < |
4441 | | typename T, |
4442 | | template <typename...> class List = detail::vector, |
4443 | | typename Reader = ValueReader> |
4444 | | class ValueFlagList : public ValueFlagBase |
4445 | | { |
4446 | | private: |
4447 | | using Container = List<T>; |
4448 | | Container values; |
4449 | | const Container defaultValues; |
4450 | | Reader reader; |
4451 | | |
4452 | | public: |
4453 | | |
4454 | | typedef T value_type; |
4455 | | typedef typename Container::allocator_type allocator_type; |
4456 | | typedef typename Container::pointer pointer; |
4457 | | typedef typename Container::const_pointer const_pointer; |
4458 | | typedef T& reference; |
4459 | | typedef const T& const_reference; |
4460 | | typedef typename Container::size_type size_type; |
4461 | | typedef typename Container::difference_type difference_type; |
4462 | | typedef typename Container::iterator iterator; |
4463 | | typedef typename Container::const_iterator const_iterator; |
4464 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4465 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4466 | | |
4467 | | ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}): |
4468 | | ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_) |
4469 | | { |
4470 | | group_.Add(*this); |
4471 | | } |
4472 | | |
4473 | | virtual ~ValueFlagList() {} |
4474 | | |
4475 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4476 | | { |
4477 | | const std::string &value_ = values_.at(0); |
4478 | | |
4479 | | T v{}; |
4480 | | #ifdef ARGS_NOEXCEPT |
4481 | | if (!reader(name, value_, v)) |
4482 | | { |
4483 | | error = Error::Parse; |
4484 | | return; |
4485 | | } |
4486 | | #else |
4487 | | reader(name, value_, v); |
4488 | | #endif |
4489 | | values.insert(std::end(values), v); |
4490 | | } |
4491 | | |
4492 | | /** Get the values |
4493 | | */ |
4494 | | Container &Get() noexcept |
4495 | | { |
4496 | | return values; |
4497 | | } |
4498 | | |
4499 | | /** Get the value |
4500 | | */ |
4501 | | Container &operator *() noexcept |
4502 | | { |
4503 | | return values; |
4504 | | } |
4505 | | |
4506 | | /** Get the values |
4507 | | */ |
4508 | | const Container &operator *() const noexcept |
4509 | | { |
4510 | | return values; |
4511 | | } |
4512 | | |
4513 | | /** Get the values |
4514 | | */ |
4515 | | Container *operator ->() noexcept |
4516 | | { |
4517 | | return &values; |
4518 | | } |
4519 | | |
4520 | | /** Get the values |
4521 | | */ |
4522 | | const Container *operator ->() const noexcept |
4523 | | { |
4524 | | return &values; |
4525 | | } |
4526 | | |
4527 | | virtual std::string Name() const override |
4528 | | { |
4529 | | return name + std::string("..."); |
4530 | | } |
4531 | | |
4532 | | virtual void Reset() noexcept override |
4533 | | { |
4534 | | ValueFlagBase::Reset(); |
4535 | | values = defaultValues; |
4536 | | } |
4537 | | |
4538 | | virtual FlagBase *Match(const EitherFlag &arg) override |
4539 | | { |
4540 | | const bool wasMatched = Matched(); |
4541 | | auto me = FlagBase::Match(arg); |
4542 | | if (me && !wasMatched) |
4543 | | { |
4544 | | values.clear(); |
4545 | | } |
4546 | | return me; |
4547 | | } |
4548 | | |
4549 | | iterator begin() noexcept |
4550 | | { |
4551 | | return values.begin(); |
4552 | | } |
4553 | | |
4554 | | const_iterator begin() const noexcept |
4555 | | { |
4556 | | return values.begin(); |
4557 | | } |
4558 | | |
4559 | | const_iterator cbegin() const noexcept |
4560 | | { |
4561 | | return values.cbegin(); |
4562 | | } |
4563 | | |
4564 | | iterator end() noexcept |
4565 | | { |
4566 | | return values.end(); |
4567 | | } |
4568 | | |
4569 | | const_iterator end() const noexcept |
4570 | | { |
4571 | | return values.end(); |
4572 | | } |
4573 | | |
4574 | | const_iterator cend() const noexcept |
4575 | | { |
4576 | | return values.cend(); |
4577 | | } |
4578 | | }; |
4579 | | |
4580 | | /** A mapping value flag class |
4581 | | * |
4582 | | * \tparam K the type to extract the argument as |
4583 | | * \tparam T the type to store the result as |
4584 | | * \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) |
4585 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
4586 | | */ |
4587 | | template < |
4588 | | typename K, |
4589 | | typename T, |
4590 | | typename Reader = ValueReader, |
4591 | | template <typename...> class Map = detail::unordered_map> |
4592 | | class MapFlag : public ValueFlagBase |
4593 | | { |
4594 | | private: |
4595 | | const Map<K, T> map; |
4596 | | T value; |
4597 | | const T defaultValue; |
4598 | | Reader reader; |
4599 | | |
4600 | | protected: |
4601 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
4602 | | { |
4603 | | return detail::MapKeysToStrings(map); |
4604 | | } |
4605 | | |
4606 | | public: |
4607 | | |
4608 | | 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_) |
4609 | | { |
4610 | | group_.Add(*this); |
4611 | | } |
4612 | | |
4613 | | 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) |
4614 | | { |
4615 | | } |
4616 | | |
4617 | | 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_) |
4618 | | { |
4619 | | } |
4620 | | |
4621 | | virtual ~MapFlag() {} |
4622 | | |
4623 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4624 | | { |
4625 | | const std::string &value_ = values_.at(0); |
4626 | | |
4627 | | K key{}; |
4628 | | #ifdef ARGS_NOEXCEPT |
4629 | | if (!reader(name, value_, key)) |
4630 | | { |
4631 | | error = Error::Parse; |
4632 | | return; |
4633 | | } |
4634 | | #else |
4635 | | reader(name, value_, key); |
4636 | | #endif |
4637 | | auto it = map.find(key); |
4638 | | if (it == std::end(map)) |
4639 | | { |
4640 | | std::ostringstream problem; |
4641 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
4642 | | #ifdef ARGS_NOEXCEPT |
4643 | | error = Error::Map; |
4644 | | errorMsg = problem.str(); |
4645 | | #else |
4646 | | throw MapError(problem.str()); |
4647 | | #endif |
4648 | | } else |
4649 | | { |
4650 | | this->value = it->second; |
4651 | | } |
4652 | | } |
4653 | | |
4654 | | /** Get the value |
4655 | | */ |
4656 | | T &Get() noexcept |
4657 | | { |
4658 | | return value; |
4659 | | } |
4660 | | |
4661 | | /** Get the value |
4662 | | */ |
4663 | | T &operator *() noexcept |
4664 | | { |
4665 | | return value; |
4666 | | } |
4667 | | |
4668 | | /** Get the value |
4669 | | */ |
4670 | | const T &operator *() const noexcept |
4671 | | { |
4672 | | return value; |
4673 | | } |
4674 | | |
4675 | | /** Get the value |
4676 | | */ |
4677 | | T *operator ->() noexcept |
4678 | | { |
4679 | | return &value; |
4680 | | } |
4681 | | |
4682 | | /** Get the value |
4683 | | */ |
4684 | | const T *operator ->() const noexcept |
4685 | | { |
4686 | | return &value; |
4687 | | } |
4688 | | |
4689 | | virtual void Reset() noexcept override |
4690 | | { |
4691 | | ValueFlagBase::Reset(); |
4692 | | value = defaultValue; |
4693 | | } |
4694 | | }; |
4695 | | |
4696 | | /** A mapping value flag list class |
4697 | | * |
4698 | | * \tparam K the type to extract the argument as |
4699 | | * \tparam T the type to store the result as |
4700 | | * \tparam List the list type that houses the values |
4701 | | * \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) |
4702 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
4703 | | */ |
4704 | | template < |
4705 | | typename K, |
4706 | | typename T, |
4707 | | template <typename...> class List = detail::vector, |
4708 | | typename Reader = ValueReader, |
4709 | | template <typename...> class Map = detail::unordered_map> |
4710 | | class MapFlagList : public ValueFlagBase |
4711 | | { |
4712 | | private: |
4713 | | using Container = List<T>; |
4714 | | const Map<K, T> map; |
4715 | | Container values; |
4716 | | const Container defaultValues; |
4717 | | Reader reader; |
4718 | | |
4719 | | protected: |
4720 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
4721 | | { |
4722 | | return detail::MapKeysToStrings(map); |
4723 | | } |
4724 | | |
4725 | | public: |
4726 | | typedef T value_type; |
4727 | | typedef typename Container::allocator_type allocator_type; |
4728 | | typedef typename Container::pointer pointer; |
4729 | | typedef typename Container::const_pointer const_pointer; |
4730 | | typedef T& reference; |
4731 | | typedef const T& const_reference; |
4732 | | typedef typename Container::size_type size_type; |
4733 | | typedef typename Container::difference_type difference_type; |
4734 | | typedef typename Container::iterator iterator; |
4735 | | typedef typename Container::const_iterator const_iterator; |
4736 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4737 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4738 | | |
4739 | | MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}): |
4740 | | ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), values(defaultValues_), defaultValues(defaultValues_) |
4741 | | { |
4742 | | group_.Add(*this); |
4743 | | } |
4744 | | |
4745 | | virtual ~MapFlagList() {} |
4746 | | |
4747 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
4748 | | { |
4749 | | const std::string &value_ = values_.at(0); |
4750 | | |
4751 | | K key{}; |
4752 | | #ifdef ARGS_NOEXCEPT |
4753 | | if (!reader(name, value_, key)) |
4754 | | { |
4755 | | error = Error::Parse; |
4756 | | return; |
4757 | | } |
4758 | | #else |
4759 | | reader(name, value_, key); |
4760 | | #endif |
4761 | | auto it = map.find(key); |
4762 | | if (it == std::end(map)) |
4763 | | { |
4764 | | std::ostringstream problem; |
4765 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
4766 | | #ifdef ARGS_NOEXCEPT |
4767 | | error = Error::Map; |
4768 | | errorMsg = problem.str(); |
4769 | | #else |
4770 | | throw MapError(problem.str()); |
4771 | | #endif |
4772 | | } else |
4773 | | { |
4774 | | this->values.emplace_back(it->second); |
4775 | | } |
4776 | | } |
4777 | | |
4778 | | /** Get the value |
4779 | | */ |
4780 | | Container &Get() noexcept |
4781 | | { |
4782 | | return values; |
4783 | | } |
4784 | | |
4785 | | /** Get the value |
4786 | | */ |
4787 | | Container &operator *() noexcept |
4788 | | { |
4789 | | return values; |
4790 | | } |
4791 | | |
4792 | | /** Get the values |
4793 | | */ |
4794 | | const Container &operator *() const noexcept |
4795 | | { |
4796 | | return values; |
4797 | | } |
4798 | | |
4799 | | /** Get the values |
4800 | | */ |
4801 | | Container *operator ->() noexcept |
4802 | | { |
4803 | | return &values; |
4804 | | } |
4805 | | |
4806 | | /** Get the values |
4807 | | */ |
4808 | | const Container *operator ->() const noexcept |
4809 | | { |
4810 | | return &values; |
4811 | | } |
4812 | | |
4813 | | virtual std::string Name() const override |
4814 | | { |
4815 | | return name + std::string("..."); |
4816 | | } |
4817 | | |
4818 | | virtual void Reset() noexcept override |
4819 | | { |
4820 | | ValueFlagBase::Reset(); |
4821 | | values = defaultValues; |
4822 | | } |
4823 | | |
4824 | | virtual FlagBase *Match(const EitherFlag &arg) override |
4825 | | { |
4826 | | const bool wasMatched = Matched(); |
4827 | | auto me = FlagBase::Match(arg); |
4828 | | if (me && !wasMatched) |
4829 | | { |
4830 | | values.clear(); |
4831 | | } |
4832 | | return me; |
4833 | | } |
4834 | | |
4835 | | iterator begin() noexcept |
4836 | | { |
4837 | | return values.begin(); |
4838 | | } |
4839 | | |
4840 | | const_iterator begin() const noexcept |
4841 | | { |
4842 | | return values.begin(); |
4843 | | } |
4844 | | |
4845 | | const_iterator cbegin() const noexcept |
4846 | | { |
4847 | | return values.cbegin(); |
4848 | | } |
4849 | | |
4850 | | iterator end() noexcept |
4851 | | { |
4852 | | return values.end(); |
4853 | | } |
4854 | | |
4855 | | const_iterator end() const noexcept |
4856 | | { |
4857 | | return values.end(); |
4858 | | } |
4859 | | |
4860 | | const_iterator cend() const noexcept |
4861 | | { |
4862 | | return values.cend(); |
4863 | | } |
4864 | | }; |
4865 | | |
4866 | | /** A positional argument class |
4867 | | * |
4868 | | * \tparam T the type to extract the argument as |
4869 | | * \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) |
4870 | | */ |
4871 | | template < |
4872 | | typename T, |
4873 | | typename Reader = ValueReader> |
4874 | | class Positional : public PositionalBase |
4875 | | { |
4876 | | private: |
4877 | | T value; |
4878 | | const T defaultValue; |
4879 | | Reader reader; |
4880 | | public: |
4881 | | 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_) |
4882 | | { |
4883 | | group_.Add(*this); |
4884 | | } |
4885 | | |
4886 | | Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_) |
4887 | | { |
4888 | | } |
4889 | | |
4890 | | virtual ~Positional() {} |
4891 | | |
4892 | | virtual void ParseValue(const std::string &value_) override |
4893 | | { |
4894 | | #ifdef ARGS_NOEXCEPT |
4895 | | if (!reader(name, value_, this->value)) |
4896 | | { |
4897 | | error = Error::Parse; |
4898 | | return; |
4899 | | } |
4900 | | #else |
4901 | | reader(name, value_, this->value); |
4902 | | #endif |
4903 | | ready = false; |
4904 | | matched = true; |
4905 | | } |
4906 | | |
4907 | | /** Get the value |
4908 | | */ |
4909 | | T &Get() noexcept |
4910 | | { |
4911 | | return value; |
4912 | | } |
4913 | | |
4914 | | /** Get the value |
4915 | | */ |
4916 | | T &operator *() noexcept |
4917 | | { |
4918 | | return value; |
4919 | | } |
4920 | | |
4921 | | /** Get the value |
4922 | | */ |
4923 | | const T &operator *() const noexcept |
4924 | | { |
4925 | | return value; |
4926 | | } |
4927 | | |
4928 | | /** Get the value |
4929 | | */ |
4930 | | T *operator ->() noexcept |
4931 | | { |
4932 | | return &value; |
4933 | | } |
4934 | | |
4935 | | /** Get the value |
4936 | | */ |
4937 | | const T *operator ->() const noexcept |
4938 | | { |
4939 | | return &value; |
4940 | | } |
4941 | | |
4942 | | virtual void Reset() noexcept override |
4943 | | { |
4944 | | PositionalBase::Reset(); |
4945 | | value = defaultValue; |
4946 | | } |
4947 | | }; |
4948 | | |
4949 | | /** A positional argument class that pushes the found values into a list |
4950 | | * |
4951 | | * \tparam T the type to extract the argument as |
4952 | | * \tparam List the list type that houses the values |
4953 | | * \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) |
4954 | | */ |
4955 | | template < |
4956 | | typename T, |
4957 | | template <typename...> class List = detail::vector, |
4958 | | typename Reader = ValueReader> |
4959 | | class PositionalList : public PositionalBase |
4960 | | { |
4961 | | private: |
4962 | | using Container = List<T>; |
4963 | | Container values; |
4964 | | const Container defaultValues; |
4965 | | Reader reader; |
4966 | | |
4967 | | public: |
4968 | | typedef T value_type; |
4969 | | typedef typename Container::allocator_type allocator_type; |
4970 | | typedef typename Container::pointer pointer; |
4971 | | typedef typename Container::const_pointer const_pointer; |
4972 | | typedef T& reference; |
4973 | | typedef const T& const_reference; |
4974 | | typedef typename Container::size_type size_type; |
4975 | | typedef typename Container::difference_type difference_type; |
4976 | | typedef typename Container::iterator iterator; |
4977 | | typedef typename Container::const_iterator const_iterator; |
4978 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4979 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4980 | | |
4981 | | 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_) |
4982 | | { |
4983 | | group_.Add(*this); |
4984 | | } |
4985 | | |
4986 | | PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_) |
4987 | | { |
4988 | | } |
4989 | | |
4990 | | virtual ~PositionalList() {} |
4991 | | |
4992 | | virtual void ParseValue(const std::string &value_) override |
4993 | | { |
4994 | | T v{}; |
4995 | | #ifdef ARGS_NOEXCEPT |
4996 | | if (!reader(name, value_, v)) |
4997 | | { |
4998 | | error = Error::Parse; |
4999 | | return; |
5000 | | } |
5001 | | #else |
5002 | | reader(name, value_, v); |
5003 | | #endif |
5004 | | values.insert(std::end(values), v); |
5005 | | matched = true; |
5006 | | } |
5007 | | |
5008 | | virtual std::string Name() const override |
5009 | | { |
5010 | | return name + std::string("..."); |
5011 | | } |
5012 | | |
5013 | | /** Get the values |
5014 | | */ |
5015 | | Container &Get() noexcept |
5016 | | { |
5017 | | return values; |
5018 | | } |
5019 | | |
5020 | | /** Get the value |
5021 | | */ |
5022 | | Container &operator *() noexcept |
5023 | | { |
5024 | | return values; |
5025 | | } |
5026 | | |
5027 | | /** Get the values |
5028 | | */ |
5029 | | const Container &operator *() const noexcept |
5030 | | { |
5031 | | return values; |
5032 | | } |
5033 | | |
5034 | | /** Get the values |
5035 | | */ |
5036 | | Container *operator ->() noexcept |
5037 | | { |
5038 | | return &values; |
5039 | | } |
5040 | | |
5041 | | /** Get the values |
5042 | | */ |
5043 | | const Container *operator ->() const noexcept |
5044 | | { |
5045 | | return &values; |
5046 | | } |
5047 | | |
5048 | | virtual void Reset() noexcept override |
5049 | | { |
5050 | | PositionalBase::Reset(); |
5051 | | values = defaultValues; |
5052 | | } |
5053 | | |
5054 | | virtual PositionalBase *GetNextPositional() override |
5055 | | { |
5056 | | const bool wasMatched = Matched(); |
5057 | | auto me = PositionalBase::GetNextPositional(); |
5058 | | if (me && !wasMatched) |
5059 | | { |
5060 | | values.clear(); |
5061 | | } |
5062 | | return me; |
5063 | | } |
5064 | | |
5065 | | iterator begin() noexcept |
5066 | | { |
5067 | | return values.begin(); |
5068 | | } |
5069 | | |
5070 | | const_iterator begin() const noexcept |
5071 | | { |
5072 | | return values.begin(); |
5073 | | } |
5074 | | |
5075 | | const_iterator cbegin() const noexcept |
5076 | | { |
5077 | | return values.cbegin(); |
5078 | | } |
5079 | | |
5080 | | iterator end() noexcept |
5081 | | { |
5082 | | return values.end(); |
5083 | | } |
5084 | | |
5085 | | const_iterator end() const noexcept |
5086 | | { |
5087 | | return values.end(); |
5088 | | } |
5089 | | |
5090 | | const_iterator cend() const noexcept |
5091 | | { |
5092 | | return values.cend(); |
5093 | | } |
5094 | | }; |
5095 | | |
5096 | | /** A positional argument mapping class |
5097 | | * |
5098 | | * \tparam K the type to extract the argument as |
5099 | | * \tparam T the type to store the result as |
5100 | | * \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) |
5101 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
5102 | | */ |
5103 | | template < |
5104 | | typename K, |
5105 | | typename T, |
5106 | | typename Reader = ValueReader, |
5107 | | template <typename...> class Map = detail::unordered_map> |
5108 | | class MapPositional : public PositionalBase |
5109 | | { |
5110 | | private: |
5111 | | const Map<K, T> map; |
5112 | | T value; |
5113 | | const T defaultValue; |
5114 | | Reader reader; |
5115 | | |
5116 | | protected: |
5117 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
5118 | | { |
5119 | | return detail::MapKeysToStrings(map); |
5120 | | } |
5121 | | |
5122 | | public: |
5123 | | |
5124 | | MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}): |
5125 | | PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_) |
5126 | | { |
5127 | | group_.Add(*this); |
5128 | | } |
5129 | | |
5130 | | virtual ~MapPositional() {} |
5131 | | |
5132 | | virtual void ParseValue(const std::string &value_) override |
5133 | | { |
5134 | | K key{}; |
5135 | | #ifdef ARGS_NOEXCEPT |
5136 | | if (!reader(name, value_, key)) |
5137 | | { |
5138 | | error = Error::Parse; |
5139 | | return; |
5140 | | } |
5141 | | #else |
5142 | | reader(name, value_, key); |
5143 | | #endif |
5144 | | auto it = map.find(key); |
5145 | | if (it == std::end(map)) |
5146 | | { |
5147 | | std::ostringstream problem; |
5148 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
5149 | | #ifdef ARGS_NOEXCEPT |
5150 | | error = Error::Map; |
5151 | | errorMsg = problem.str(); |
5152 | | #else |
5153 | | throw MapError(problem.str()); |
5154 | | #endif |
5155 | | } else |
5156 | | { |
5157 | | this->value = it->second; |
5158 | | ready = false; |
5159 | | matched = true; |
5160 | | } |
5161 | | } |
5162 | | |
5163 | | /** Get the value |
5164 | | */ |
5165 | | T &Get() noexcept |
5166 | | { |
5167 | | return value; |
5168 | | } |
5169 | | |
5170 | | /** Get the value |
5171 | | */ |
5172 | | T &operator *() noexcept |
5173 | | { |
5174 | | return value; |
5175 | | } |
5176 | | |
5177 | | /** Get the value |
5178 | | */ |
5179 | | const T &operator *() const noexcept |
5180 | | { |
5181 | | return value; |
5182 | | } |
5183 | | |
5184 | | /** Get the value |
5185 | | */ |
5186 | | T *operator ->() noexcept |
5187 | | { |
5188 | | return &value; |
5189 | | } |
5190 | | |
5191 | | /** Get the value |
5192 | | */ |
5193 | | const T *operator ->() const noexcept |
5194 | | { |
5195 | | return &value; |
5196 | | } |
5197 | | |
5198 | | virtual void Reset() noexcept override |
5199 | | { |
5200 | | PositionalBase::Reset(); |
5201 | | value = defaultValue; |
5202 | | } |
5203 | | }; |
5204 | | |
5205 | | /** A positional argument mapping list class |
5206 | | * |
5207 | | * \tparam K the type to extract the argument as |
5208 | | * \tparam T the type to store the result as |
5209 | | * \tparam List the list type that houses the values |
5210 | | * \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) |
5211 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
5212 | | */ |
5213 | | template < |
5214 | | typename K, |
5215 | | typename T, |
5216 | | template <typename...> class List = detail::vector, |
5217 | | typename Reader = ValueReader, |
5218 | | template <typename...> class Map = detail::unordered_map> |
5219 | | class MapPositionalList : public PositionalBase |
5220 | | { |
5221 | | private: |
5222 | | using Container = List<T>; |
5223 | | |
5224 | | const Map<K, T> map; |
5225 | | Container values; |
5226 | | const Container defaultValues; |
5227 | | Reader reader; |
5228 | | |
5229 | | protected: |
5230 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
5231 | | { |
5232 | | return detail::MapKeysToStrings(map); |
5233 | | } |
5234 | | |
5235 | | public: |
5236 | | typedef T value_type; |
5237 | | typedef typename Container::allocator_type allocator_type; |
5238 | | typedef typename Container::pointer pointer; |
5239 | | typedef typename Container::const_pointer const_pointer; |
5240 | | typedef T& reference; |
5241 | | typedef const T& const_reference; |
5242 | | typedef typename Container::size_type size_type; |
5243 | | typedef typename Container::difference_type difference_type; |
5244 | | typedef typename Container::iterator iterator; |
5245 | | typedef typename Container::const_iterator const_iterator; |
5246 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
5247 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
5248 | | |
5249 | | MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}): |
5250 | | PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_) |
5251 | | { |
5252 | | group_.Add(*this); |
5253 | | } |
5254 | | |
5255 | | virtual ~MapPositionalList() {} |
5256 | | |
5257 | | virtual void ParseValue(const std::string &value_) override |
5258 | | { |
5259 | | K key{}; |
5260 | | #ifdef ARGS_NOEXCEPT |
5261 | | if (!reader(name, value_, key)) |
5262 | | { |
5263 | | error = Error::Parse; |
5264 | | return; |
5265 | | } |
5266 | | #else |
5267 | | reader(name, value_, key); |
5268 | | #endif |
5269 | | auto it = map.find(key); |
5270 | | if (it == std::end(map)) |
5271 | | { |
5272 | | std::ostringstream problem; |
5273 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
5274 | | #ifdef ARGS_NOEXCEPT |
5275 | | error = Error::Map; |
5276 | | errorMsg = problem.str(); |
5277 | | #else |
5278 | | throw MapError(problem.str()); |
5279 | | #endif |
5280 | | } else |
5281 | | { |
5282 | | this->values.emplace_back(it->second); |
5283 | | matched = true; |
5284 | | } |
5285 | | } |
5286 | | |
5287 | | /** Get the value |
5288 | | */ |
5289 | | Container &Get() noexcept |
5290 | | { |
5291 | | return values; |
5292 | | } |
5293 | | |
5294 | | /** Get the value |
5295 | | */ |
5296 | | Container &operator *() noexcept |
5297 | | { |
5298 | | return values; |
5299 | | } |
5300 | | |
5301 | | /** Get the values |
5302 | | */ |
5303 | | const Container &operator *() const noexcept |
5304 | | { |
5305 | | return values; |
5306 | | } |
5307 | | |
5308 | | /** Get the values |
5309 | | */ |
5310 | | Container *operator ->() noexcept |
5311 | | { |
5312 | | return &values; |
5313 | | } |
5314 | | |
5315 | | /** Get the values |
5316 | | */ |
5317 | | const Container *operator ->() const noexcept |
5318 | | { |
5319 | | return &values; |
5320 | | } |
5321 | | |
5322 | | virtual std::string Name() const override |
5323 | | { |
5324 | | return name + std::string("..."); |
5325 | | } |
5326 | | |
5327 | | virtual void Reset() noexcept override |
5328 | | { |
5329 | | PositionalBase::Reset(); |
5330 | | values = defaultValues; |
5331 | | } |
5332 | | |
5333 | | virtual PositionalBase *GetNextPositional() override |
5334 | | { |
5335 | | const bool wasMatched = Matched(); |
5336 | | auto me = PositionalBase::GetNextPositional(); |
5337 | | if (me && !wasMatched) |
5338 | | { |
5339 | | values.clear(); |
5340 | | } |
5341 | | return me; |
5342 | | } |
5343 | | |
5344 | | iterator begin() noexcept |
5345 | | { |
5346 | | return values.begin(); |
5347 | | } |
5348 | | |
5349 | | const_iterator begin() const noexcept |
5350 | | { |
5351 | | return values.begin(); |
5352 | | } |
5353 | | |
5354 | | const_iterator cbegin() const noexcept |
5355 | | { |
5356 | | return values.cbegin(); |
5357 | | } |
5358 | | |
5359 | | iterator end() noexcept |
5360 | | { |
5361 | | return values.end(); |
5362 | | } |
5363 | | |
5364 | | const_iterator end() const noexcept |
5365 | | { |
5366 | | return values.end(); |
5367 | | } |
5368 | | |
5369 | | const_iterator cend() const noexcept |
5370 | | { |
5371 | | return values.cend(); |
5372 | | } |
5373 | | }; |
5374 | | } |
5375 | | |
5376 | | #pragma pop_macro("min") |
5377 | | #pragma pop_macro("max") |
5378 | | #endif |