Line | Count | Source (jump to first uncovered line) |
1 | | /* A simple header-only C++ argument parser library. |
2 | | * |
3 | | * https://github.com/Taywee/args |
4 | | * |
5 | | * Copyright (c) 2016-2024 Taylor C. 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 | | |
36 | | #define ARGS_VERSION "6.4.7" |
37 | | #define ARGS_VERSION_MAJOR 6 |
38 | | #define ARGS_VERSION_MINOR 4 |
39 | | #define ARGS_VERSION_PATCH 7 |
40 | | |
41 | | #include <algorithm> |
42 | | #include <iterator> |
43 | | #include <exception> |
44 | | #include <functional> |
45 | | #include <sstream> |
46 | | #include <string> |
47 | | #include <tuple> |
48 | | #include <vector> |
49 | | #include <unordered_map> |
50 | | #include <unordered_set> |
51 | | #include <type_traits> |
52 | | #include <cstddef> |
53 | | #include <cctype> |
54 | | #include <iostream> |
55 | | |
56 | | #if defined(_MSC_VER) && _MSC_VER <= 1800 |
57 | | #define noexcept |
58 | | #endif |
59 | | |
60 | | #ifdef ARGS_TESTNAMESPACE |
61 | | namespace argstest |
62 | | { |
63 | | #else |
64 | | |
65 | | /** \namespace args |
66 | | * \brief contains all the functionality of the args library |
67 | | */ |
68 | | namespace args |
69 | | { |
70 | | #endif |
71 | | /** Getter to grab the value from the argument type. |
72 | | * |
73 | | * If the Get() function of the type returns a reference, so does this, and |
74 | | * the value will be modifiable. |
75 | | */ |
76 | | template <typename Option> |
77 | | auto get(Option &option_) -> decltype(option_.Get()) |
78 | | { |
79 | | return option_.Get(); |
80 | | } |
81 | | |
82 | | /** (INTERNAL) Count UTF-8 glyphs |
83 | | * |
84 | | * This is not reliable, and will fail for combinatory glyphs, but it's |
85 | | * good enough here for now. |
86 | | * |
87 | | * \param string The string to count glyphs from |
88 | | * \return The UTF-8 glyphs in the string |
89 | | */ |
90 | | inline std::string::size_type Glyphs(const std::string &string_) |
91 | 0 | { |
92 | 0 | std::string::size_type length = 0; |
93 | 0 | for (const char c: string_) |
94 | 0 | { |
95 | 0 | if ((c & 0xc0) != 0x80) |
96 | 0 | { |
97 | 0 | ++length; |
98 | 0 | } |
99 | 0 | } |
100 | 0 | return length; |
101 | 0 | } |
102 | | |
103 | | /** (INTERNAL) Wrap a vector of words into a vector of lines |
104 | | * |
105 | | * Empty words are skipped. Word "\n" forces wrapping. |
106 | | * |
107 | | * \param begin The begin iterator |
108 | | * \param end The end iterator |
109 | | * \param width The width of the body |
110 | | * \param firstlinewidth the width of the first line, defaults to the width of the body |
111 | | * \param firstlineindent the indent of the first line, defaults to 0 |
112 | | * \return the vector of lines |
113 | | */ |
114 | | template <typename It> |
115 | | inline std::vector<std::string> Wrap(It begin, |
116 | | It end, |
117 | | const std::string::size_type width, |
118 | | std::string::size_type firstlinewidth = 0, |
119 | | std::string::size_type firstlineindent = 0) |
120 | 0 | { |
121 | 0 | std::vector<std::string> output; |
122 | 0 | std::string line(firstlineindent, ' '); |
123 | 0 | bool empty = true; |
124 | 0 |
|
125 | 0 | if (firstlinewidth == 0) |
126 | 0 | { |
127 | 0 | firstlinewidth = width; |
128 | 0 | } |
129 | 0 |
|
130 | 0 | auto currentwidth = firstlinewidth; |
131 | 0 |
|
132 | 0 | for (auto it = begin; it != end; ++it) |
133 | 0 | { |
134 | 0 | if (it->empty()) |
135 | 0 | { |
136 | 0 | continue; |
137 | 0 | } |
138 | 0 |
|
139 | 0 | if (*it == "\n") |
140 | 0 | { |
141 | 0 | if (!empty) |
142 | 0 | { |
143 | 0 | output.push_back(line); |
144 | 0 | line.clear(); |
145 | 0 | empty = true; |
146 | 0 | currentwidth = width; |
147 | 0 | } |
148 | 0 |
|
149 | 0 | continue; |
150 | 0 | } |
151 | 0 |
|
152 | 0 | auto itemsize = Glyphs(*it); |
153 | 0 | if ((line.length() + 1 + itemsize) > currentwidth) |
154 | 0 | { |
155 | 0 | if (!empty) |
156 | 0 | { |
157 | 0 | output.push_back(line); |
158 | 0 | line.clear(); |
159 | 0 | empty = true; |
160 | 0 | currentwidth = width; |
161 | 0 | } |
162 | 0 | } |
163 | 0 |
|
164 | 0 | if (itemsize > 0) |
165 | 0 | { |
166 | 0 | if (!empty) |
167 | 0 | { |
168 | 0 | line += ' '; |
169 | 0 | } |
170 | 0 |
|
171 | 0 | line += *it; |
172 | 0 | empty = false; |
173 | 0 | } |
174 | 0 | } |
175 | 0 |
|
176 | 0 | if (!empty) |
177 | 0 | { |
178 | 0 | output.push_back(line); |
179 | 0 | } |
180 | 0 |
|
181 | 0 | return output; |
182 | 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) |
183 | | |
184 | | namespace detail |
185 | | { |
186 | | template <typename T> |
187 | | std::string Join(const T& array, const std::string &delimiter) |
188 | 0 | { |
189 | 0 | std::string res; |
190 | 0 | for (auto &element : array) |
191 | 0 | { |
192 | 0 | if (!res.empty()) |
193 | 0 | { |
194 | 0 | res += delimiter; |
195 | 0 | } |
196 | |
|
197 | 0 | res += element; |
198 | 0 | } |
199 | |
|
200 | 0 | return res; |
201 | 0 | } |
202 | | } |
203 | | |
204 | | /** (INTERNAL) Wrap a string into a vector of lines |
205 | | * |
206 | | * This is quick and hacky, but works well enough. You can specify a |
207 | | * different width for the first line |
208 | | * |
209 | | * \param width The width of the body |
210 | | * \param firstlinewid the width of the first line, defaults to the width of the body |
211 | | * \return the vector of lines |
212 | | */ |
213 | | inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0) |
214 | 0 | { |
215 | 0 | // Preserve existing line breaks |
216 | 0 | const auto newlineloc = in.find('\n'); |
217 | 0 | if (newlineloc != in.npos) |
218 | 0 | { |
219 | 0 | auto first = Wrap(std::string(in, 0, newlineloc), width); |
220 | 0 | auto second = Wrap(std::string(in, newlineloc + 1), width); |
221 | 0 | first.insert( |
222 | 0 | std::end(first), |
223 | 0 | std::make_move_iterator(std::begin(second)), |
224 | 0 | std::make_move_iterator(std::end(second))); |
225 | 0 | return first; |
226 | 0 | } |
227 | 0 |
|
228 | 0 | std::istringstream stream(in); |
229 | 0 | std::string::size_type indent = 0; |
230 | 0 |
|
231 | 0 | for (auto c : in) |
232 | 0 | { |
233 | 0 | if (!std::isspace(static_cast<unsigned char>(c))) |
234 | 0 | { |
235 | 0 | break; |
236 | 0 | } |
237 | 0 | ++indent; |
238 | 0 | } |
239 | 0 |
|
240 | 0 | return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(), |
241 | 0 | width, firstlinewidth, indent); |
242 | 0 | } |
243 | | |
244 | | #ifdef ARGS_NOEXCEPT |
245 | | /// Error class, for when ARGS_NOEXCEPT is defined |
246 | | enum class Error |
247 | | { |
248 | | None, |
249 | | Usage, |
250 | | Parse, |
251 | | Validation, |
252 | | Required, |
253 | | Map, |
254 | | Extra, |
255 | | Help, |
256 | | Subparser, |
257 | | Completion, |
258 | | }; |
259 | | #else |
260 | | /** Base error class |
261 | | */ |
262 | | class Error : public std::runtime_error |
263 | | { |
264 | | public: |
265 | 893 | Error(const std::string &problem) : std::runtime_error(problem) {} |
266 | 0 | virtual ~Error() {} |
267 | | }; |
268 | | |
269 | | /** Errors that occur during usage |
270 | | */ |
271 | | class UsageError : public Error |
272 | | { |
273 | | public: |
274 | 0 | UsageError(const std::string &problem) : Error(problem) {} |
275 | 0 | virtual ~UsageError() {} |
276 | | }; |
277 | | |
278 | | /** Errors that occur during regular parsing |
279 | | */ |
280 | | class ParseError : public Error |
281 | | { |
282 | | public: |
283 | 872 | ParseError(const std::string &problem) : Error(problem) {} |
284 | 0 | virtual ~ParseError() {} |
285 | | }; |
286 | | |
287 | | /** Errors that are detected from group validation after parsing finishes |
288 | | */ |
289 | | class ValidationError : public Error |
290 | | { |
291 | | public: |
292 | 0 | ValidationError(const std::string &problem) : Error(problem) {} |
293 | 0 | virtual ~ValidationError() {} |
294 | | }; |
295 | | |
296 | | /** Errors that when a required flag is omitted |
297 | | */ |
298 | | class RequiredError : public ValidationError |
299 | | { |
300 | | public: |
301 | 0 | RequiredError(const std::string &problem) : ValidationError(problem) {} |
302 | 0 | virtual ~RequiredError() {} |
303 | | }; |
304 | | |
305 | | /** Errors in map lookups |
306 | | */ |
307 | | class MapError : public ParseError |
308 | | { |
309 | | public: |
310 | 0 | MapError(const std::string &problem) : ParseError(problem) {} |
311 | 0 | virtual ~MapError() {} |
312 | | }; |
313 | | |
314 | | /** Error that occurs when a singular flag is specified multiple times |
315 | | */ |
316 | | class ExtraError : public ParseError |
317 | | { |
318 | | public: |
319 | 0 | ExtraError(const std::string &problem) : ParseError(problem) {} |
320 | 0 | virtual ~ExtraError() {} |
321 | | }; |
322 | | |
323 | | /** An exception that indicates that the user has requested help |
324 | | */ |
325 | | class Help : public Error |
326 | | { |
327 | | public: |
328 | 21 | Help(const std::string &flag) : Error(flag) {} |
329 | 0 | virtual ~Help() {} |
330 | | }; |
331 | | |
332 | | /** (INTERNAL) An exception that emulates coroutine-like control flow for subparsers. |
333 | | */ |
334 | | class SubparserError : public Error |
335 | | { |
336 | | public: |
337 | 0 | SubparserError() : Error("") {} |
338 | 0 | virtual ~SubparserError() {} |
339 | | }; |
340 | | |
341 | | /** An exception that contains autocompletion reply |
342 | | */ |
343 | | class Completion : public Error |
344 | | { |
345 | | public: |
346 | 0 | Completion(const std::string &flag) : Error(flag) {} |
347 | 0 | virtual ~Completion() {} |
348 | | }; |
349 | | #endif |
350 | | |
351 | | /** A simple unified option type for unified initializer lists for the Matcher class. |
352 | | */ |
353 | | struct EitherFlag |
354 | | { |
355 | | const bool isShort; |
356 | | const char shortFlag; |
357 | | const std::string longFlag; |
358 | 538 | EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {} |
359 | 2.71k | EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {} |
360 | 9.55k | EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {} |
361 | | |
362 | | /** Get just the long flags from an initializer list of EitherFlags |
363 | | */ |
364 | | static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags) |
365 | 2.71k | { |
366 | 2.71k | std::unordered_set<std::string> longFlags; |
367 | 2.71k | for (const EitherFlag &flag: flags) |
368 | 5.43k | { |
369 | 5.43k | if (!flag.isShort) |
370 | 2.71k | { |
371 | 2.71k | longFlags.insert(flag.longFlag); |
372 | 2.71k | } |
373 | 5.43k | } |
374 | 2.71k | return longFlags; |
375 | 2.71k | } |
376 | | |
377 | | /** Get just the short flags from an initializer list of EitherFlags |
378 | | */ |
379 | | static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags) |
380 | 2.71k | { |
381 | 2.71k | std::unordered_set<char> shortFlags; |
382 | 2.71k | for (const EitherFlag &flag: flags) |
383 | 5.43k | { |
384 | 5.43k | if (flag.isShort) |
385 | 2.71k | { |
386 | 2.71k | shortFlags.insert(flag.shortFlag); |
387 | 2.71k | } |
388 | 5.43k | } |
389 | 2.71k | return shortFlags; |
390 | 2.71k | } |
391 | | |
392 | | std::string str() const |
393 | 0 | { |
394 | 0 | return isShort ? std::string(1, shortFlag) : longFlag; |
395 | 0 | } |
396 | | |
397 | | std::string str(const std::string &shortPrefix, const std::string &longPrefix) const |
398 | 0 | { |
399 | 0 | return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag; |
400 | 0 | } |
401 | | }; |
402 | | |
403 | | |
404 | | |
405 | | /** A class of "matchers", specifying short and flags that can possibly be |
406 | | * matched. |
407 | | * |
408 | | * This is supposed to be constructed and then passed in, not used directly |
409 | | * from user code. |
410 | | */ |
411 | | class Matcher |
412 | | { |
413 | | private: |
414 | | const std::unordered_set<char> shortFlags; |
415 | | const std::unordered_set<std::string> longFlags; |
416 | | |
417 | | public: |
418 | | /** Specify short and long flags separately as iterators |
419 | | * |
420 | | * ex: `args::Matcher(shortFlags.begin(), shortFlags.end(), longFlags.begin(), longFlags.end())` |
421 | | */ |
422 | | template <typename ShortIt, typename LongIt> |
423 | | Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) : |
424 | 2.71k | shortFlags(shortFlagsStart, shortFlagsEnd), |
425 | 2.71k | longFlags(longFlagsStart, longFlagsEnd) |
426 | 2.71k | { |
427 | 2.71k | if (shortFlags.empty() && longFlags.empty()) |
428 | 0 | { |
429 | 0 | #ifndef ARGS_NOEXCEPT |
430 | 0 | throw UsageError("empty Matcher"); |
431 | 0 | #endif |
432 | 0 | } |
433 | 2.71k | } |
434 | | |
435 | | #ifdef ARGS_NOEXCEPT |
436 | | /// Only for ARGS_NOEXCEPT |
437 | | Error GetError() const noexcept |
438 | | { |
439 | | return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None; |
440 | | } |
441 | | #endif |
442 | | |
443 | | /** Specify short and long flags separately as iterables |
444 | | * |
445 | | * ex: `args::Matcher(shortFlags, longFlags)` |
446 | | */ |
447 | | template <typename Short, typename Long> |
448 | | Matcher(Short &&shortIn, Long &&longIn) : |
449 | 2.71k | Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn)) |
450 | 2.71k | {} |
451 | | |
452 | | /** Specify a mixed single initializer-list of both short and long flags |
453 | | * |
454 | | * This is the fancy one. It takes a single initializer list of |
455 | | * any number of any mixed kinds of flags. Chars are |
456 | | * automatically interpreted as short flags, and strings are |
457 | | * automatically interpreted as long flags: |
458 | | * |
459 | | * args::Matcher{'a'} |
460 | | * args::Matcher{"foo"} |
461 | | * args::Matcher{'h', "help"} |
462 | | * args::Matcher{"foo", 'f', 'F', "FoO"} |
463 | | */ |
464 | | Matcher(std::initializer_list<EitherFlag> in) : |
465 | 2.71k | Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {} |
466 | | |
467 | 2.71k | Matcher(Matcher &&other) noexcept : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags)) |
468 | 2.71k | {} |
469 | | |
470 | 5.43k | ~Matcher() {} |
471 | | |
472 | | /** (INTERNAL) Check if there is a match of a short flag |
473 | | */ |
474 | | bool Match(const char flag) const |
475 | 17.9k | { |
476 | 17.9k | return shortFlags.find(flag) != shortFlags.end(); |
477 | 17.9k | } |
478 | | |
479 | | /** (INTERNAL) Check if there is a match of a long flag |
480 | | */ |
481 | | bool Match(const std::string &flag) const |
482 | 1.52k | { |
483 | 1.52k | return longFlags.find(flag) != longFlags.end(); |
484 | 1.52k | } |
485 | | |
486 | | /** (INTERNAL) Check if there is a match of a flag |
487 | | */ |
488 | | bool Match(const EitherFlag &flag) const |
489 | 19.5k | { |
490 | 19.5k | return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag); |
491 | 19.5k | } |
492 | | |
493 | | /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded |
494 | | */ |
495 | | std::vector<EitherFlag> GetFlagStrings() const |
496 | 0 | { |
497 | 0 | std::vector<EitherFlag> flagStrings; |
498 | 0 | flagStrings.reserve(shortFlags.size() + longFlags.size()); |
499 | 0 | for (const char flag: shortFlags) |
500 | 0 | { |
501 | 0 | flagStrings.emplace_back(flag); |
502 | 0 | } |
503 | 0 | for (const std::string &flag: longFlags) |
504 | 0 | { |
505 | 0 | flagStrings.emplace_back(flag); |
506 | 0 | } |
507 | 0 | return flagStrings; |
508 | 0 | } |
509 | | |
510 | | /** (INTERNAL) Get long flag if it exists or any short flag |
511 | | */ |
512 | | EitherFlag GetLongOrAny() const |
513 | 0 | { |
514 | 0 | if (!longFlags.empty()) |
515 | 0 | { |
516 | 0 | return *longFlags.begin(); |
517 | 0 | } |
518 | | |
519 | 0 | if (!shortFlags.empty()) |
520 | 0 | { |
521 | 0 | return *shortFlags.begin(); |
522 | 0 | } |
523 | | |
524 | | // should be unreachable |
525 | 0 | return ' '; |
526 | 0 | } |
527 | | |
528 | | /** (INTERNAL) Get short flag if it exists or any long flag |
529 | | */ |
530 | | EitherFlag GetShortOrAny() const |
531 | 0 | { |
532 | 0 | if (!shortFlags.empty()) |
533 | 0 | { |
534 | 0 | return *shortFlags.begin(); |
535 | 0 | } |
536 | | |
537 | 0 | if (!longFlags.empty()) |
538 | 0 | { |
539 | 0 | return *longFlags.begin(); |
540 | 0 | } |
541 | | |
542 | | // should be unreachable |
543 | 0 | return ' '; |
544 | 0 | } |
545 | | }; |
546 | | |
547 | | /** Attributes for flags. |
548 | | */ |
549 | | enum class Options |
550 | | { |
551 | | /** Default options. |
552 | | */ |
553 | | None = 0x0, |
554 | | |
555 | | /** Flag can't be passed multiple times. |
556 | | */ |
557 | | Single = 0x01, |
558 | | |
559 | | /** Flag can't be omitted. |
560 | | */ |
561 | | Required = 0x02, |
562 | | |
563 | | /** Flag is excluded from usage line. |
564 | | */ |
565 | | HiddenFromUsage = 0x04, |
566 | | |
567 | | /** Flag is excluded from options help. |
568 | | */ |
569 | | HiddenFromDescription = 0x08, |
570 | | |
571 | | /** Flag is global and can be used in any subcommand. |
572 | | */ |
573 | | Global = 0x10, |
574 | | |
575 | | /** Flag stops a parser. |
576 | | */ |
577 | | KickOut = 0x20, |
578 | | |
579 | | /** Flag is excluded from auto completion. |
580 | | */ |
581 | | HiddenFromCompletion = 0x40, |
582 | | |
583 | | /** Flag is excluded from options help and usage line |
584 | | */ |
585 | | Hidden = HiddenFromUsage | HiddenFromDescription | HiddenFromCompletion, |
586 | | }; |
587 | | |
588 | | inline Options operator | (Options lhs, Options rhs) |
589 | 0 | { |
590 | 0 | return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs)); |
591 | 0 | } |
592 | | |
593 | | inline Options operator & (Options lhs, Options rhs) |
594 | 13.3k | { |
595 | 13.3k | return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs)); |
596 | 13.3k | } |
597 | | |
598 | | class FlagBase; |
599 | | class PositionalBase; |
600 | | class Command; |
601 | | class ArgumentParser; |
602 | | |
603 | | /** A simple structure of parameters for easy user-modifyable help menus |
604 | | */ |
605 | | struct HelpParams |
606 | | { |
607 | | /** The width of the help menu |
608 | | */ |
609 | | unsigned int width = 80; |
610 | | /** The indent of the program line |
611 | | */ |
612 | | unsigned int progindent = 2; |
613 | | /** The indent of the program trailing lines for long parameters |
614 | | */ |
615 | | unsigned int progtailindent = 4; |
616 | | /** The indent of the description and epilogs |
617 | | */ |
618 | | unsigned int descriptionindent = 4; |
619 | | /** The indent of the flags |
620 | | */ |
621 | | unsigned int flagindent = 6; |
622 | | /** The indent of the flag descriptions |
623 | | */ |
624 | | unsigned int helpindent = 40; |
625 | | /** The additional indent each group adds |
626 | | */ |
627 | | unsigned int eachgroupindent = 2; |
628 | | |
629 | | /** The minimum gutter between each flag and its help |
630 | | */ |
631 | | unsigned int gutter = 1; |
632 | | |
633 | | /** Show the terminator when both options and positional parameters are present |
634 | | */ |
635 | | bool showTerminator = true; |
636 | | |
637 | | /** Show the {OPTIONS} on the prog line when this is true |
638 | | */ |
639 | | bool showProglineOptions = true; |
640 | | |
641 | | /** Show the positionals on the prog line when this is true |
642 | | */ |
643 | | bool showProglinePositionals = true; |
644 | | |
645 | | /** The prefix for short flags |
646 | | */ |
647 | | std::string shortPrefix; |
648 | | |
649 | | /** The prefix for long flags |
650 | | */ |
651 | | std::string longPrefix; |
652 | | |
653 | | /** The separator for short flags |
654 | | */ |
655 | | std::string shortSeparator; |
656 | | |
657 | | /** The separator for long flags |
658 | | */ |
659 | | std::string longSeparator; |
660 | | |
661 | | /** The program name for help generation |
662 | | */ |
663 | | std::string programName; |
664 | | |
665 | | /** Show command's flags |
666 | | */ |
667 | | bool showCommandChildren = false; |
668 | | |
669 | | /** Show command's descriptions and epilog |
670 | | */ |
671 | | bool showCommandFullHelp = false; |
672 | | |
673 | | /** The postfix for progline when showProglineOptions is true and command has any flags |
674 | | */ |
675 | | std::string proglineOptions = "{OPTIONS}"; |
676 | | |
677 | | /** The prefix for progline when command has any subcommands |
678 | | */ |
679 | | std::string proglineCommand = "COMMAND"; |
680 | | |
681 | | /** The prefix for progline value |
682 | | */ |
683 | | std::string proglineValueOpen = " <"; |
684 | | |
685 | | /** The postfix for progline value |
686 | | */ |
687 | | std::string proglineValueClose = ">"; |
688 | | |
689 | | /** The prefix for progline required argument |
690 | | */ |
691 | | std::string proglineRequiredOpen = ""; |
692 | | |
693 | | /** The postfix for progline required argument |
694 | | */ |
695 | | std::string proglineRequiredClose = ""; |
696 | | |
697 | | /** The prefix for progline non-required argument |
698 | | */ |
699 | | std::string proglineNonrequiredOpen = "["; |
700 | | |
701 | | /** The postfix for progline non-required argument |
702 | | */ |
703 | | std::string proglineNonrequiredClose = "]"; |
704 | | |
705 | | /** Show flags in program line |
706 | | */ |
707 | | bool proglineShowFlags = false; |
708 | | |
709 | | /** Use short flags in program lines when possible |
710 | | */ |
711 | | bool proglinePreferShortFlags = false; |
712 | | |
713 | | /** Program line prefix |
714 | | */ |
715 | | std::string usageString; |
716 | | |
717 | | /** String shown in help before flags descriptions |
718 | | */ |
719 | | std::string optionsString = "OPTIONS:"; |
720 | | |
721 | | /** Display value name after all the long and short flags |
722 | | */ |
723 | | bool useValueNameOnce = false; |
724 | | |
725 | | /** Show value name |
726 | | */ |
727 | | bool showValueName = true; |
728 | | |
729 | | /** Add newline before flag description |
730 | | */ |
731 | | bool addNewlineBeforeDescription = false; |
732 | | |
733 | | /** The prefix for option value |
734 | | */ |
735 | | std::string valueOpen = "["; |
736 | | |
737 | | /** The postfix for option value |
738 | | */ |
739 | | std::string valueClose = "]"; |
740 | | |
741 | | /** Add choices to argument description |
742 | | */ |
743 | | bool addChoices = false; |
744 | | |
745 | | /** The prefix for choices |
746 | | */ |
747 | | std::string choiceString = "\nOne of: "; |
748 | | |
749 | | /** Add default values to argument description |
750 | | */ |
751 | | bool addDefault = false; |
752 | | |
753 | | /** The prefix for default values |
754 | | */ |
755 | | std::string defaultString = "\nDefault: "; |
756 | | }; |
757 | | |
758 | | /** A number of arguments which can be consumed by an option. |
759 | | * |
760 | | * Represents a closed interval [min, max]. |
761 | | */ |
762 | | struct Nargs |
763 | | { |
764 | | const size_t min; |
765 | | const size_t max; |
766 | | |
767 | | Nargs(size_t min_, size_t max_) : min{min_}, max{max_} |
768 | 0 | { |
769 | 0 | #ifndef ARGS_NOEXCEPT |
770 | 0 | if (max < min) |
771 | 0 | { |
772 | 0 | throw UsageError("Nargs: max > min"); |
773 | 0 | } |
774 | 0 | #endif |
775 | 0 | } |
776 | | |
777 | 6.68k | Nargs(size_t num_) : min{num_}, max{num_} |
778 | 6.68k | { |
779 | 6.68k | } |
780 | | |
781 | | friend bool operator == (const Nargs &lhs, const Nargs &rhs) |
782 | 0 | { |
783 | 0 | return lhs.min == rhs.min && lhs.max == rhs.max; |
784 | 0 | } |
785 | | |
786 | | friend bool operator != (const Nargs &lhs, const Nargs &rhs) |
787 | 0 | { |
788 | 0 | return !(lhs == rhs); |
789 | 0 | } |
790 | | }; |
791 | | |
792 | | /** Base class for all match types |
793 | | */ |
794 | | class Base |
795 | | { |
796 | | private: |
797 | | Options options = {}; |
798 | | |
799 | | protected: |
800 | | bool matched = false; |
801 | | const std::string help; |
802 | | #ifdef ARGS_NOEXCEPT |
803 | | /// Only for ARGS_NOEXCEPT |
804 | | mutable Error error = Error::None; |
805 | | mutable std::string errorMsg; |
806 | | #endif |
807 | | |
808 | | public: |
809 | 3.62k | Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {} |
810 | 3.62k | virtual ~Base() {} |
811 | | |
812 | | Options GetOptions() const noexcept |
813 | 6.70k | { |
814 | 6.70k | return options; |
815 | 6.70k | } |
816 | | |
817 | | bool IsRequired() const noexcept |
818 | 19 | { |
819 | 19 | return (GetOptions() & Options::Required) != Options::None; |
820 | 19 | } |
821 | | |
822 | | virtual bool Matched() const noexcept |
823 | 8.51k | { |
824 | 8.51k | return matched; |
825 | 8.51k | } |
826 | | |
827 | | virtual void Validate(const std::string &, const std::string &) const |
828 | 0 | { |
829 | 0 | } |
830 | | |
831 | | operator bool() const noexcept |
832 | 0 | { |
833 | 0 | return Matched(); |
834 | 0 | } |
835 | | |
836 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const |
837 | 0 | { |
838 | 0 | std::tuple<std::string, std::string, unsigned> description; |
839 | 0 | std::get<1>(description) = help; |
840 | 0 | std::get<2>(description) = indentLevel; |
841 | 0 | return { std::move(description) }; |
842 | 0 | } |
843 | | |
844 | | virtual std::vector<Command*> GetCommands() |
845 | 2.71k | { |
846 | 2.71k | return {}; |
847 | 2.71k | } |
848 | | |
849 | | virtual bool IsGroup() const |
850 | 39 | { |
851 | 39 | return false; |
852 | 39 | } |
853 | | |
854 | | virtual FlagBase *Match(const EitherFlag &) |
855 | 0 | { |
856 | 0 | return nullptr; |
857 | 0 | } |
858 | | |
859 | | virtual PositionalBase *GetNextPositional() |
860 | 546 | { |
861 | 546 | return nullptr; |
862 | 546 | } |
863 | | |
864 | | virtual std::vector<FlagBase*> GetAllFlags() |
865 | 0 | { |
866 | 0 | return {}; |
867 | 0 | } |
868 | | |
869 | | virtual bool HasFlag() const |
870 | 0 | { |
871 | 0 | return false; |
872 | 0 | } |
873 | | |
874 | | virtual bool HasPositional() const |
875 | 0 | { |
876 | 0 | return false; |
877 | 0 | } |
878 | | |
879 | | virtual bool HasCommand() const |
880 | 39 | { |
881 | 39 | return false; |
882 | 39 | } |
883 | | |
884 | | virtual std::vector<std::string> GetProgramLine(const HelpParams &) const |
885 | 0 | { |
886 | 0 | return {}; |
887 | 0 | } |
888 | | |
889 | | /// Sets a kick-out value for building subparsers |
890 | | void KickOut(bool kickout_) noexcept |
891 | 0 | { |
892 | 0 | if (kickout_) |
893 | 0 | { |
894 | 0 | options = options | Options::KickOut; |
895 | 0 | } |
896 | 0 | else |
897 | 0 | { |
898 | 0 | options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut)); |
899 | 0 | } |
900 | 0 | } |
901 | | |
902 | | /// Gets the kick-out value for building subparsers |
903 | | bool KickOut() const noexcept |
904 | 6.65k | { |
905 | 6.65k | return (options & Options::KickOut) != Options::None; |
906 | 6.65k | } |
907 | | |
908 | | virtual void Reset() noexcept |
909 | 3.62k | { |
910 | 3.62k | matched = false; |
911 | | #ifdef ARGS_NOEXCEPT |
912 | | error = Error::None; |
913 | | errorMsg.clear(); |
914 | | #endif |
915 | 3.62k | } |
916 | | |
917 | | #ifdef ARGS_NOEXCEPT |
918 | | /// Only for ARGS_NOEXCEPT |
919 | | virtual Error GetError() const |
920 | | { |
921 | | return error; |
922 | | } |
923 | | |
924 | | /// Only for ARGS_NOEXCEPT |
925 | | virtual std::string GetErrorMsg() const |
926 | | { |
927 | | return errorMsg; |
928 | | } |
929 | | #endif |
930 | | }; |
931 | | |
932 | | /** Base class for all match types that have a name |
933 | | */ |
934 | | class NamedBase : public Base |
935 | | { |
936 | | protected: |
937 | | const std::string name; |
938 | | bool kickout = false; |
939 | | std::string defaultString; |
940 | | bool defaultStringManual = false; |
941 | | std::vector<std::string> choicesStrings; |
942 | | bool choicesStringManual = false; |
943 | | |
944 | 0 | virtual std::string GetDefaultString(const HelpParams&) const { return {}; } |
945 | | |
946 | 0 | virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; } |
947 | | |
948 | 0 | virtual std::string GetNameString(const HelpParams&) const { return Name(); } |
949 | | |
950 | | void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const |
951 | 0 | { |
952 | 0 | if (isManual && !manual.empty()) |
953 | 0 | { |
954 | 0 | dest += str; |
955 | 0 | dest += manual; |
956 | 0 | } |
957 | 0 | else if (!isManual && isGenerated && !generated.empty()) |
958 | 0 | { |
959 | 0 | dest += str; |
960 | 0 | dest += generated; |
961 | 0 | } |
962 | 0 | } |
963 | | |
964 | | public: |
965 | 2.71k | NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {} |
966 | 2.71k | virtual ~NamedBase() {} |
967 | | |
968 | | /** Sets default value string that will be added to argument description. |
969 | | * Use empty string to disable it for this argument. |
970 | | */ |
971 | | void HelpDefault(const std::string &str) |
972 | 0 | { |
973 | 0 | defaultStringManual = true; |
974 | 0 | defaultString = str; |
975 | 0 | } |
976 | | |
977 | | /** Gets default value string that will be added to argument description. |
978 | | */ |
979 | | std::string HelpDefault(const HelpParams ¶ms) const |
980 | 0 | { |
981 | 0 | return defaultStringManual ? defaultString : GetDefaultString(params); |
982 | 0 | } |
983 | | |
984 | | /** Sets choices strings that will be added to argument description. |
985 | | * Use empty vector to disable it for this argument. |
986 | | */ |
987 | | void HelpChoices(const std::vector<std::string> &array) |
988 | 0 | { |
989 | 0 | choicesStringManual = true; |
990 | 0 | choicesStrings = array; |
991 | 0 | } |
992 | | |
993 | | /** Gets choices strings that will be added to argument description. |
994 | | */ |
995 | | std::vector<std::string> HelpChoices(const HelpParams ¶ms) const |
996 | 0 | { |
997 | 0 | return choicesStringManual ? choicesStrings : GetChoicesStrings(params); |
998 | 0 | } |
999 | | |
1000 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned indentLevel) const override |
1001 | 0 | { |
1002 | 0 | std::tuple<std::string, std::string, unsigned> description; |
1003 | 0 | std::get<0>(description) = GetNameString(params); |
1004 | 0 | std::get<1>(description) = help; |
1005 | 0 | std::get<2>(description) = indentLevel; |
1006 | |
|
1007 | 0 | AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString); |
1008 | 0 | AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString); |
1009 | |
|
1010 | 0 | return { std::move(description) }; |
1011 | 0 | } |
1012 | | |
1013 | | virtual std::string Name() const |
1014 | 21 | { |
1015 | 21 | return name; |
1016 | 21 | } |
1017 | | }; |
1018 | | |
1019 | | namespace detail |
1020 | | { |
1021 | | template<typename T> |
1022 | | using vector = std::vector<T, std::allocator<T>>; |
1023 | | |
1024 | | template<typename K, typename T> |
1025 | | using unordered_map = std::unordered_map<K, T, std::hash<K>, |
1026 | | std::equal_to<K>, std::allocator<std::pair<const K, T> > >; |
1027 | | |
1028 | | template<typename S, typename T> |
1029 | | class is_streamable |
1030 | | { |
1031 | | template<typename SS, typename TT> |
1032 | | static auto test(int) |
1033 | | -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() ); |
1034 | | |
1035 | | template<typename, typename> |
1036 | | static auto test(...) -> std::false_type; |
1037 | | |
1038 | | public: |
1039 | | using type = decltype(test<S,T>(0)); |
1040 | | }; |
1041 | | |
1042 | | template <typename T> |
1043 | | using IsConvertableToString = typename is_streamable<std::ostringstream, T>::type; |
1044 | | |
1045 | | template <typename T> |
1046 | | typename std::enable_if<IsConvertableToString<T>::value, std::string>::type |
1047 | | ToString(const T &value) |
1048 | | { |
1049 | | std::ostringstream s; |
1050 | | s << value; |
1051 | | return s.str(); |
1052 | | } |
1053 | | |
1054 | | template <typename T> |
1055 | | typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type |
1056 | | ToString(const T &) |
1057 | | { |
1058 | | return {}; |
1059 | | } |
1060 | | |
1061 | | template <typename T> |
1062 | | std::vector<std::string> MapKeysToStrings(const T &map) |
1063 | | { |
1064 | | std::vector<std::string> res; |
1065 | | using K = typename std::decay<decltype(std::begin(map)->first)>::type; |
1066 | | if (IsConvertableToString<K>::value) |
1067 | | { |
1068 | | for (const auto &p : map) |
1069 | | { |
1070 | | res.push_back(detail::ToString(p.first)); |
1071 | | } |
1072 | | |
1073 | | std::sort(res.begin(), res.end()); |
1074 | | } |
1075 | | return res; |
1076 | | } |
1077 | | } |
1078 | | |
1079 | | /** Base class for all flag options |
1080 | | */ |
1081 | | class FlagBase : public NamedBase |
1082 | | { |
1083 | | protected: |
1084 | | const Matcher matcher; |
1085 | | |
1086 | | virtual std::string GetNameString(const HelpParams ¶ms) const override |
1087 | 0 | { |
1088 | 0 | const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name(); |
1089 | 0 | std::string flags; |
1090 | 0 | const auto flagStrings = matcher.GetFlagStrings(); |
1091 | 0 | const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce; |
1092 | 0 | for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it) |
1093 | 0 | { |
1094 | 0 | auto &flag = *it; |
1095 | 0 | if (it != flagStrings.begin()) |
1096 | 0 | { |
1097 | 0 | flags += ", "; |
1098 | 0 | } |
1099 | |
|
1100 | 0 | flags += flag.isShort ? params.shortPrefix : params.longPrefix; |
1101 | 0 | flags += flag.str(); |
1102 | |
|
1103 | 0 | if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) |
1104 | 0 | { |
1105 | 0 | flags += flag.isShort ? params.shortSeparator : params.longSeparator; |
1106 | 0 | flags += params.valueOpen + postfix + params.valueClose; |
1107 | 0 | } |
1108 | 0 | } |
1109 | |
|
1110 | 0 | return flags; |
1111 | 0 | } |
1112 | | |
1113 | | public: |
1114 | 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_)) {} |
1115 | | |
1116 | 2.71k | FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {} |
1117 | | |
1118 | 2.71k | virtual ~FlagBase() {} |
1119 | | |
1120 | | virtual FlagBase *Match(const EitherFlag &flag) override |
1121 | 19.5k | { |
1122 | 19.5k | if (matcher.Match(flag)) |
1123 | 6.68k | { |
1124 | 6.68k | if ((GetOptions() & Options::Single) != Options::None && matched) |
1125 | 0 | { |
1126 | 0 | std::ostringstream problem; |
1127 | 0 | problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once"; |
1128 | | #ifdef ARGS_NOEXCEPT |
1129 | | error = Error::Extra; |
1130 | | errorMsg = problem.str(); |
1131 | | #else |
1132 | 0 | throw ExtraError(problem.str()); |
1133 | 0 | #endif |
1134 | 0 | } |
1135 | 6.68k | matched = true; |
1136 | 6.68k | return this; |
1137 | 6.68k | } |
1138 | 12.8k | return nullptr; |
1139 | 19.5k | } |
1140 | | |
1141 | | virtual std::vector<FlagBase*> GetAllFlags() override |
1142 | 0 | { |
1143 | 0 | return { this }; |
1144 | 0 | } |
1145 | | |
1146 | | const Matcher &GetMatcher() const |
1147 | 0 | { |
1148 | 0 | return matcher; |
1149 | 0 | } |
1150 | | |
1151 | | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override |
1152 | 39 | { |
1153 | 39 | if (!Matched() && IsRequired()) |
1154 | 0 | { |
1155 | 0 | std::ostringstream problem; |
1156 | 0 | problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required"; |
1157 | | #ifdef ARGS_NOEXCEPT |
1158 | | error = Error::Required; |
1159 | | errorMsg = problem.str(); |
1160 | | #else |
1161 | 0 | throw RequiredError(problem.str()); |
1162 | 0 | #endif |
1163 | 0 | } |
1164 | 39 | } |
1165 | | |
1166 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1167 | 0 | { |
1168 | 0 | if (!params.proglineShowFlags) |
1169 | 0 | { |
1170 | 0 | return {}; |
1171 | 0 | } |
1172 | | |
1173 | 0 | const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name(); |
1174 | 0 | const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny(); |
1175 | 0 | std::string res = flag.str(params.shortPrefix, params.longPrefix); |
1176 | 0 | if (!postfix.empty()) |
1177 | 0 | { |
1178 | 0 | res += params.proglineValueOpen + postfix + params.proglineValueClose; |
1179 | 0 | } |
1180 | |
|
1181 | 0 | return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose |
1182 | 0 | : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose }; |
1183 | 0 | } |
1184 | | |
1185 | | virtual bool HasFlag() const override |
1186 | 0 | { |
1187 | 0 | return true; |
1188 | 0 | } |
1189 | | |
1190 | | #ifdef ARGS_NOEXCEPT |
1191 | | /// Only for ARGS_NOEXCEPT |
1192 | | virtual Error GetError() const override |
1193 | | { |
1194 | | const auto nargs = NumberOfArguments(); |
1195 | | if (nargs.min > nargs.max) |
1196 | | { |
1197 | | return Error::Usage; |
1198 | | } |
1199 | | |
1200 | | const auto matcherError = matcher.GetError(); |
1201 | | if (matcherError != Error::None) |
1202 | | { |
1203 | | return matcherError; |
1204 | | } |
1205 | | |
1206 | | return error; |
1207 | | } |
1208 | | #endif |
1209 | | |
1210 | | /** Defines how many values can be consumed by this option. |
1211 | | * |
1212 | | * \return closed interval [min, max] |
1213 | | */ |
1214 | | virtual Nargs NumberOfArguments() const noexcept = 0; |
1215 | | |
1216 | | /** Parse values of this option. |
1217 | | * |
1218 | | * \param value Vector of values. It's size must be in NumberOfArguments() interval. |
1219 | | */ |
1220 | | virtual void ParseValue(const std::vector<std::string> &value) = 0; |
1221 | | }; |
1222 | | |
1223 | | /** Base class for value-accepting flag options |
1224 | | */ |
1225 | | class ValueFlagBase : public FlagBase |
1226 | | { |
1227 | | public: |
1228 | 0 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {} |
1229 | 0 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {} |
1230 | 0 | virtual ~ValueFlagBase() {} |
1231 | | |
1232 | | virtual Nargs NumberOfArguments() const noexcept override |
1233 | 0 | { |
1234 | 0 | return 1; |
1235 | 0 | } |
1236 | | }; |
1237 | | |
1238 | | class CompletionFlag : public ValueFlagBase |
1239 | | { |
1240 | | public: |
1241 | | std::vector<std::string> reply; |
1242 | | size_t cword = 0; |
1243 | | std::string syntax; |
1244 | | |
1245 | | template <typename GroupClass> |
1246 | | CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden) |
1247 | | { |
1248 | | group_.AddCompletion(*this); |
1249 | | } |
1250 | | |
1251 | 0 | virtual ~CompletionFlag() {} |
1252 | | |
1253 | | virtual Nargs NumberOfArguments() const noexcept override |
1254 | 0 | { |
1255 | 0 | return 2; |
1256 | 0 | } |
1257 | | |
1258 | | virtual void ParseValue(const std::vector<std::string> &value_) override |
1259 | 0 | { |
1260 | 0 | syntax = value_.at(0); |
1261 | 0 | std::istringstream(value_.at(1)) >> cword; |
1262 | 0 | } |
1263 | | |
1264 | | /** Get the completion reply |
1265 | | */ |
1266 | | std::string Get() noexcept |
1267 | 0 | { |
1268 | 0 | return detail::Join(reply, "\n"); |
1269 | 0 | } |
1270 | | |
1271 | | virtual void Reset() noexcept override |
1272 | 0 | { |
1273 | 0 | ValueFlagBase::Reset(); |
1274 | 0 | cword = 0; |
1275 | 0 | syntax.clear(); |
1276 | 0 | reply.clear(); |
1277 | 0 | } |
1278 | | }; |
1279 | | |
1280 | | |
1281 | | /** Base class for positional options |
1282 | | */ |
1283 | | class PositionalBase : public NamedBase |
1284 | | { |
1285 | | protected: |
1286 | | bool ready; |
1287 | | |
1288 | | public: |
1289 | 0 | PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {} |
1290 | 0 | virtual ~PositionalBase() {} |
1291 | | |
1292 | | bool Ready() |
1293 | 0 | { |
1294 | 0 | return ready; |
1295 | 0 | } |
1296 | | |
1297 | | virtual void ParseValue(const std::string &value_) = 0; |
1298 | | |
1299 | | virtual void Reset() noexcept override |
1300 | 0 | { |
1301 | 0 | matched = false; |
1302 | 0 | ready = true; |
1303 | 0 | #ifdef ARGS_NOEXCEPT |
1304 | 0 | error = Error::None; |
1305 | 0 | errorMsg.clear(); |
1306 | 0 | #endif |
1307 | 0 | } |
1308 | | |
1309 | | virtual PositionalBase *GetNextPositional() override |
1310 | 0 | { |
1311 | 0 | return Ready() ? this : nullptr; |
1312 | 0 | } |
1313 | | |
1314 | | virtual bool HasPositional() const override |
1315 | 0 | { |
1316 | 0 | return true; |
1317 | 0 | } |
1318 | | |
1319 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1320 | 0 | { |
1321 | 0 | return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose |
1322 | 0 | : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose }; |
1323 | 0 | } |
1324 | | |
1325 | | virtual void Validate(const std::string &, const std::string &) const override |
1326 | 0 | { |
1327 | 0 | if (IsRequired() && !Matched()) |
1328 | 0 | { |
1329 | 0 | std::ostringstream problem; |
1330 | 0 | problem << "Option '" << Name() << "' is required"; |
1331 | 0 | #ifdef ARGS_NOEXCEPT |
1332 | 0 | error = Error::Required; |
1333 | 0 | errorMsg = problem.str(); |
1334 | 0 | #else |
1335 | 0 | throw RequiredError(problem.str()); |
1336 | 0 | #endif |
1337 | 0 | } |
1338 | 0 | } |
1339 | | }; |
1340 | | |
1341 | | /** Class for all kinds of validating groups, including ArgumentParser |
1342 | | */ |
1343 | | class Group : public Base |
1344 | | { |
1345 | | private: |
1346 | | std::vector<Base*> children; |
1347 | | std::function<bool(const Group &)> validator; |
1348 | | |
1349 | | public: |
1350 | | /** Default validators |
1351 | | */ |
1352 | | struct Validators |
1353 | | { |
1354 | | static bool Xor(const Group &group) |
1355 | 0 | { |
1356 | 0 | return group.MatchedChildren() == 1; |
1357 | 0 | } |
1358 | | |
1359 | | static bool AtLeastOne(const Group &group) |
1360 | 0 | { |
1361 | 0 | return group.MatchedChildren() >= 1; |
1362 | 0 | } |
1363 | | |
1364 | | static bool AtMostOne(const Group &group) |
1365 | 0 | { |
1366 | 0 | return group.MatchedChildren() <= 1; |
1367 | 0 | } |
1368 | | |
1369 | | static bool All(const Group &group) |
1370 | 0 | { |
1371 | 0 | return group.Children().size() == group.MatchedChildren(); |
1372 | 0 | } |
1373 | | |
1374 | | static bool AllOrNone(const Group &group) |
1375 | 0 | { |
1376 | 0 | return (All(group) || None(group)); |
1377 | 0 | } |
1378 | | |
1379 | | static bool AllChildGroups(const Group &group) |
1380 | 0 | { |
1381 | 0 | return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool { |
1382 | 0 | return child->IsGroup() && !child->Matched(); |
1383 | 0 | }); |
1384 | 0 | } |
1385 | | |
1386 | | static bool DontCare(const Group &) |
1387 | 0 | { |
1388 | 0 | return true; |
1389 | 0 | } |
1390 | | |
1391 | | static bool CareTooMuch(const Group &) |
1392 | 0 | { |
1393 | 0 | return false; |
1394 | 0 | } |
1395 | | |
1396 | | static bool None(const Group &group) |
1397 | 0 | { |
1398 | 0 | return group.MatchedChildren() == 0; |
1399 | 0 | } |
1400 | | }; |
1401 | | /// If help is empty, this group will not be printed in help output |
1402 | 906 | Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) {} |
1403 | | /// If help is empty, this group will not be printed in help output |
1404 | | 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_) |
1405 | 0 | { |
1406 | 0 | group_.Add(*this); |
1407 | 0 | } |
1408 | 906 | virtual ~Group() {} |
1409 | | |
1410 | | /** Append a child to this Group. |
1411 | | */ |
1412 | | void Add(Base &child) |
1413 | 2.71k | { |
1414 | 2.71k | children.emplace_back(&child); |
1415 | 2.71k | } |
1416 | | |
1417 | | /** Get all this group's children |
1418 | | */ |
1419 | | const std::vector<Base *> &Children() const |
1420 | 9.40k | { |
1421 | 9.40k | return children; |
1422 | 9.40k | } |
1423 | | |
1424 | | /** Return the first FlagBase that matches flag, or nullptr |
1425 | | * |
1426 | | * \param flag The flag with prefixes stripped |
1427 | | * \return the first matching FlagBase pointer, or nullptr if there is no match |
1428 | | */ |
1429 | | virtual FlagBase *Match(const EitherFlag &flag) override |
1430 | 7.37k | { |
1431 | 7.37k | for (Base *child: Children()) |
1432 | 19.5k | { |
1433 | 19.5k | if (FlagBase *match = child->Match(flag)) |
1434 | 6.68k | { |
1435 | 6.68k | return match; |
1436 | 6.68k | } |
1437 | 19.5k | } |
1438 | 681 | return nullptr; |
1439 | 7.37k | } |
1440 | | |
1441 | | virtual std::vector<FlagBase*> GetAllFlags() override |
1442 | 0 | { |
1443 | 0 | std::vector<FlagBase*> res; |
1444 | 0 | for (Base *child: Children()) |
1445 | 0 | { |
1446 | 0 | auto childRes = child->GetAllFlags(); |
1447 | 0 | res.insert(res.end(), childRes.begin(), childRes.end()); |
1448 | 0 | } |
1449 | 0 | return res; |
1450 | 0 | } |
1451 | | |
1452 | | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override |
1453 | 0 | { |
1454 | 0 | for (Base *child: Children()) |
1455 | 0 | { |
1456 | 0 | child->Validate(shortPrefix, longPrefix); |
1457 | 0 | } |
1458 | 0 | } |
1459 | | |
1460 | | /** Get the next ready positional, or nullptr if there is none |
1461 | | * |
1462 | | * \return the first ready PositionalBase pointer, or nullptr if there is no match |
1463 | | */ |
1464 | | virtual PositionalBase *GetNextPositional() override |
1465 | 182 | { |
1466 | 182 | for (Base *child: Children()) |
1467 | 546 | { |
1468 | 546 | if (auto next = child->GetNextPositional()) |
1469 | 0 | { |
1470 | 0 | return next; |
1471 | 0 | } |
1472 | 546 | } |
1473 | 182 | return nullptr; |
1474 | 182 | } |
1475 | | |
1476 | | /** Get whether this has any FlagBase children |
1477 | | * |
1478 | | * \return Whether or not there are any FlagBase children |
1479 | | */ |
1480 | | virtual bool HasFlag() const override |
1481 | 0 | { |
1482 | 0 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); }); |
1483 | 0 | } |
1484 | | |
1485 | | /** Get whether this has any PositionalBase children |
1486 | | * |
1487 | | * \return Whether or not there are any PositionalBase children |
1488 | | */ |
1489 | | virtual bool HasPositional() const override |
1490 | 0 | { |
1491 | 0 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); }); |
1492 | 0 | } |
1493 | | |
1494 | | /** Get whether this has any Command children |
1495 | | * |
1496 | | * \return Whether or not there are any Command children |
1497 | | */ |
1498 | | virtual bool HasCommand() const override |
1499 | 13 | { |
1500 | 39 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); }); |
1501 | 13 | } |
1502 | | |
1503 | | /** Count the number of matched children this group has |
1504 | | */ |
1505 | | std::vector<Base *>::size_type MatchedChildren() const |
1506 | 0 | { |
1507 | 0 | // Cast to avoid warnings from -Wsign-conversion |
1508 | 0 | return static_cast<std::vector<Base *>::size_type>( |
1509 | 0 | std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();})); |
1510 | 0 | } |
1511 | | |
1512 | | /** Whether or not this group matches validation |
1513 | | */ |
1514 | | virtual bool Matched() const noexcept override |
1515 | 0 | { |
1516 | 0 | return validator(*this); |
1517 | 0 | } |
1518 | | |
1519 | | /** Get validation |
1520 | | */ |
1521 | | bool Get() const |
1522 | 0 | { |
1523 | 0 | return Matched(); |
1524 | 0 | } |
1525 | | |
1526 | | /** Get all the child descriptions for help generation |
1527 | | */ |
1528 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override |
1529 | 0 | { |
1530 | 0 | std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions; |
1531 | | |
1532 | | // Push that group description on the back if not empty |
1533 | 0 | unsigned addindent = 0; |
1534 | 0 | if (!help.empty()) |
1535 | 0 | { |
1536 | 0 | descriptions.emplace_back(help, "", indent); |
1537 | 0 | addindent = 1; |
1538 | 0 | } |
1539 | |
|
1540 | 0 | for (Base *child: Children()) |
1541 | 0 | { |
1542 | 0 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) |
1543 | 0 | { |
1544 | 0 | continue; |
1545 | 0 | } |
1546 | | |
1547 | 0 | auto groupDescriptions = child->GetDescription(params, indent + addindent); |
1548 | 0 | descriptions.insert( |
1549 | 0 | std::end(descriptions), |
1550 | 0 | std::make_move_iterator(std::begin(groupDescriptions)), |
1551 | 0 | std::make_move_iterator(std::end(groupDescriptions))); |
1552 | 0 | } |
1553 | 0 | return descriptions; |
1554 | 0 | } |
1555 | | |
1556 | | /** Get the names of positional parameters |
1557 | | */ |
1558 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
1559 | 0 | { |
1560 | 0 | std::vector <std::string> names; |
1561 | 0 | for (Base *child: Children()) |
1562 | 0 | { |
1563 | 0 | if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None) |
1564 | 0 | { |
1565 | 0 | continue; |
1566 | 0 | } |
1567 | | |
1568 | 0 | auto groupNames = child->GetProgramLine(params); |
1569 | 0 | names.insert( |
1570 | 0 | std::end(names), |
1571 | 0 | std::make_move_iterator(std::begin(groupNames)), |
1572 | 0 | std::make_move_iterator(std::end(groupNames))); |
1573 | 0 | } |
1574 | 0 | return names; |
1575 | 0 | } |
1576 | | |
1577 | | virtual std::vector<Command*> GetCommands() override |
1578 | 906 | { |
1579 | 906 | std::vector<Command*> res; |
1580 | 906 | for (const auto &child : Children()) |
1581 | 2.71k | { |
1582 | 2.71k | auto subparsers = child->GetCommands(); |
1583 | 2.71k | res.insert(std::end(res), std::begin(subparsers), std::end(subparsers)); |
1584 | 2.71k | } |
1585 | 906 | return res; |
1586 | 906 | } |
1587 | | |
1588 | | virtual bool IsGroup() const override |
1589 | 0 | { |
1590 | 0 | return true; |
1591 | 0 | } |
1592 | | |
1593 | | virtual void Reset() noexcept override |
1594 | 906 | { |
1595 | 906 | Base::Reset(); |
1596 | | |
1597 | 906 | for (auto &child: Children()) |
1598 | 2.71k | { |
1599 | 2.71k | child->Reset(); |
1600 | 2.71k | } |
1601 | | #ifdef ARGS_NOEXCEPT |
1602 | | error = Error::None; |
1603 | | errorMsg.clear(); |
1604 | | #endif |
1605 | 906 | } |
1606 | | |
1607 | | #ifdef ARGS_NOEXCEPT |
1608 | | /// Only for ARGS_NOEXCEPT |
1609 | | virtual Error GetError() const override |
1610 | | { |
1611 | | if (error != Error::None) |
1612 | | { |
1613 | | return error; |
1614 | | } |
1615 | | |
1616 | | auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); |
1617 | | if (it == Children().end()) |
1618 | | { |
1619 | | return Error::None; |
1620 | | } else |
1621 | | { |
1622 | | return (*it)->GetError(); |
1623 | | } |
1624 | | } |
1625 | | |
1626 | | /// Only for ARGS_NOEXCEPT |
1627 | | virtual std::string GetErrorMsg() const override |
1628 | | { |
1629 | | if (error != Error::None) |
1630 | | { |
1631 | | return errorMsg; |
1632 | | } |
1633 | | |
1634 | | auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); |
1635 | | if (it == Children().end()) |
1636 | | { |
1637 | | return ""; |
1638 | | } else |
1639 | | { |
1640 | | return (*it)->GetErrorMsg(); |
1641 | | } |
1642 | | } |
1643 | | #endif |
1644 | | |
1645 | | }; |
1646 | | |
1647 | | /** Class for using global options in ArgumentParser. |
1648 | | */ |
1649 | | class GlobalOptions : public Group |
1650 | | { |
1651 | | public: |
1652 | | GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global) |
1653 | 0 | { |
1654 | 0 | Add(options_); |
1655 | 0 | } |
1656 | | }; |
1657 | | |
1658 | | /** Utility class for building subparsers with coroutines/callbacks. |
1659 | | * |
1660 | | * Brief example: |
1661 | | * \code |
1662 | | * Command command(argumentParser, "command", "my command", [](args::Subparser &s) |
1663 | | * { |
1664 | | * // your command flags/positionals |
1665 | | * s.Parse(); //required |
1666 | | * //your command code |
1667 | | * }); |
1668 | | * \endcode |
1669 | | * |
1670 | | * For ARGS_NOEXCEPT mode don't forget to check `s.GetError()` after `s.Parse()` |
1671 | | * and return if it isn't equals to args::Error::None. |
1672 | | * |
1673 | | * \sa Command |
1674 | | */ |
1675 | | class Subparser : public Group |
1676 | | { |
1677 | | private: |
1678 | | std::vector<std::string> args; |
1679 | | std::vector<std::string> kicked; |
1680 | | ArgumentParser *parser = nullptr; |
1681 | | const HelpParams &helpParams; |
1682 | | const Command &command; |
1683 | | bool isParsed = false; |
1684 | | |
1685 | | public: |
1686 | | Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_) |
1687 | 0 | : Group({}, Validators::AllChildGroups), args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_) |
1688 | 0 | { |
1689 | 0 | } |
1690 | | |
1691 | 0 | Subparser(const Command &command_, const HelpParams &helpParams_) : Group({}, Validators::AllChildGroups), helpParams(helpParams_), command(command_) |
1692 | 0 | { |
1693 | 0 | } |
1694 | | |
1695 | | Subparser(const Subparser&) = delete; |
1696 | | Subparser(Subparser&&) = delete; |
1697 | | Subparser &operator = (const Subparser&) = delete; |
1698 | | Subparser &operator = (Subparser&&) = delete; |
1699 | | |
1700 | | const Command &GetCommand() |
1701 | 0 | { |
1702 | 0 | return command; |
1703 | 0 | } |
1704 | | |
1705 | | /** (INTERNAL) Determines whether Parse was called or not. |
1706 | | */ |
1707 | | bool IsParsed() const |
1708 | 0 | { |
1709 | 0 | return isParsed; |
1710 | 0 | } |
1711 | | |
1712 | | /** Continue parsing arguments for new command. |
1713 | | */ |
1714 | | void Parse(); |
1715 | | |
1716 | | /** Returns a vector of kicked out arguments. |
1717 | | * |
1718 | | * \sa Base::KickOut |
1719 | | */ |
1720 | | const std::vector<std::string> &KickedOut() const noexcept |
1721 | 0 | { |
1722 | 0 | return kicked; |
1723 | 0 | } |
1724 | | }; |
1725 | | |
1726 | | /** Main class for building subparsers. |
1727 | | * |
1728 | | * /sa Subparser |
1729 | | */ |
1730 | | class Command : public Group |
1731 | | { |
1732 | | private: |
1733 | | friend class Subparser; |
1734 | | |
1735 | | std::string name; |
1736 | | std::string help; |
1737 | | std::string description; |
1738 | | std::string epilog; |
1739 | | std::string proglinePostfix; |
1740 | | |
1741 | | std::function<void(Subparser&)> parserCoroutine; |
1742 | | bool commandIsRequired = true; |
1743 | | Command *selectedCommand = nullptr; |
1744 | | |
1745 | | mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription; |
1746 | | mutable std::vector<std::string> subparserProgramLine; |
1747 | | mutable bool subparserHasFlag = false; |
1748 | | mutable bool subparserHasPositional = false; |
1749 | | mutable bool subparserHasCommand = false; |
1750 | | #ifdef ARGS_NOEXCEPT |
1751 | | mutable Error subparserError = Error::None; |
1752 | | #endif |
1753 | | mutable Subparser *subparser = nullptr; |
1754 | | |
1755 | | protected: |
1756 | | |
1757 | | class RaiiSubparser |
1758 | | { |
1759 | | public: |
1760 | | RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_); |
1761 | | RaiiSubparser(const Command &command_, const HelpParams ¶ms_); |
1762 | | |
1763 | | ~RaiiSubparser() |
1764 | 0 | { |
1765 | 0 | command.subparser = oldSubparser; |
1766 | 0 | } |
1767 | | |
1768 | | Subparser &Parser() |
1769 | 0 | { |
1770 | 0 | return parser; |
1771 | 0 | } |
1772 | | |
1773 | | private: |
1774 | | const Command &command; |
1775 | | Subparser parser; |
1776 | | Subparser *oldSubparser; |
1777 | | }; |
1778 | | |
1779 | 906 | Command() = default; |
1780 | | |
1781 | | std::function<void(Subparser&)> &GetCoroutine() |
1782 | 0 | { |
1783 | 0 | return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine; |
1784 | 0 | } |
1785 | | |
1786 | | Command &SelectedCommand() |
1787 | 0 | { |
1788 | 0 | Command *res = this; |
1789 | 0 | while (res->selectedCommand != nullptr) |
1790 | 0 | { |
1791 | 0 | res = res->selectedCommand; |
1792 | 0 | } |
1793 | |
|
1794 | 0 | return *res; |
1795 | 0 | } |
1796 | | |
1797 | | const Command &SelectedCommand() const |
1798 | 0 | { |
1799 | 0 | const Command *res = this; |
1800 | 0 | while (res->selectedCommand != nullptr) |
1801 | 0 | { |
1802 | 0 | res = res->selectedCommand; |
1803 | 0 | } |
1804 | 0 |
|
1805 | 0 | return *res; |
1806 | 0 | } |
1807 | | |
1808 | | void UpdateSubparserHelp(const HelpParams ¶ms) const |
1809 | 0 | { |
1810 | 0 | if (parserCoroutine) |
1811 | 0 | { |
1812 | 0 | RaiiSubparser coro(*this, params); |
1813 | 0 | #ifndef ARGS_NOEXCEPT |
1814 | 0 | try |
1815 | 0 | { |
1816 | 0 | parserCoroutine(coro.Parser()); |
1817 | 0 | } |
1818 | 0 | catch (args::SubparserError&) |
1819 | 0 | { |
1820 | 0 | } |
1821 | | #else |
1822 | | parserCoroutine(coro.Parser()); |
1823 | | #endif |
1824 | 0 | } |
1825 | 0 | } |
1826 | | |
1827 | | public: |
1828 | | Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {}) |
1829 | | : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_)) |
1830 | 0 | { |
1831 | 0 | base_.Add(*this); |
1832 | 0 | } |
1833 | | |
1834 | | /** The description that appears on the prog line after options |
1835 | | */ |
1836 | | const std::string &ProglinePostfix() const |
1837 | 0 | { return proglinePostfix; } |
1838 | | |
1839 | | /** The description that appears on the prog line after options |
1840 | | */ |
1841 | | void ProglinePostfix(const std::string &proglinePostfix_) |
1842 | 0 | { this->proglinePostfix = proglinePostfix_; } |
1843 | | |
1844 | | /** The description that appears above options |
1845 | | */ |
1846 | | const std::string &Description() const |
1847 | 0 | { return description; } |
1848 | | /** The description that appears above options |
1849 | | */ |
1850 | | |
1851 | | void Description(const std::string &description_) |
1852 | 906 | { this->description = description_; } |
1853 | | |
1854 | | /** The description that appears below options |
1855 | | */ |
1856 | | const std::string &Epilog() const |
1857 | 0 | { return epilog; } |
1858 | | |
1859 | | /** The description that appears below options |
1860 | | */ |
1861 | | void Epilog(const std::string &epilog_) |
1862 | 906 | { this->epilog = epilog_; } |
1863 | | |
1864 | | /** The name of command |
1865 | | */ |
1866 | | const std::string &Name() const |
1867 | 0 | { return name; } |
1868 | | |
1869 | | /** The description of command |
1870 | | */ |
1871 | | const std::string &Help() const |
1872 | 0 | { return help; } |
1873 | | |
1874 | | /** If value is true, parser will fail if no command was parsed. |
1875 | | * |
1876 | | * Default: true. |
1877 | | */ |
1878 | | void RequireCommand(bool value) |
1879 | 0 | { commandIsRequired = value; } |
1880 | | |
1881 | | virtual bool IsGroup() const override |
1882 | 0 | { return false; } |
1883 | | |
1884 | | virtual bool Matched() const noexcept override |
1885 | 8.47k | { return Base::Matched(); } |
1886 | | |
1887 | | operator bool() const noexcept |
1888 | 0 | { return Matched(); } |
1889 | | |
1890 | | void Match() noexcept |
1891 | 0 | { matched = true; } |
1892 | | |
1893 | | void SelectCommand(Command *c) noexcept |
1894 | 0 | { |
1895 | 0 | selectedCommand = c; |
1896 | |
|
1897 | 0 | if (c != nullptr) |
1898 | 0 | { |
1899 | 0 | c->Match(); |
1900 | 0 | } |
1901 | 0 | } |
1902 | | |
1903 | | virtual FlagBase *Match(const EitherFlag &flag) override |
1904 | 7.37k | { |
1905 | 7.37k | if (selectedCommand != nullptr) |
1906 | 0 | { |
1907 | 0 | if (auto *res = selectedCommand->Match(flag)) |
1908 | 0 | { |
1909 | 0 | return res; |
1910 | 0 | } |
1911 | | |
1912 | 0 | for (auto *child: Children()) |
1913 | 0 | { |
1914 | 0 | if ((child->GetOptions() & Options::Global) != Options::None) |
1915 | 0 | { |
1916 | 0 | if (auto *res = child->Match(flag)) |
1917 | 0 | { |
1918 | 0 | return res; |
1919 | 0 | } |
1920 | 0 | } |
1921 | 0 | } |
1922 | | |
1923 | 0 | return nullptr; |
1924 | 0 | } |
1925 | | |
1926 | 7.37k | if (subparser != nullptr) |
1927 | 0 | { |
1928 | 0 | return subparser->Match(flag); |
1929 | 0 | } |
1930 | | |
1931 | 7.37k | return Matched() ? Group::Match(flag) : nullptr; |
1932 | 7.37k | } |
1933 | | |
1934 | | virtual std::vector<FlagBase*> GetAllFlags() override |
1935 | 0 | { |
1936 | 0 | std::vector<FlagBase*> res; |
1937 | |
|
1938 | 0 | if (!Matched()) |
1939 | 0 | { |
1940 | 0 | return res; |
1941 | 0 | } |
1942 | | |
1943 | 0 | for (auto *child: Children()) |
1944 | 0 | { |
1945 | 0 | if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None) |
1946 | 0 | { |
1947 | 0 | auto childFlags = child->GetAllFlags(); |
1948 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
1949 | 0 | } |
1950 | 0 | } |
1951 | |
|
1952 | 0 | if (selectedCommand != nullptr) |
1953 | 0 | { |
1954 | 0 | auto childFlags = selectedCommand->GetAllFlags(); |
1955 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
1956 | 0 | } |
1957 | |
|
1958 | 0 | if (subparser != nullptr) |
1959 | 0 | { |
1960 | 0 | auto childFlags = subparser->GetAllFlags(); |
1961 | 0 | res.insert(res.end(), childFlags.begin(), childFlags.end()); |
1962 | 0 | } |
1963 | |
|
1964 | 0 | return res; |
1965 | 0 | } |
1966 | | |
1967 | | virtual PositionalBase *GetNextPositional() override |
1968 | 182 | { |
1969 | 182 | if (selectedCommand != nullptr) |
1970 | 0 | { |
1971 | 0 | if (auto *res = selectedCommand->GetNextPositional()) |
1972 | 0 | { |
1973 | 0 | return res; |
1974 | 0 | } |
1975 | | |
1976 | 0 | for (auto *child: Children()) |
1977 | 0 | { |
1978 | 0 | if ((child->GetOptions() & Options::Global) != Options::None) |
1979 | 0 | { |
1980 | 0 | if (auto *res = child->GetNextPositional()) |
1981 | 0 | { |
1982 | 0 | return res; |
1983 | 0 | } |
1984 | 0 | } |
1985 | 0 | } |
1986 | | |
1987 | 0 | return nullptr; |
1988 | 0 | } |
1989 | | |
1990 | 182 | if (subparser != nullptr) |
1991 | 0 | { |
1992 | 0 | return subparser->GetNextPositional(); |
1993 | 0 | } |
1994 | | |
1995 | 182 | return Matched() ? Group::GetNextPositional() : nullptr; |
1996 | 182 | } |
1997 | | |
1998 | | virtual bool HasFlag() const override |
1999 | 0 | { |
2000 | 0 | return subparserHasFlag || Group::HasFlag(); |
2001 | 0 | } |
2002 | | |
2003 | | virtual bool HasPositional() const override |
2004 | 0 | { |
2005 | 0 | return subparserHasPositional || Group::HasPositional(); |
2006 | 0 | } |
2007 | | |
2008 | | virtual bool HasCommand() const override |
2009 | 0 | { |
2010 | 0 | return true; |
2011 | 0 | } |
2012 | | |
2013 | | std::vector<std::string> GetCommandProgramLine(const HelpParams ¶ms) const |
2014 | 0 | { |
2015 | 0 | UpdateSubparserHelp(params); |
2016 | |
|
2017 | 0 | std::vector<std::string> res; |
2018 | |
|
2019 | 0 | if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags) |
2020 | 0 | { |
2021 | 0 | res.push_back(params.proglineOptions); |
2022 | 0 | } |
2023 | |
|
2024 | 0 | auto group_res = Group::GetProgramLine(params); |
2025 | 0 | std::move(std::move(group_res).begin(), std::move(group_res).end(), std::back_inserter(res)); |
2026 | |
|
2027 | 0 | res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end()); |
2028 | |
|
2029 | 0 | if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand)) |
2030 | 0 | { |
2031 | 0 | res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]"); |
2032 | 0 | } |
2033 | |
|
2034 | 0 | if (!Name().empty()) |
2035 | 0 | { |
2036 | 0 | res.insert(res.begin(), Name()); |
2037 | 0 | } |
2038 | |
|
2039 | 0 | if (!ProglinePostfix().empty()) |
2040 | 0 | { |
2041 | 0 | std::string line; |
2042 | 0 | for (auto c : ProglinePostfix()) |
2043 | 0 | { |
2044 | 0 | if (std::isspace(static_cast<unsigned char>(c))) |
2045 | 0 | { |
2046 | 0 | if (!line.empty()) |
2047 | 0 | { |
2048 | 0 | res.push_back(line); |
2049 | 0 | line.clear(); |
2050 | 0 | } |
2051 | |
|
2052 | 0 | if (c == '\n') |
2053 | 0 | { |
2054 | 0 | res.push_back("\n"); |
2055 | 0 | } |
2056 | 0 | } |
2057 | 0 | else |
2058 | 0 | { |
2059 | 0 | line += c; |
2060 | 0 | } |
2061 | 0 | } |
2062 | |
|
2063 | 0 | if (!line.empty()) |
2064 | 0 | { |
2065 | 0 | res.push_back(line); |
2066 | 0 | } |
2067 | 0 | } |
2068 | |
|
2069 | 0 | return res; |
2070 | 0 | } |
2071 | | |
2072 | | virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override |
2073 | 0 | { |
2074 | 0 | if (!Matched()) |
2075 | 0 | { |
2076 | 0 | return {}; |
2077 | 0 | } |
2078 | | |
2079 | 0 | return GetCommandProgramLine(params); |
2080 | 0 | } |
2081 | | |
2082 | | virtual std::vector<Command*> GetCommands() override |
2083 | 906 | { |
2084 | 906 | if (selectedCommand != nullptr) |
2085 | 0 | { |
2086 | 0 | return selectedCommand->GetCommands(); |
2087 | 0 | } |
2088 | | |
2089 | 906 | if (Matched()) |
2090 | 906 | { |
2091 | 906 | return Group::GetCommands(); |
2092 | 906 | } |
2093 | | |
2094 | 0 | return { this }; |
2095 | 906 | } |
2096 | | |
2097 | | virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override |
2098 | 0 | { |
2099 | 0 | std::vector<std::tuple<std::string, std::string, unsigned>> descriptions; |
2100 | 0 | unsigned addindent = 0; |
2101 | |
|
2102 | 0 | UpdateSubparserHelp(params); |
2103 | |
|
2104 | 0 | if (!Matched()) |
2105 | 0 | { |
2106 | 0 | if (params.showCommandFullHelp) |
2107 | 0 | { |
2108 | 0 | std::ostringstream s; |
2109 | 0 | bool empty = true; |
2110 | 0 | for (const auto &progline: GetCommandProgramLine(params)) |
2111 | 0 | { |
2112 | 0 | if (!empty) |
2113 | 0 | { |
2114 | 0 | s << ' '; |
2115 | 0 | } |
2116 | 0 | else |
2117 | 0 | { |
2118 | 0 | empty = false; |
2119 | 0 | } |
2120 | |
|
2121 | 0 | s << progline; |
2122 | 0 | } |
2123 | |
|
2124 | 0 | descriptions.emplace_back(s.str(), "", indent); |
2125 | 0 | } |
2126 | 0 | else |
2127 | 0 | { |
2128 | 0 | descriptions.emplace_back(Name(), help, indent); |
2129 | 0 | } |
2130 | |
|
2131 | 0 | if (!params.showCommandChildren && !params.showCommandFullHelp) |
2132 | 0 | { |
2133 | 0 | return descriptions; |
2134 | 0 | } |
2135 | | |
2136 | 0 | addindent = 1; |
2137 | 0 | } |
2138 | | |
2139 | 0 | if (params.showCommandFullHelp && !Matched()) |
2140 | 0 | { |
2141 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2142 | 0 | descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent); |
2143 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2144 | 0 | } |
2145 | |
|
2146 | 0 | for (Base *child: Children()) |
2147 | 0 | { |
2148 | 0 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) |
2149 | 0 | { |
2150 | 0 | continue; |
2151 | 0 | } |
2152 | | |
2153 | 0 | auto groupDescriptions = child->GetDescription(params, indent + addindent); |
2154 | 0 | descriptions.insert( |
2155 | 0 | std::end(descriptions), |
2156 | 0 | std::make_move_iterator(std::begin(groupDescriptions)), |
2157 | 0 | std::make_move_iterator(std::end(groupDescriptions))); |
2158 | 0 | } |
2159 | |
|
2160 | 0 | for (auto childDescription: subparserDescription) |
2161 | 0 | { |
2162 | 0 | std::get<2>(childDescription) += indent + addindent; |
2163 | 0 | descriptions.push_back(std::move(childDescription)); |
2164 | 0 | } |
2165 | |
|
2166 | 0 | if (params.showCommandFullHelp && !Matched()) |
2167 | 0 | { |
2168 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2169 | 0 | if (!Epilog().empty()) |
2170 | 0 | { |
2171 | 0 | descriptions.emplace_back(Epilog(), "", indent + addindent); |
2172 | 0 | descriptions.emplace_back("", "", indent + addindent); |
2173 | 0 | } |
2174 | 0 | } |
2175 | |
|
2176 | 0 | return descriptions; |
2177 | 0 | } |
2178 | | |
2179 | | virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override |
2180 | 13 | { |
2181 | 13 | if (!Matched()) |
2182 | 0 | { |
2183 | 0 | return; |
2184 | 0 | } |
2185 | | |
2186 | 13 | auto onValidationError = [&] |
2187 | 13 | { |
2188 | 0 | std::ostringstream problem; |
2189 | 0 | problem << "Group validation failed somewhere!"; |
2190 | | #ifdef ARGS_NOEXCEPT |
2191 | | error = Error::Validation; |
2192 | | errorMsg = problem.str(); |
2193 | | #else |
2194 | 0 | throw ValidationError(problem.str()); |
2195 | 0 | #endif |
2196 | 0 | }; |
2197 | | |
2198 | 13 | for (Base *child: Children()) |
2199 | 39 | { |
2200 | 39 | if (child->IsGroup() && !child->Matched()) |
2201 | 0 | { |
2202 | 0 | onValidationError(); |
2203 | 0 | } |
2204 | | |
2205 | 39 | child->Validate(shortprefix, longprefix); |
2206 | 39 | } |
2207 | | |
2208 | 13 | if (subparser != nullptr) |
2209 | 0 | { |
2210 | 0 | subparser->Validate(shortprefix, longprefix); |
2211 | 0 | if (!subparser->Matched()) |
2212 | 0 | { |
2213 | 0 | onValidationError(); |
2214 | 0 | } |
2215 | 0 | } |
2216 | | |
2217 | 13 | if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand)) |
2218 | 0 | { |
2219 | 0 | std::ostringstream problem; |
2220 | 0 | problem << "Command is required"; |
2221 | | #ifdef ARGS_NOEXCEPT |
2222 | | error = Error::Validation; |
2223 | | errorMsg = problem.str(); |
2224 | | #else |
2225 | 0 | throw ValidationError(problem.str()); |
2226 | 0 | #endif |
2227 | 0 | } |
2228 | 13 | } |
2229 | | |
2230 | | virtual void Reset() noexcept override |
2231 | 906 | { |
2232 | 906 | Group::Reset(); |
2233 | 906 | selectedCommand = nullptr; |
2234 | 906 | subparserProgramLine.clear(); |
2235 | 906 | subparserDescription.clear(); |
2236 | 906 | subparserHasFlag = false; |
2237 | 906 | subparserHasPositional = false; |
2238 | 906 | subparserHasCommand = false; |
2239 | | #ifdef ARGS_NOEXCEPT |
2240 | | subparserError = Error::None; |
2241 | | #endif |
2242 | 906 | } |
2243 | | |
2244 | | #ifdef ARGS_NOEXCEPT |
2245 | | /// Only for ARGS_NOEXCEPT |
2246 | | virtual Error GetError() const override |
2247 | | { |
2248 | | if (!Matched()) |
2249 | | { |
2250 | | return Error::None; |
2251 | | } |
2252 | | |
2253 | | if (error != Error::None) |
2254 | | { |
2255 | | return error; |
2256 | | } |
2257 | | |
2258 | | if (subparserError != Error::None) |
2259 | | { |
2260 | | return subparserError; |
2261 | | } |
2262 | | |
2263 | | return Group::GetError(); |
2264 | | } |
2265 | | #endif |
2266 | | }; |
2267 | | |
2268 | | /** The main user facing command line argument parser class |
2269 | | */ |
2270 | | class ArgumentParser : public Command |
2271 | | { |
2272 | | friend class Subparser; |
2273 | | |
2274 | | private: |
2275 | | std::string longprefix; |
2276 | | std::string shortprefix; |
2277 | | |
2278 | | std::string longseparator; |
2279 | | |
2280 | | std::string terminator; |
2281 | | |
2282 | | bool allowJoinedShortValue = true; |
2283 | | bool allowJoinedLongValue = true; |
2284 | | bool allowSeparateShortValue = true; |
2285 | | bool allowSeparateLongValue = true; |
2286 | | |
2287 | | CompletionFlag *completion = nullptr; |
2288 | | bool readCompletion = false; |
2289 | | |
2290 | | protected: |
2291 | | enum class OptionType |
2292 | | { |
2293 | | LongFlag, |
2294 | | ShortFlag, |
2295 | | Positional |
2296 | | }; |
2297 | | |
2298 | | OptionType ParseOption(const std::string &s, bool allowEmpty = false) |
2299 | 1.81k | { |
2300 | 1.81k | if (s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length())) |
2301 | 538 | { |
2302 | 538 | return OptionType::LongFlag; |
2303 | 538 | } |
2304 | | |
2305 | 1.27k | if (s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length())) |
2306 | 916 | { |
2307 | 916 | return OptionType::ShortFlag; |
2308 | 916 | } |
2309 | | |
2310 | 362 | return OptionType::Positional; |
2311 | 1.27k | } |
2312 | | |
2313 | | template <typename It> |
2314 | | bool Complete(FlagBase &flag, It it, It end) |
2315 | 0 | { |
2316 | 0 | auto nextIt = it; |
2317 | 0 | if (!readCompletion || (++nextIt != end)) |
2318 | 0 | { |
2319 | 0 | return false; |
2320 | 0 | } |
2321 | | |
2322 | 0 | const auto &chunk = *it; |
2323 | 0 | for (auto &choice : flag.HelpChoices(helpParams)) |
2324 | 0 | { |
2325 | 0 | AddCompletionReply(chunk, choice); |
2326 | 0 | } |
2327 | |
|
2328 | 0 | #ifndef ARGS_NOEXCEPT |
2329 | 0 | throw Completion(completion->Get()); |
2330 | | #else |
2331 | | return true; |
2332 | | #endif |
2333 | 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*>) |
2334 | | |
2335 | | /** (INTERNAL) Parse flag's values |
2336 | | * |
2337 | | * \param arg The string to display in error message as a flag name |
2338 | | * \param[in, out] it The iterator to first value. It will point to the last value |
2339 | | * \param end The end iterator |
2340 | | * \param joinedArg Joined value (e.g. bar in --foo=bar) |
2341 | | * \param canDiscardJoined If true joined value can be parsed as flag not as a value (as in -abcd) |
2342 | | * \param[out] values The vector to store parsed arg's values |
2343 | | */ |
2344 | | template <typename It> |
2345 | | std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end, |
2346 | | const bool allowSeparate, const bool allowJoined, |
2347 | | const bool hasJoined, const std::string &joinedArg, |
2348 | | const bool canDiscardJoined, std::vector<std::string> &values) |
2349 | 6.68k | { |
2350 | 6.68k | values.clear(); |
2351 | | |
2352 | 6.68k | Nargs nargs = flag.NumberOfArguments(); |
2353 | | |
2354 | 6.68k | if (hasJoined && !allowJoined && nargs.min != 0) |
2355 | 0 | { |
2356 | 0 | return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; |
2357 | 0 | } |
2358 | | |
2359 | 6.68k | if (hasJoined) |
2360 | 6.40k | { |
2361 | 6.40k | if (!canDiscardJoined || nargs.max != 0) |
2362 | 9 | { |
2363 | 9 | values.push_back(joinedArg); |
2364 | 9 | } |
2365 | 6.40k | } else if (!allowSeparate) |
2366 | 0 | { |
2367 | 0 | if (nargs.min != 0) |
2368 | 0 | { |
2369 | 0 | return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; |
2370 | 0 | } |
2371 | 0 | } else |
2372 | 287 | { |
2373 | 287 | auto valueIt = it; |
2374 | 287 | ++valueIt; |
2375 | | |
2376 | 287 | while (valueIt != end && |
2377 | 287 | values.size() < nargs.max && |
2378 | 287 | (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional)) |
2379 | 0 | { |
2380 | 0 | if (Complete(flag, valueIt, end)) |
2381 | 0 | { |
2382 | 0 | it = end; |
2383 | 0 | return ""; |
2384 | 0 | } |
2385 | | |
2386 | 0 | values.push_back(*valueIt); |
2387 | 0 | ++it; |
2388 | 0 | ++valueIt; |
2389 | 0 | } |
2390 | 287 | } |
2391 | | |
2392 | 6.68k | if (values.size() > nargs.max) |
2393 | 9 | { |
2394 | 9 | return "Passed an argument into a non-argument flag: " + arg; |
2395 | 6.68k | } else if (values.size() < nargs.min) |
2396 | 0 | { |
2397 | 0 | if (nargs.min == 1 && nargs.max == 1) |
2398 | 0 | { |
2399 | 0 | return "Flag '" + arg + "' requires an argument but received none"; |
2400 | 0 | } else if (nargs.min == 1) |
2401 | 0 | { |
2402 | 0 | return "Flag '" + arg + "' requires at least one argument but received none"; |
2403 | 0 | } else if (nargs.min != nargs.max) |
2404 | 0 | { |
2405 | 0 | return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + |
2406 | 0 | " arguments but received " + std::to_string(values.size()); |
2407 | 0 | } else |
2408 | 0 | { |
2409 | 0 | return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + |
2410 | 0 | " arguments but received " + std::to_string(values.size()); |
2411 | 0 | } |
2412 | 0 | } |
2413 | | |
2414 | 6.68k | return {}; |
2415 | 6.68k | } 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 | 2349 | 6.68k | { | 2350 | 6.68k | values.clear(); | 2351 | | | 2352 | 6.68k | Nargs nargs = flag.NumberOfArguments(); | 2353 | | | 2354 | 6.68k | if (hasJoined && !allowJoined && nargs.min != 0) | 2355 | 0 | { | 2356 | 0 | return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; | 2357 | 0 | } | 2358 | | | 2359 | 6.68k | if (hasJoined) | 2360 | 6.40k | { | 2361 | 6.40k | if (!canDiscardJoined || nargs.max != 0) | 2362 | 9 | { | 2363 | 9 | values.push_back(joinedArg); | 2364 | 9 | } | 2365 | 6.40k | } else if (!allowSeparate) | 2366 | 0 | { | 2367 | 0 | if (nargs.min != 0) | 2368 | 0 | { | 2369 | 0 | return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; | 2370 | 0 | } | 2371 | 0 | } else | 2372 | 287 | { | 2373 | 287 | auto valueIt = it; | 2374 | 287 | ++valueIt; | 2375 | | | 2376 | 287 | while (valueIt != end && | 2377 | 287 | values.size() < nargs.max && | 2378 | 287 | (values.size() < nargs.min || ParseOption(*valueIt) == OptionType::Positional)) | 2379 | 0 | { | 2380 | 0 | if (Complete(flag, valueIt, end)) | 2381 | 0 | { | 2382 | 0 | it = end; | 2383 | 0 | return ""; | 2384 | 0 | } | 2385 | | | 2386 | 0 | values.push_back(*valueIt); | 2387 | 0 | ++it; | 2388 | 0 | ++valueIt; | 2389 | 0 | } | 2390 | 287 | } | 2391 | | | 2392 | 6.68k | if (values.size() > nargs.max) | 2393 | 9 | { | 2394 | 9 | return "Passed an argument into a non-argument flag: " + arg; | 2395 | 6.68k | } else if (values.size() < nargs.min) | 2396 | 0 | { | 2397 | 0 | if (nargs.min == 1 && nargs.max == 1) | 2398 | 0 | { | 2399 | 0 | return "Flag '" + arg + "' requires an argument but received none"; | 2400 | 0 | } else if (nargs.min == 1) | 2401 | 0 | { | 2402 | 0 | return "Flag '" + arg + "' requires at least one argument but received none"; | 2403 | 0 | } else if (nargs.min != nargs.max) | 2404 | 0 | { | 2405 | 0 | return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + | 2406 | 0 | " arguments but received " + std::to_string(values.size()); | 2407 | 0 | } else | 2408 | 0 | { | 2409 | 0 | return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + | 2410 | 0 | " arguments but received " + std::to_string(values.size()); | 2411 | 0 | } | 2412 | 0 | } | 2413 | | | 2414 | 6.68k | return {}; | 2415 | 6.68k | } |
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> > > >&) |
2416 | | |
2417 | | template <typename It> |
2418 | | bool ParseLong(It &it, It end) |
2419 | 538 | { |
2420 | 538 | const auto &chunk = *it; |
2421 | 538 | const auto argchunk = chunk.substr(longprefix.size()); |
2422 | | // Try to separate it, in case of a separator: |
2423 | 538 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); |
2424 | | // If the separator is in the argument, separate it. |
2425 | 538 | const auto arg = (separator != argchunk.npos ? |
2426 | 222 | std::string(argchunk, 0, separator) |
2427 | 538 | : argchunk); |
2428 | 538 | const auto joined = (separator != argchunk.npos ? |
2429 | 222 | argchunk.substr(separator + longseparator.size()) |
2430 | 538 | : std::string()); |
2431 | | |
2432 | 538 | if (auto flag = Match(arg)) |
2433 | 85 | { |
2434 | 85 | std::vector<std::string> values; |
2435 | 85 | const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, |
2436 | 85 | separator != argchunk.npos, joined, false, values); |
2437 | 85 | if (!errorMessage.empty()) |
2438 | 9 | { |
2439 | 9 | #ifndef ARGS_NOEXCEPT |
2440 | 9 | throw ParseError(errorMessage); |
2441 | | #else |
2442 | | error = Error::Parse; |
2443 | | errorMsg = errorMessage; |
2444 | | return false; |
2445 | | #endif |
2446 | 9 | } |
2447 | | |
2448 | 76 | if (!readCompletion) |
2449 | 76 | { |
2450 | 76 | flag->ParseValue(values); |
2451 | 76 | } |
2452 | | |
2453 | 76 | if (flag->KickOut()) |
2454 | 0 | { |
2455 | 0 | ++it; |
2456 | 0 | return false; |
2457 | 0 | } |
2458 | 76 | } else |
2459 | 453 | { |
2460 | 453 | const std::string errorMessage("Flag could not be matched: " + arg); |
2461 | 453 | #ifndef ARGS_NOEXCEPT |
2462 | 453 | throw ParseError(errorMessage); |
2463 | | #else |
2464 | | error = Error::Parse; |
2465 | | errorMsg = errorMessage; |
2466 | | return false; |
2467 | | #endif |
2468 | 453 | } |
2469 | | |
2470 | 76 | return true; |
2471 | 538 | } 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 | 2419 | 538 | { | 2420 | 538 | const auto &chunk = *it; | 2421 | 538 | const auto argchunk = chunk.substr(longprefix.size()); | 2422 | | // Try to separate it, in case of a separator: | 2423 | 538 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); | 2424 | | // If the separator is in the argument, separate it. | 2425 | 538 | const auto arg = (separator != argchunk.npos ? | 2426 | 222 | std::string(argchunk, 0, separator) | 2427 | 538 | : argchunk); | 2428 | 538 | const auto joined = (separator != argchunk.npos ? | 2429 | 222 | argchunk.substr(separator + longseparator.size()) | 2430 | 538 | : std::string()); | 2431 | | | 2432 | 538 | if (auto flag = Match(arg)) | 2433 | 85 | { | 2434 | 85 | std::vector<std::string> values; | 2435 | 85 | const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, | 2436 | 85 | separator != argchunk.npos, joined, false, values); | 2437 | 85 | if (!errorMessage.empty()) | 2438 | 9 | { | 2439 | 9 | #ifndef ARGS_NOEXCEPT | 2440 | 9 | throw ParseError(errorMessage); | 2441 | | #else | 2442 | | error = Error::Parse; | 2443 | | errorMsg = errorMessage; | 2444 | | return false; | 2445 | | #endif | 2446 | 9 | } | 2447 | | | 2448 | 76 | if (!readCompletion) | 2449 | 76 | { | 2450 | 76 | flag->ParseValue(values); | 2451 | 76 | } | 2452 | | | 2453 | 76 | if (flag->KickOut()) | 2454 | 0 | { | 2455 | 0 | ++it; | 2456 | 0 | return false; | 2457 | 0 | } | 2458 | 76 | } else | 2459 | 453 | { | 2460 | 453 | const std::string errorMessage("Flag could not be matched: " + arg); | 2461 | 453 | #ifndef ARGS_NOEXCEPT | 2462 | 453 | throw ParseError(errorMessage); | 2463 | | #else | 2464 | | error = Error::Parse; | 2465 | | errorMsg = errorMessage; | 2466 | | return false; | 2467 | | #endif | 2468 | 453 | } | 2469 | | | 2470 | 76 | return true; | 2471 | 538 | } |
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*>) |
2472 | | |
2473 | | template <typename It> |
2474 | | bool ParseShort(It &it, It end) |
2475 | 458 | { |
2476 | 458 | const auto &chunk = *it; |
2477 | 458 | const auto argchunk = chunk.substr(shortprefix.size()); |
2478 | 7.06k | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) |
2479 | 6.83k | { |
2480 | 6.83k | const auto arg = *argit; |
2481 | | |
2482 | 6.83k | if (auto flag = Match(arg)) |
2483 | 6.60k | { |
2484 | 6.60k | const std::string value(argit + 1, std::end(argchunk)); |
2485 | 6.60k | std::vector<std::string> values; |
2486 | 6.60k | const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, |
2487 | 6.60k | allowSeparateShortValue, allowJoinedShortValue, |
2488 | 6.60k | !value.empty(), value, !value.empty(), values); |
2489 | | |
2490 | 6.60k | if (!errorMessage.empty()) |
2491 | 0 | { |
2492 | 0 | #ifndef ARGS_NOEXCEPT |
2493 | 0 | throw ParseError(errorMessage); |
2494 | | #else |
2495 | | error = Error::Parse; |
2496 | | errorMsg = errorMessage; |
2497 | | return false; |
2498 | | #endif |
2499 | 0 | } |
2500 | | |
2501 | 6.60k | if (!readCompletion) |
2502 | 6.60k | { |
2503 | 6.60k | flag->ParseValue(values); |
2504 | 6.60k | } |
2505 | | |
2506 | 6.60k | if (flag->KickOut()) |
2507 | 0 | { |
2508 | 0 | ++it; |
2509 | 0 | return false; |
2510 | 0 | } |
2511 | | |
2512 | 6.60k | if (!values.empty()) |
2513 | 0 | { |
2514 | 0 | break; |
2515 | 0 | } |
2516 | 6.60k | } else |
2517 | 228 | { |
2518 | 228 | const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); |
2519 | 228 | #ifndef ARGS_NOEXCEPT |
2520 | 228 | throw ParseError(errorMessage); |
2521 | | #else |
2522 | | error = Error::Parse; |
2523 | | errorMsg = errorMessage; |
2524 | | return false; |
2525 | | #endif |
2526 | 228 | } |
2527 | 6.83k | } |
2528 | | |
2529 | 230 | return true; |
2530 | 458 | } 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 | 2475 | 458 | { | 2476 | 458 | const auto &chunk = *it; | 2477 | 458 | const auto argchunk = chunk.substr(shortprefix.size()); | 2478 | 7.06k | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) | 2479 | 6.83k | { | 2480 | 6.83k | const auto arg = *argit; | 2481 | | | 2482 | 6.83k | if (auto flag = Match(arg)) | 2483 | 6.60k | { | 2484 | 6.60k | const std::string value(argit + 1, std::end(argchunk)); | 2485 | 6.60k | std::vector<std::string> values; | 2486 | 6.60k | const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, | 2487 | 6.60k | allowSeparateShortValue, allowJoinedShortValue, | 2488 | 6.60k | !value.empty(), value, !value.empty(), values); | 2489 | | | 2490 | 6.60k | if (!errorMessage.empty()) | 2491 | 0 | { | 2492 | 0 | #ifndef ARGS_NOEXCEPT | 2493 | 0 | throw ParseError(errorMessage); | 2494 | | #else | 2495 | | error = Error::Parse; | 2496 | | errorMsg = errorMessage; | 2497 | | return false; | 2498 | | #endif | 2499 | 0 | } | 2500 | | | 2501 | 6.60k | if (!readCompletion) | 2502 | 6.60k | { | 2503 | 6.60k | flag->ParseValue(values); | 2504 | 6.60k | } | 2505 | | | 2506 | 6.60k | if (flag->KickOut()) | 2507 | 0 | { | 2508 | 0 | ++it; | 2509 | 0 | return false; | 2510 | 0 | } | 2511 | | | 2512 | 6.60k | if (!values.empty()) | 2513 | 0 | { | 2514 | 0 | break; | 2515 | 0 | } | 2516 | 6.60k | } else | 2517 | 228 | { | 2518 | 228 | const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); | 2519 | 228 | #ifndef ARGS_NOEXCEPT | 2520 | 228 | throw ParseError(errorMessage); | 2521 | | #else | 2522 | | error = Error::Parse; | 2523 | | errorMsg = errorMessage; | 2524 | | return false; | 2525 | | #endif | 2526 | 228 | } | 2527 | 6.83k | } | 2528 | | | 2529 | 230 | return true; | 2530 | 458 | } |
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*>) |
2531 | | |
2532 | | bool AddCompletionReply(const std::string &cur, const std::string &choice) |
2533 | 0 | { |
2534 | 0 | if (cur.empty() || choice.find(cur) == 0) |
2535 | 0 | { |
2536 | 0 | if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos) |
2537 | 0 | { |
2538 | 0 | completion->reply.push_back(choice.substr(choice.find(longseparator) + 1)); |
2539 | 0 | } else |
2540 | 0 | { |
2541 | 0 | completion->reply.push_back(choice); |
2542 | 0 | } |
2543 | 0 | return true; |
2544 | 0 | } |
2545 | | |
2546 | 0 | return false; |
2547 | 0 | } |
2548 | | |
2549 | | template <typename It> |
2550 | | bool Complete(It it, It end) |
2551 | 1.18k | { |
2552 | 1.18k | auto nextIt = it; |
2553 | 1.18k | if (!readCompletion || (++nextIt != end)) |
2554 | 1.18k | { |
2555 | 1.18k | return false; |
2556 | 1.18k | } |
2557 | | |
2558 | 0 | const auto &chunk = *it; |
2559 | 0 | auto pos = GetNextPositional(); |
2560 | 0 | std::vector<Command *> commands = GetCommands(); |
2561 | 0 | const auto optionType = ParseOption(chunk, true); |
2562 | |
|
2563 | 0 | if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) |
2564 | 0 | { |
2565 | 0 | for (auto &cmd : commands) |
2566 | 0 | { |
2567 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
2568 | 0 | { |
2569 | 0 | AddCompletionReply(chunk, cmd->Name()); |
2570 | 0 | } |
2571 | 0 | } |
2572 | 0 | } else |
2573 | 0 | { |
2574 | 0 | bool hasPositionalCompletion = true; |
2575 | |
|
2576 | 0 | if (!commands.empty()) |
2577 | 0 | { |
2578 | 0 | for (auto &cmd : commands) |
2579 | 0 | { |
2580 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
2581 | 0 | { |
2582 | 0 | AddCompletionReply(chunk, cmd->Name()); |
2583 | 0 | } |
2584 | 0 | } |
2585 | 0 | } else if (pos) |
2586 | 0 | { |
2587 | 0 | if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) |
2588 | 0 | { |
2589 | 0 | auto choices = pos->HelpChoices(helpParams); |
2590 | 0 | hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; |
2591 | 0 | for (auto &choice : choices) |
2592 | 0 | { |
2593 | 0 | AddCompletionReply(chunk, choice); |
2594 | 0 | } |
2595 | 0 | } |
2596 | 0 | } |
2597 | |
|
2598 | 0 | if (hasPositionalCompletion) |
2599 | 0 | { |
2600 | 0 | auto flags = GetAllFlags(); |
2601 | 0 | for (auto flag : flags) |
2602 | 0 | { |
2603 | 0 | if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) |
2604 | 0 | { |
2605 | 0 | continue; |
2606 | 0 | } |
2607 | | |
2608 | 0 | auto &matcher = flag->GetMatcher(); |
2609 | 0 | if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) |
2610 | 0 | { |
2611 | 0 | for (auto &flagName : matcher.GetFlagStrings()) |
2612 | 0 | { |
2613 | 0 | if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) |
2614 | 0 | { |
2615 | 0 | break; |
2616 | 0 | } |
2617 | 0 | } |
2618 | 0 | } |
2619 | 0 | } |
2620 | |
|
2621 | 0 | if (optionType == OptionType::LongFlag && allowJoinedLongValue) |
2622 | 0 | { |
2623 | 0 | const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); |
2624 | 0 | if (separator != chunk.npos) |
2625 | 0 | { |
2626 | 0 | std::string arg(chunk, 0, separator); |
2627 | 0 | if (auto flag = this->Match(arg.substr(longprefix.size()))) |
2628 | 0 | { |
2629 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) |
2630 | 0 | { |
2631 | 0 | AddCompletionReply(chunk, arg + longseparator + choice); |
2632 | 0 | } |
2633 | 0 | } |
2634 | 0 | } |
2635 | 0 | } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) |
2636 | 0 | { |
2637 | 0 | if (chunk.size() > shortprefix.size() + 1) |
2638 | 0 | { |
2639 | 0 | auto arg = chunk.at(shortprefix.size()); |
2640 | | //TODO: support -abcVALUE where a and b take no value |
2641 | 0 | if (auto flag = this->Match(arg)) |
2642 | 0 | { |
2643 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) |
2644 | 0 | { |
2645 | 0 | AddCompletionReply(chunk, shortprefix + arg + choice); |
2646 | 0 | } |
2647 | 0 | } |
2648 | 0 | } |
2649 | 0 | } |
2650 | 0 | } |
2651 | 0 | } |
2652 | |
|
2653 | 0 | #ifndef ARGS_NOEXCEPT |
2654 | 0 | throw Completion(completion->Get()); |
2655 | | #else |
2656 | | return true; |
2657 | | #endif |
2658 | 1.18k | } 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> >*>) Line | Count | Source | 2551 | 1.18k | { | 2552 | 1.18k | auto nextIt = it; | 2553 | 1.18k | if (!readCompletion || (++nextIt != end)) | 2554 | 1.18k | { | 2555 | 1.18k | return false; | 2556 | 1.18k | } | 2557 | | | 2558 | 0 | const auto &chunk = *it; | 2559 | 0 | auto pos = GetNextPositional(); | 2560 | 0 | std::vector<Command *> commands = GetCommands(); | 2561 | 0 | const auto optionType = ParseOption(chunk, true); | 2562 | |
| 2563 | 0 | if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) | 2564 | 0 | { | 2565 | 0 | for (auto &cmd : commands) | 2566 | 0 | { | 2567 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 2568 | 0 | { | 2569 | 0 | AddCompletionReply(chunk, cmd->Name()); | 2570 | 0 | } | 2571 | 0 | } | 2572 | 0 | } else | 2573 | 0 | { | 2574 | 0 | bool hasPositionalCompletion = true; | 2575 | |
| 2576 | 0 | if (!commands.empty()) | 2577 | 0 | { | 2578 | 0 | for (auto &cmd : commands) | 2579 | 0 | { | 2580 | 0 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 2581 | 0 | { | 2582 | 0 | AddCompletionReply(chunk, cmd->Name()); | 2583 | 0 | } | 2584 | 0 | } | 2585 | 0 | } else if (pos) | 2586 | 0 | { | 2587 | 0 | if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) | 2588 | 0 | { | 2589 | 0 | auto choices = pos->HelpChoices(helpParams); | 2590 | 0 | hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; | 2591 | 0 | for (auto &choice : choices) | 2592 | 0 | { | 2593 | 0 | AddCompletionReply(chunk, choice); | 2594 | 0 | } | 2595 | 0 | } | 2596 | 0 | } | 2597 | |
| 2598 | 0 | if (hasPositionalCompletion) | 2599 | 0 | { | 2600 | 0 | auto flags = GetAllFlags(); | 2601 | 0 | for (auto flag : flags) | 2602 | 0 | { | 2603 | 0 | if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) | 2604 | 0 | { | 2605 | 0 | continue; | 2606 | 0 | } | 2607 | | | 2608 | 0 | auto &matcher = flag->GetMatcher(); | 2609 | 0 | if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) | 2610 | 0 | { | 2611 | 0 | for (auto &flagName : matcher.GetFlagStrings()) | 2612 | 0 | { | 2613 | 0 | if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) | 2614 | 0 | { | 2615 | 0 | break; | 2616 | 0 | } | 2617 | 0 | } | 2618 | 0 | } | 2619 | 0 | } | 2620 | |
| 2621 | 0 | if (optionType == OptionType::LongFlag && allowJoinedLongValue) | 2622 | 0 | { | 2623 | 0 | const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); | 2624 | 0 | if (separator != chunk.npos) | 2625 | 0 | { | 2626 | 0 | std::string arg(chunk, 0, separator); | 2627 | 0 | if (auto flag = this->Match(arg.substr(longprefix.size()))) | 2628 | 0 | { | 2629 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) | 2630 | 0 | { | 2631 | 0 | AddCompletionReply(chunk, arg + longseparator + choice); | 2632 | 0 | } | 2633 | 0 | } | 2634 | 0 | } | 2635 | 0 | } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) | 2636 | 0 | { | 2637 | 0 | if (chunk.size() > shortprefix.size() + 1) | 2638 | 0 | { | 2639 | 0 | auto arg = chunk.at(shortprefix.size()); | 2640 | | //TODO: support -abcVALUE where a and b take no value | 2641 | 0 | if (auto flag = this->Match(arg)) | 2642 | 0 | { | 2643 | 0 | for (auto &choice : flag->HelpChoices(helpParams)) | 2644 | 0 | { | 2645 | 0 | AddCompletionReply(chunk, shortprefix + arg + choice); | 2646 | 0 | } | 2647 | 0 | } | 2648 | 0 | } | 2649 | 0 | } | 2650 | 0 | } | 2651 | 0 | } | 2652 | |
| 2653 | 0 | #ifndef ARGS_NOEXCEPT | 2654 | 0 | throw Completion(completion->Get()); | 2655 | | #else | 2656 | | return true; | 2657 | | #endif | 2658 | 1.18k | } |
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*>) |
2659 | | |
2660 | | template <typename It> |
2661 | | It Parse(It begin, It end) |
2662 | 906 | { |
2663 | 906 | bool terminated = false; |
2664 | 906 | std::vector<Command *> commands = GetCommands(); |
2665 | | |
2666 | | // Check all arg chunks |
2667 | 1.90k | for (auto it = begin; it != end; ++it) |
2668 | 1.18k | { |
2669 | 1.18k | if (Complete(it, end)) |
2670 | 0 | { |
2671 | 0 | return end; |
2672 | 0 | } |
2673 | | |
2674 | 1.18k | const auto &chunk = *it; |
2675 | | |
2676 | 1.18k | if (!terminated && chunk == terminator) |
2677 | 2 | { |
2678 | 2 | terminated = true; |
2679 | 1.17k | } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) |
2680 | 538 | { |
2681 | 538 | if (!ParseLong(it, end)) |
2682 | 0 | { |
2683 | 0 | return it; |
2684 | 0 | } |
2685 | 640 | } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) |
2686 | 458 | { |
2687 | 458 | if (!ParseShort(it, end)) |
2688 | 0 | { |
2689 | 0 | return it; |
2690 | 0 | } |
2691 | 458 | } else if (!terminated && !commands.empty()) |
2692 | 0 | { |
2693 | 0 | auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); |
2694 | 0 | if (itCommand == commands.end()) |
2695 | 0 | { |
2696 | 0 | const std::string errorMessage("Unknown command: " + chunk); |
2697 | 0 | #ifndef ARGS_NOEXCEPT |
2698 | 0 | throw ParseError(errorMessage); |
2699 | | #else |
2700 | | error = Error::Parse; |
2701 | | errorMsg = errorMessage; |
2702 | | return it; |
2703 | | #endif |
2704 | 0 | } |
2705 | | |
2706 | 0 | SelectCommand(*itCommand); |
2707 | |
|
2708 | 0 | if (const auto &coroutine = GetCoroutine()) |
2709 | 0 | { |
2710 | 0 | ++it; |
2711 | 0 | RaiiSubparser coro(*this, std::vector<std::string>(it, end)); |
2712 | 0 | coroutine(coro.Parser()); |
2713 | | #ifdef ARGS_NOEXCEPT |
2714 | | error = GetError(); |
2715 | | if (error != Error::None) |
2716 | | { |
2717 | | return end; |
2718 | | } |
2719 | | |
2720 | | if (!coro.Parser().IsParsed()) |
2721 | | { |
2722 | | error = Error::Usage; |
2723 | | return end; |
2724 | | } |
2725 | | #else |
2726 | 0 | if (!coro.Parser().IsParsed()) |
2727 | 0 | { |
2728 | 0 | throw UsageError("Subparser::Parse was not called"); |
2729 | 0 | } |
2730 | 0 | #endif |
2731 | | |
2732 | 0 | break; |
2733 | 0 | } |
2734 | | |
2735 | 0 | commands = GetCommands(); |
2736 | 0 | } else |
2737 | 182 | { |
2738 | 182 | auto pos = GetNextPositional(); |
2739 | 182 | if (pos) |
2740 | 0 | { |
2741 | 0 | pos->ParseValue(chunk); |
2742 | |
|
2743 | 0 | if (pos->KickOut()) |
2744 | 0 | { |
2745 | 0 | return ++it; |
2746 | 0 | } |
2747 | 0 | } else |
2748 | 182 | { |
2749 | 182 | const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); |
2750 | 182 | #ifndef ARGS_NOEXCEPT |
2751 | 182 | throw ParseError(errorMessage); |
2752 | | #else |
2753 | | error = Error::Parse; |
2754 | | errorMsg = errorMessage; |
2755 | | return it; |
2756 | | #endif |
2757 | 182 | } |
2758 | 182 | } |
2759 | | |
2760 | 998 | if (!readCompletion && completion != nullptr && completion->Matched()) |
2761 | 0 | { |
2762 | | #ifdef ARGS_NOEXCEPT |
2763 | | error = Error::Completion; |
2764 | | #endif |
2765 | 0 | readCompletion = true; |
2766 | 0 | ++it; |
2767 | 0 | const auto argsLeft = static_cast<size_t>(std::distance(it, end)); |
2768 | 0 | if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) |
2769 | 0 | { |
2770 | 0 | #ifndef ARGS_NOEXCEPT |
2771 | 0 | throw Completion(""); |
2772 | 0 | #endif |
2773 | 0 | } |
2774 | | |
2775 | 0 | std::vector<std::string> curArgs(++it, end); |
2776 | 0 | curArgs.resize(completion->cword); |
2777 | |
|
2778 | 0 | if (completion->syntax == "bash") |
2779 | 0 | { |
2780 | | // bash tokenizes --flag=value as --flag=value |
2781 | 0 | for (size_t idx = 0; idx < curArgs.size(); ) |
2782 | 0 | { |
2783 | 0 | if (idx > 0 && curArgs[idx] == "=") |
2784 | 0 | { |
2785 | 0 | curArgs[idx - 1] += "="; |
2786 | | // Avoid warnings from -Wsign-conversion |
2787 | 0 | const auto signedIdx = static_cast<std::ptrdiff_t>(idx); |
2788 | 0 | if (idx + 1 < curArgs.size()) |
2789 | 0 | { |
2790 | 0 | curArgs[idx - 1] += curArgs[idx + 1]; |
2791 | 0 | curArgs.erase(curArgs.begin() + signedIdx, curArgs.begin() + signedIdx + 2); |
2792 | 0 | } else |
2793 | 0 | { |
2794 | 0 | curArgs.erase(curArgs.begin() + signedIdx); |
2795 | 0 | } |
2796 | 0 | } else |
2797 | 0 | { |
2798 | 0 | ++idx; |
2799 | 0 | } |
2800 | 0 | } |
2801 | |
|
2802 | 0 | } |
2803 | 0 | #ifndef ARGS_NOEXCEPT |
2804 | 0 | try |
2805 | 0 | { |
2806 | 0 | Parse(curArgs.begin(), curArgs.end()); |
2807 | 0 | throw Completion(""); |
2808 | 0 | } |
2809 | 0 | catch (Completion &) |
2810 | 0 | { |
2811 | 0 | throw; |
2812 | 0 | } |
2813 | 0 | catch (args::Error&) |
2814 | 0 | { |
2815 | 0 | throw Completion(""); |
2816 | 0 | } |
2817 | | #else |
2818 | | return Parse(curArgs.begin(), curArgs.end()); |
2819 | | #endif |
2820 | 0 | } |
2821 | 998 | } |
2822 | | |
2823 | 724 | Validate(shortprefix, longprefix); |
2824 | 724 | return end; |
2825 | 906 | } 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 | 2662 | 906 | { | 2663 | 906 | bool terminated = false; | 2664 | 906 | std::vector<Command *> commands = GetCommands(); | 2665 | | | 2666 | | // Check all arg chunks | 2667 | 1.90k | for (auto it = begin; it != end; ++it) | 2668 | 1.18k | { | 2669 | 1.18k | if (Complete(it, end)) | 2670 | 0 | { | 2671 | 0 | return end; | 2672 | 0 | } | 2673 | | | 2674 | 1.18k | const auto &chunk = *it; | 2675 | | | 2676 | 1.18k | if (!terminated && chunk == terminator) | 2677 | 2 | { | 2678 | 2 | terminated = true; | 2679 | 1.17k | } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) | 2680 | 538 | { | 2681 | 538 | if (!ParseLong(it, end)) | 2682 | 0 | { | 2683 | 0 | return it; | 2684 | 0 | } | 2685 | 640 | } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) | 2686 | 458 | { | 2687 | 458 | if (!ParseShort(it, end)) | 2688 | 0 | { | 2689 | 0 | return it; | 2690 | 0 | } | 2691 | 458 | } else if (!terminated && !commands.empty()) | 2692 | 0 | { | 2693 | 0 | auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); | 2694 | 0 | if (itCommand == commands.end()) | 2695 | 0 | { | 2696 | 0 | const std::string errorMessage("Unknown command: " + chunk); | 2697 | 0 | #ifndef ARGS_NOEXCEPT | 2698 | 0 | throw ParseError(errorMessage); | 2699 | | #else | 2700 | | error = Error::Parse; | 2701 | | errorMsg = errorMessage; | 2702 | | return it; | 2703 | | #endif | 2704 | 0 | } | 2705 | | | 2706 | 0 | SelectCommand(*itCommand); | 2707 | |
| 2708 | 0 | if (const auto &coroutine = GetCoroutine()) | 2709 | 0 | { | 2710 | 0 | ++it; | 2711 | 0 | RaiiSubparser coro(*this, std::vector<std::string>(it, end)); | 2712 | 0 | coroutine(coro.Parser()); | 2713 | | #ifdef ARGS_NOEXCEPT | 2714 | | error = GetError(); | 2715 | | if (error != Error::None) | 2716 | | { | 2717 | | return end; | 2718 | | } | 2719 | | | 2720 | | if (!coro.Parser().IsParsed()) | 2721 | | { | 2722 | | error = Error::Usage; | 2723 | | return end; | 2724 | | } | 2725 | | #else | 2726 | 0 | if (!coro.Parser().IsParsed()) | 2727 | 0 | { | 2728 | 0 | throw UsageError("Subparser::Parse was not called"); | 2729 | 0 | } | 2730 | 0 | #endif | 2731 | | | 2732 | 0 | break; | 2733 | 0 | } | 2734 | | | 2735 | 0 | commands = GetCommands(); | 2736 | 0 | } else | 2737 | 182 | { | 2738 | 182 | auto pos = GetNextPositional(); | 2739 | 182 | if (pos) | 2740 | 0 | { | 2741 | 0 | pos->ParseValue(chunk); | 2742 | |
| 2743 | 0 | if (pos->KickOut()) | 2744 | 0 | { | 2745 | 0 | return ++it; | 2746 | 0 | } | 2747 | 0 | } else | 2748 | 182 | { | 2749 | 182 | const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); | 2750 | 182 | #ifndef ARGS_NOEXCEPT | 2751 | 182 | throw ParseError(errorMessage); | 2752 | | #else | 2753 | | error = Error::Parse; | 2754 | | errorMsg = errorMessage; | 2755 | | return it; | 2756 | | #endif | 2757 | 182 | } | 2758 | 182 | } | 2759 | | | 2760 | 998 | if (!readCompletion && completion != nullptr && completion->Matched()) | 2761 | 0 | { | 2762 | | #ifdef ARGS_NOEXCEPT | 2763 | | error = Error::Completion; | 2764 | | #endif | 2765 | 0 | readCompletion = true; | 2766 | 0 | ++it; | 2767 | 0 | const auto argsLeft = static_cast<size_t>(std::distance(it, end)); | 2768 | 0 | if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) | 2769 | 0 | { | 2770 | 0 | #ifndef ARGS_NOEXCEPT | 2771 | 0 | throw Completion(""); | 2772 | 0 | #endif | 2773 | 0 | } | 2774 | | | 2775 | 0 | std::vector<std::string> curArgs(++it, end); | 2776 | 0 | curArgs.resize(completion->cword); | 2777 | |
| 2778 | 0 | if (completion->syntax == "bash") | 2779 | 0 | { | 2780 | | // bash tokenizes --flag=value as --flag=value | 2781 | 0 | for (size_t idx = 0; idx < curArgs.size(); ) | 2782 | 0 | { | 2783 | 0 | if (idx > 0 && curArgs[idx] == "=") | 2784 | 0 | { | 2785 | 0 | curArgs[idx - 1] += "="; | 2786 | | // Avoid warnings from -Wsign-conversion | 2787 | 0 | const auto signedIdx = static_cast<std::ptrdiff_t>(idx); | 2788 | 0 | if (idx + 1 < curArgs.size()) | 2789 | 0 | { | 2790 | 0 | curArgs[idx - 1] += curArgs[idx + 1]; | 2791 | 0 | curArgs.erase(curArgs.begin() + signedIdx, curArgs.begin() + signedIdx + 2); | 2792 | 0 | } else | 2793 | 0 | { | 2794 | 0 | curArgs.erase(curArgs.begin() + signedIdx); | 2795 | 0 | } | 2796 | 0 | } else | 2797 | 0 | { | 2798 | 0 | ++idx; | 2799 | 0 | } | 2800 | 0 | } | 2801 | |
| 2802 | 0 | } | 2803 | 0 | #ifndef ARGS_NOEXCEPT | 2804 | 0 | try | 2805 | 0 | { | 2806 | 0 | Parse(curArgs.begin(), curArgs.end()); | 2807 | 0 | throw Completion(""); | 2808 | 0 | } | 2809 | 0 | catch (Completion &) | 2810 | 0 | { | 2811 | 0 | throw; | 2812 | 0 | } | 2813 | 0 | catch (args::Error&) | 2814 | 0 | { | 2815 | 0 | throw Completion(""); | 2816 | 0 | } | 2817 | | #else | 2818 | | return Parse(curArgs.begin(), curArgs.end()); | 2819 | | #endif | 2820 | 0 | } | 2821 | 998 | } | 2822 | | | 2823 | 724 | Validate(shortprefix, longprefix); | 2824 | 724 | return end; | 2825 | 906 | } |
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*>) |
2826 | | |
2827 | | public: |
2828 | | HelpParams helpParams; |
2829 | | |
2830 | | ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string()) |
2831 | 906 | { |
2832 | 906 | Description(description_); |
2833 | 906 | Epilog(epilog_); |
2834 | 906 | LongPrefix("--"); |
2835 | 906 | ShortPrefix("-"); |
2836 | 906 | LongSeparator("="); |
2837 | 906 | Terminator("--"); |
2838 | 906 | SetArgumentSeparations(true, true, true, true); |
2839 | 906 | matched = true; |
2840 | 906 | } |
2841 | | |
2842 | | void AddCompletion(CompletionFlag &completionFlag) |
2843 | 0 | { |
2844 | 0 | completion = &completionFlag; |
2845 | 0 | Add(completionFlag); |
2846 | 0 | } |
2847 | | |
2848 | | /** The program name for help generation |
2849 | | */ |
2850 | | const std::string &Prog() const |
2851 | 0 | { return helpParams.programName; } |
2852 | | /** The program name for help generation |
2853 | | */ |
2854 | | void Prog(const std::string &prog_) |
2855 | 0 | { this->helpParams.programName = prog_; } |
2856 | | |
2857 | | /** The prefix for long flags |
2858 | | */ |
2859 | | const std::string &LongPrefix() const |
2860 | 0 | { return longprefix; } |
2861 | | /** The prefix for long flags |
2862 | | */ |
2863 | | void LongPrefix(const std::string &longprefix_) |
2864 | 906 | { |
2865 | 906 | this->longprefix = longprefix_; |
2866 | 906 | this->helpParams.longPrefix = longprefix_; |
2867 | 906 | } |
2868 | | |
2869 | | /** The prefix for short flags |
2870 | | */ |
2871 | | const std::string &ShortPrefix() const |
2872 | 0 | { return shortprefix; } |
2873 | | /** The prefix for short flags |
2874 | | */ |
2875 | | void ShortPrefix(const std::string &shortprefix_) |
2876 | 906 | { |
2877 | 906 | this->shortprefix = shortprefix_; |
2878 | 906 | this->helpParams.shortPrefix = shortprefix_; |
2879 | 906 | } |
2880 | | |
2881 | | /** The separator for long flags |
2882 | | */ |
2883 | | const std::string &LongSeparator() const |
2884 | 0 | { return longseparator; } |
2885 | | /** The separator for long flags |
2886 | | */ |
2887 | | void LongSeparator(const std::string &longseparator_) |
2888 | 906 | { |
2889 | 906 | if (longseparator_.empty()) |
2890 | 0 | { |
2891 | 0 | const std::string errorMessage("longseparator can not be set to empty"); |
2892 | | #ifdef ARGS_NOEXCEPT |
2893 | | error = Error::Usage; |
2894 | | errorMsg = errorMessage; |
2895 | | #else |
2896 | 0 | throw UsageError(errorMessage); |
2897 | 0 | #endif |
2898 | 0 | } else |
2899 | 906 | { |
2900 | 906 | this->longseparator = longseparator_; |
2901 | 906 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator_ : " "; |
2902 | 906 | } |
2903 | 906 | } |
2904 | | |
2905 | | /** The terminator that forcibly separates flags from positionals |
2906 | | */ |
2907 | | const std::string &Terminator() const |
2908 | 0 | { return terminator; } |
2909 | | /** The terminator that forcibly separates flags from positionals |
2910 | | */ |
2911 | | void Terminator(const std::string &terminator_) |
2912 | 906 | { this->terminator = terminator_; } |
2913 | | |
2914 | | /** Get the current argument separation parameters. |
2915 | | * |
2916 | | * See SetArgumentSeparations for details on what each one means. |
2917 | | */ |
2918 | | void GetArgumentSeparations( |
2919 | | bool &allowJoinedShortValue_, |
2920 | | bool &allowJoinedLongValue_, |
2921 | | bool &allowSeparateShortValue_, |
2922 | | bool &allowSeparateLongValue_) const |
2923 | 0 | { |
2924 | 0 | allowJoinedShortValue_ = this->allowJoinedShortValue; |
2925 | 0 | allowJoinedLongValue_ = this->allowJoinedLongValue; |
2926 | 0 | allowSeparateShortValue_ = this->allowSeparateShortValue; |
2927 | 0 | allowSeparateLongValue_ = this->allowSeparateLongValue; |
2928 | 0 | } |
2929 | | |
2930 | | /** Change allowed option separation. |
2931 | | * |
2932 | | * \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) |
2933 | | * \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) |
2934 | | * \param allowSeparateShortValue_ Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) |
2935 | | * \param allowSeparateLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) |
2936 | | */ |
2937 | | void SetArgumentSeparations( |
2938 | | const bool allowJoinedShortValue_, |
2939 | | const bool allowJoinedLongValue_, |
2940 | | const bool allowSeparateShortValue_, |
2941 | | const bool allowSeparateLongValue_) |
2942 | 906 | { |
2943 | 906 | this->allowJoinedShortValue = allowJoinedShortValue_; |
2944 | 906 | this->allowJoinedLongValue = allowJoinedLongValue_; |
2945 | 906 | this->allowSeparateShortValue = allowSeparateShortValue_; |
2946 | 906 | this->allowSeparateLongValue = allowSeparateLongValue_; |
2947 | | |
2948 | 906 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " "; |
2949 | 906 | this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " "; |
2950 | 906 | } |
2951 | | |
2952 | | /** Pass the help menu into an ostream |
2953 | | */ |
2954 | | void Help(std::ostream &help_) const |
2955 | 0 | { |
2956 | 0 | auto &command = SelectedCommand(); |
2957 | 0 | const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description(); |
2958 | 0 | const auto description_text = Wrap(commandDescription, helpParams.width - helpParams.descriptionindent); |
2959 | 0 | const auto epilog_text = Wrap(command.Epilog(), helpParams.width - helpParams.descriptionindent); |
2960 | 0 |
|
2961 | 0 | const bool hasoptions = command.HasFlag(); |
2962 | 0 | const bool hasarguments = command.HasPositional(); |
2963 | 0 |
|
2964 | 0 | std::vector<std::string> prognameline; |
2965 | 0 | prognameline.push_back(helpParams.usageString); |
2966 | 0 | prognameline.push_back(Prog()); |
2967 | 0 | auto commandProgLine = command.GetProgramLine(helpParams); |
2968 | 0 | prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end()); |
2969 | 0 |
|
2970 | 0 | const auto proglines = Wrap(prognameline.begin(), prognameline.end(), |
2971 | 0 | helpParams.width - (helpParams.progindent + helpParams.progtailindent), |
2972 | 0 | helpParams.width - helpParams.progindent); |
2973 | 0 | auto progit = std::begin(proglines); |
2974 | 0 | if (progit != std::end(proglines)) |
2975 | 0 | { |
2976 | 0 | help_ << std::string(helpParams.progindent, ' ') << *progit << '\n'; |
2977 | 0 | ++progit; |
2978 | 0 | } |
2979 | 0 | for (; progit != std::end(proglines); ++progit) |
2980 | 0 | { |
2981 | 0 | help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n'; |
2982 | 0 | } |
2983 | 0 |
|
2984 | 0 | help_ << '\n'; |
2985 | 0 |
|
2986 | 0 | if (!description_text.empty()) |
2987 | 0 | { |
2988 | 0 | for (const auto &line: description_text) |
2989 | 0 | { |
2990 | 0 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; |
2991 | 0 | } |
2992 | 0 | help_ << "\n"; |
2993 | 0 | } |
2994 | 0 |
|
2995 | 0 | bool lastDescriptionIsNewline = false; |
2996 | 0 |
|
2997 | 0 | if (!helpParams.optionsString.empty()) |
2998 | 0 | { |
2999 | 0 | help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n"; |
3000 | 0 | } |
3001 | 0 |
|
3002 | 0 | for (const auto &desc: command.GetDescription(helpParams, 0)) |
3003 | 0 | { |
3004 | 0 | lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty(); |
3005 | 0 | const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent; |
3006 | 0 | const auto flags = Wrap(std::get<0>(desc), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter)); |
3007 | 0 | const auto info = Wrap(std::get<1>(desc), helpParams.width - (helpParams.helpindent + groupindent)); |
3008 | 0 |
|
3009 | 0 | std::string::size_type flagssize = 0; |
3010 | 0 | for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit) |
3011 | 0 | { |
3012 | 0 | if (flagsit != std::begin(flags)) |
3013 | 0 | { |
3014 | 0 | help_ << '\n'; |
3015 | 0 | } |
3016 | 0 | help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit; |
3017 | 0 | flagssize = Glyphs(*flagsit); |
3018 | 0 | } |
3019 | 0 |
|
3020 | 0 | auto infoit = std::begin(info); |
3021 | 0 | // groupindent is on both sides of this inequality, and therefore can be removed |
3022 | 0 | if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription) |
3023 | 0 | { |
3024 | 0 | help_ << '\n'; |
3025 | 0 | } else |
3026 | 0 | { |
3027 | 0 | // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here |
3028 | 0 | help_ << std::string(helpParams.helpindent - (helpParams.flagindent + flagssize), ' ') << *infoit << '\n'; |
3029 | 0 | ++infoit; |
3030 | 0 | } |
3031 | 0 | for (; infoit != std::end(info); ++infoit) |
3032 | 0 | { |
3033 | 0 | help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n'; |
3034 | 0 | } |
3035 | 0 | } |
3036 | 0 | if (hasoptions && hasarguments && helpParams.showTerminator) |
3037 | 0 | { |
3038 | 0 | lastDescriptionIsNewline = false; |
3039 | 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", helpParams.width - helpParams.flagindent)) |
3040 | 0 | { |
3041 | 0 | help_ << std::string(helpParams.flagindent, ' ') << item << '\n'; |
3042 | 0 | } |
3043 | 0 | } |
3044 | 0 |
|
3045 | 0 | if (!lastDescriptionIsNewline) |
3046 | 0 | { |
3047 | 0 | help_ << "\n"; |
3048 | 0 | } |
3049 | 0 |
|
3050 | 0 | for (const auto &line: epilog_text) |
3051 | 0 | { |
3052 | 0 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; |
3053 | 0 | } |
3054 | 0 | } |
3055 | | |
3056 | | /** Generate a help menu as a string. |
3057 | | * |
3058 | | * \return the help text as a single string |
3059 | | */ |
3060 | | std::string Help() const |
3061 | 0 | { |
3062 | 0 | std::ostringstream help_; |
3063 | 0 | Help(help_); |
3064 | 0 | return help_.str(); |
3065 | 0 | } |
3066 | | |
3067 | | virtual void Reset() noexcept override |
3068 | 906 | { |
3069 | 906 | Command::Reset(); |
3070 | 906 | matched = true; |
3071 | 906 | readCompletion = false; |
3072 | 906 | } |
3073 | | |
3074 | | /** Parse all arguments. |
3075 | | * |
3076 | | * \param begin an iterator to the beginning of the argument list |
3077 | | * \param end an iterator to the past-the-end element of the argument list |
3078 | | * \return the iterator after the last parsed value. Only useful for kick-out |
3079 | | */ |
3080 | | template <typename It> |
3081 | | It ParseArgs(It begin, It end) |
3082 | 906 | { |
3083 | | // Reset all Matched statuses and errors |
3084 | 906 | Reset(); |
3085 | | #ifdef ARGS_NOEXCEPT |
3086 | | error = GetError(); |
3087 | | if (error != Error::None) |
3088 | | { |
3089 | | return end; |
3090 | | } |
3091 | | #endif |
3092 | 906 | return Parse(begin, end); |
3093 | 906 | } 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 | 3082 | 906 | { | 3083 | | // Reset all Matched statuses and errors | 3084 | 906 | Reset(); | 3085 | | #ifdef ARGS_NOEXCEPT | 3086 | | error = GetError(); | 3087 | | if (error != Error::None) | 3088 | | { | 3089 | | return end; | 3090 | | } | 3091 | | #endif | 3092 | 906 | return Parse(begin, end); | 3093 | 906 | } |
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*>) |
3094 | | |
3095 | | /** Parse all arguments. |
3096 | | * |
3097 | | * \param args an iterable of the arguments |
3098 | | * \return the iterator after the last parsed value. Only useful for kick-out |
3099 | | */ |
3100 | | template <typename T> |
3101 | | auto ParseArgs(const T &args) -> decltype(std::begin(args)) |
3102 | 0 | { |
3103 | 0 | return ParseArgs(std::begin(args), std::end(args)); |
3104 | 0 | } |
3105 | | |
3106 | | /** Convenience function to parse the CLI from argc and argv |
3107 | | * |
3108 | | * Just assigns the program name and vectorizes arguments for passing into ParseArgs() |
3109 | | * |
3110 | | * \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. |
3111 | | */ |
3112 | | bool ParseCLI(const int argc, const char * const * argv) |
3113 | 0 | { |
3114 | 0 | if (Prog().empty()) |
3115 | 0 | { |
3116 | 0 | Prog(argv[0]); |
3117 | 0 | } |
3118 | 0 | const std::vector<std::string> args(argv + 1, argv + argc); |
3119 | 0 | return ParseArgs(args) == std::end(args); |
3120 | 0 | } |
3121 | | |
3122 | | template <typename T> |
3123 | | bool ParseCLI(const T &args) |
3124 | | { |
3125 | | return ParseArgs(args) == std::end(args); |
3126 | | } |
3127 | | }; |
3128 | | |
3129 | | inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_) |
3130 | 0 | : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser) |
3131 | 0 | { |
3132 | 0 | command.subparser = &parser; |
3133 | 0 | } |
3134 | | |
3135 | 0 | inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams ¶ms_): command(command_), parser(command, params_), oldSubparser(command.subparser) |
3136 | 0 | { |
3137 | 0 | command.subparser = &parser; |
3138 | 0 | } |
3139 | | |
3140 | | inline void Subparser::Parse() |
3141 | 0 | { |
3142 | 0 | isParsed = true; |
3143 | 0 | Reset(); |
3144 | 0 | command.subparserDescription = GetDescription(helpParams, 0); |
3145 | 0 | command.subparserHasFlag = HasFlag(); |
3146 | 0 | command.subparserHasPositional = HasPositional(); |
3147 | 0 | command.subparserHasCommand = HasCommand(); |
3148 | 0 | command.subparserProgramLine = GetProgramLine(helpParams); |
3149 | 0 | if (parser == nullptr) |
3150 | 0 | { |
3151 | 0 | #ifndef ARGS_NOEXCEPT |
3152 | 0 | throw args::SubparserError(); |
3153 | 0 | #else |
3154 | 0 | error = Error::Subparser; |
3155 | 0 | return; |
3156 | 0 | #endif |
3157 | 0 | } |
3158 | 0 |
|
3159 | 0 | auto it = parser->Parse(args.begin(), args.end()); |
3160 | 0 | command.Validate(parser->ShortPrefix(), parser->LongPrefix()); |
3161 | 0 | kicked.assign(it, args.end()); |
3162 | 0 |
|
3163 | 0 | #ifdef ARGS_NOEXCEPT |
3164 | 0 | command.subparserError = GetError(); |
3165 | 0 | #endif |
3166 | 0 | } |
3167 | | |
3168 | | inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser) |
3169 | 0 | { |
3170 | 0 | parser.Help(os); |
3171 | 0 | return os; |
3172 | 0 | } |
3173 | | |
3174 | | /** Boolean argument matcher |
3175 | | */ |
3176 | | class Flag : public FlagBase |
3177 | | { |
3178 | | public: |
3179 | 2.71k | Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_) |
3180 | 2.71k | { |
3181 | 2.71k | group_.Add(*this); |
3182 | 2.71k | } |
3183 | | |
3184 | 1.81k | 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) |
3185 | 1.81k | { |
3186 | 1.81k | } |
3187 | | |
3188 | 0 | virtual ~Flag() {} |
3189 | | |
3190 | | /** Get whether this was matched |
3191 | | */ |
3192 | | bool Get() const |
3193 | 0 | { |
3194 | 0 | return Matched(); |
3195 | 0 | } |
3196 | | |
3197 | | virtual Nargs NumberOfArguments() const noexcept override |
3198 | 6.68k | { |
3199 | 6.68k | return 0; |
3200 | 6.68k | } |
3201 | | |
3202 | | virtual void ParseValue(const std::vector<std::string>&) override |
3203 | 6.65k | { |
3204 | 6.65k | } |
3205 | | }; |
3206 | | |
3207 | | /** Help flag class |
3208 | | * |
3209 | | * Works like a regular flag, but throws an instance of Help when it is matched |
3210 | | */ |
3211 | | class HelpFlag : public Flag |
3212 | | { |
3213 | | public: |
3214 | 906 | HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {} |
3215 | | |
3216 | 0 | virtual ~HelpFlag() {} |
3217 | | |
3218 | | virtual void ParseValue(const std::vector<std::string> &) |
3219 | 21 | { |
3220 | | #ifdef ARGS_NOEXCEPT |
3221 | | error = Error::Help; |
3222 | | errorMsg = Name(); |
3223 | | #else |
3224 | 21 | throw Help(Name()); |
3225 | 21 | #endif |
3226 | 21 | } |
3227 | | |
3228 | | /** Get whether this was matched |
3229 | | */ |
3230 | | bool Get() const noexcept |
3231 | 0 | { |
3232 | 0 | return Matched(); |
3233 | 0 | } |
3234 | | }; |
3235 | | |
3236 | | /** A flag class that simply counts the number of times it's matched |
3237 | | */ |
3238 | | class CounterFlag : public Flag |
3239 | | { |
3240 | | private: |
3241 | | const int startcount; |
3242 | | int count; |
3243 | | |
3244 | | public: |
3245 | | CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}): |
3246 | 0 | Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {} |
3247 | | |
3248 | 0 | virtual ~CounterFlag() {} |
3249 | | |
3250 | | virtual FlagBase *Match(const EitherFlag &arg) override |
3251 | 0 | { |
3252 | 0 | auto me = FlagBase::Match(arg); |
3253 | 0 | if (me) |
3254 | 0 | { |
3255 | 0 | ++count; |
3256 | 0 | } |
3257 | 0 | return me; |
3258 | 0 | } |
3259 | | |
3260 | | /** Get the count |
3261 | | */ |
3262 | | int &Get() noexcept |
3263 | 0 | { |
3264 | 0 | return count; |
3265 | 0 | } |
3266 | | |
3267 | 0 | int &operator *() noexcept { |
3268 | 0 | return count; |
3269 | 0 | } |
3270 | | |
3271 | 0 | const int &operator *() const noexcept { |
3272 | 0 | return count; |
3273 | 0 | } |
3274 | | |
3275 | | virtual void Reset() noexcept override |
3276 | 0 | { |
3277 | 0 | FlagBase::Reset(); |
3278 | 0 | count = startcount; |
3279 | 0 | } |
3280 | | }; |
3281 | | |
3282 | | /** A flag class that calls a function when it's matched |
3283 | | */ |
3284 | | class ActionFlag : public FlagBase |
3285 | | { |
3286 | | private: |
3287 | | std::function<void(const std::vector<std::string> &)> action; |
3288 | | Nargs nargs; |
3289 | | |
3290 | | public: |
3291 | | 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_ = {}): |
3292 | | FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_) |
3293 | 0 | { |
3294 | 0 | group_.Add(*this); |
3295 | 0 | } |
3296 | | |
3297 | | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}): |
3298 | | FlagBase(name_, help_, std::move(matcher_), options_), nargs(1) |
3299 | 0 | { |
3300 | 0 | group_.Add(*this); |
3301 | 0 | action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); }; |
3302 | 0 | } |
3303 | | |
3304 | | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}): |
3305 | | FlagBase(name_, help_, std::move(matcher_), options_), nargs(0) |
3306 | 0 | { |
3307 | 0 | group_.Add(*this); |
3308 | 0 | action = [action_](const std::vector<std::string> &) { return action_(); }; |
3309 | 0 | } |
3310 | | |
3311 | | virtual Nargs NumberOfArguments() const noexcept override |
3312 | 0 | { return nargs; } |
3313 | | |
3314 | | virtual void ParseValue(const std::vector<std::string> &value) override |
3315 | 0 | { action(value); } |
3316 | | }; |
3317 | | |
3318 | | /** A default Reader class for argument classes |
3319 | | * |
3320 | | * If destination type is assignable to std::string it uses an assignment to std::string. |
3321 | | * Otherwise ValueReader simply uses a std::istringstream to read into the destination type, and |
3322 | | * raises a ParseError if there are any characters left. |
3323 | | */ |
3324 | | struct ValueReader |
3325 | | { |
3326 | | template <typename T> |
3327 | | typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type |
3328 | | operator ()(const std::string &name, const std::string &value, T &destination) |
3329 | | { |
3330 | | std::istringstream ss(value); |
3331 | | bool failed = !(ss >> destination); |
3332 | | |
3333 | | if (!failed) |
3334 | | { |
3335 | | ss >> std::ws; |
3336 | | } |
3337 | | |
3338 | | if (ss.rdbuf()->in_avail() > 0 || failed) |
3339 | | { |
3340 | | #ifdef ARGS_NOEXCEPT |
3341 | | (void)name; |
3342 | | return false; |
3343 | | #else |
3344 | | std::ostringstream problem; |
3345 | | problem << "Argument '" << name << "' received invalid value type '" << value << "'"; |
3346 | | throw ParseError(problem.str()); |
3347 | | #endif |
3348 | | } |
3349 | | return true; |
3350 | | } |
3351 | | |
3352 | | template <typename T> |
3353 | | typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type |
3354 | | operator()(const std::string &, const std::string &value, T &destination) |
3355 | | { |
3356 | | destination = value; |
3357 | | return true; |
3358 | | } |
3359 | | }; |
3360 | | |
3361 | | /** An argument-accepting flag class |
3362 | | * |
3363 | | * \tparam T the type to extract the argument as |
3364 | | * \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) |
3365 | | */ |
3366 | | template < |
3367 | | typename T, |
3368 | | typename Reader = ValueReader> |
3369 | | class ValueFlag : public ValueFlagBase |
3370 | | { |
3371 | | protected: |
3372 | | T value; |
3373 | | T defaultValue; |
3374 | | |
3375 | | virtual std::string GetDefaultString(const HelpParams&) const override |
3376 | | { |
3377 | | return detail::ToString(defaultValue); |
3378 | | } |
3379 | | |
3380 | | private: |
3381 | | Reader reader; |
3382 | | |
3383 | | public: |
3384 | | |
3385 | | 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_) |
3386 | | { |
3387 | | group_.Add(*this); |
3388 | | } |
3389 | | |
3390 | | 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) |
3391 | | { |
3392 | | } |
3393 | | |
3394 | | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_) |
3395 | | { |
3396 | | } |
3397 | | |
3398 | | virtual ~ValueFlag() {} |
3399 | | |
3400 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
3401 | | { |
3402 | | const std::string &value_ = values_.at(0); |
3403 | | |
3404 | | #ifdef ARGS_NOEXCEPT |
3405 | | if (!reader(name, value_, this->value)) |
3406 | | { |
3407 | | error = Error::Parse; |
3408 | | } |
3409 | | #else |
3410 | | reader(name, value_, this->value); |
3411 | | #endif |
3412 | | } |
3413 | | |
3414 | | virtual void Reset() noexcept override |
3415 | | { |
3416 | | ValueFlagBase::Reset(); |
3417 | | value = defaultValue; |
3418 | | } |
3419 | | |
3420 | | /** Get the value |
3421 | | */ |
3422 | | T &Get() noexcept |
3423 | | { |
3424 | | return value; |
3425 | | } |
3426 | | |
3427 | | /** Get the value |
3428 | | */ |
3429 | | T &operator *() noexcept |
3430 | | { |
3431 | | return value; |
3432 | | } |
3433 | | |
3434 | | /** Get the value |
3435 | | */ |
3436 | | const T &operator *() const noexcept |
3437 | | { |
3438 | | return value; |
3439 | | } |
3440 | | |
3441 | | /** Get the value |
3442 | | */ |
3443 | | T *operator ->() noexcept |
3444 | | { |
3445 | | return &value; |
3446 | | } |
3447 | | |
3448 | | /** Get the value |
3449 | | */ |
3450 | | const T *operator ->() const noexcept |
3451 | | { |
3452 | | return &value; |
3453 | | } |
3454 | | |
3455 | | /** Get the default value |
3456 | | */ |
3457 | | const T &GetDefault() noexcept |
3458 | | { |
3459 | | return defaultValue; |
3460 | | } |
3461 | | }; |
3462 | | |
3463 | | /** An optional argument-accepting flag class |
3464 | | * |
3465 | | * \tparam T the type to extract the argument as |
3466 | | * \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) |
3467 | | */ |
3468 | | template < |
3469 | | typename T, |
3470 | | typename Reader = ValueReader> |
3471 | | class ImplicitValueFlag : public ValueFlag<T, Reader> |
3472 | | { |
3473 | | protected: |
3474 | | T implicitValue; |
3475 | | |
3476 | | public: |
3477 | | |
3478 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {}) |
3479 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_) |
3480 | | { |
3481 | | } |
3482 | | |
3483 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {}) |
3484 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_) |
3485 | | { |
3486 | | } |
3487 | | |
3488 | | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) |
3489 | | : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue() |
3490 | | { |
3491 | | } |
3492 | | |
3493 | | virtual ~ImplicitValueFlag() {} |
3494 | | |
3495 | | virtual Nargs NumberOfArguments() const noexcept override |
3496 | | { |
3497 | | return {0, 1}; |
3498 | | } |
3499 | | |
3500 | | virtual void ParseValue(const std::vector<std::string> &value_) override |
3501 | | { |
3502 | | if (value_.empty()) |
3503 | | { |
3504 | | this->value = implicitValue; |
3505 | | } else |
3506 | | { |
3507 | | ValueFlag<T, Reader>::ParseValue(value_); |
3508 | | } |
3509 | | } |
3510 | | }; |
3511 | | |
3512 | | /** A variadic arguments accepting flag class |
3513 | | * |
3514 | | * \tparam T the type to extract the argument as |
3515 | | * \tparam List the list type that houses the values |
3516 | | * \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) |
3517 | | */ |
3518 | | template < |
3519 | | typename T, |
3520 | | template <typename...> class List = detail::vector, |
3521 | | typename Reader = ValueReader> |
3522 | | class NargsValueFlag : public FlagBase |
3523 | | { |
3524 | | protected: |
3525 | | |
3526 | | List<T> values; |
3527 | | const List<T> defaultValues; |
3528 | | Nargs nargs; |
3529 | | Reader reader; |
3530 | | |
3531 | | public: |
3532 | | |
3533 | | typedef List<T> Container; |
3534 | | typedef T value_type; |
3535 | | typedef typename Container::allocator_type allocator_type; |
3536 | | typedef typename Container::pointer pointer; |
3537 | | typedef typename Container::const_pointer const_pointer; |
3538 | | typedef T& reference; |
3539 | | typedef const T& const_reference; |
3540 | | typedef typename Container::size_type size_type; |
3541 | | typedef typename Container::difference_type difference_type; |
3542 | | typedef typename Container::iterator iterator; |
3543 | | typedef typename Container::const_iterator const_iterator; |
3544 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
3545 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
3546 | | |
3547 | | NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {}) |
3548 | | : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_) |
3549 | | { |
3550 | | group_.Add(*this); |
3551 | | } |
3552 | | |
3553 | | virtual ~NargsValueFlag() {} |
3554 | | |
3555 | | virtual Nargs NumberOfArguments() const noexcept override |
3556 | | { |
3557 | | return nargs; |
3558 | | } |
3559 | | |
3560 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
3561 | | { |
3562 | | values.clear(); |
3563 | | |
3564 | | for (const std::string &value : values_) |
3565 | | { |
3566 | | T v; |
3567 | | #ifdef ARGS_NOEXCEPT |
3568 | | if (!reader(name, value, v)) |
3569 | | { |
3570 | | error = Error::Parse; |
3571 | | } |
3572 | | #else |
3573 | | reader(name, value, v); |
3574 | | #endif |
3575 | | values.insert(std::end(values), v); |
3576 | | } |
3577 | | } |
3578 | | |
3579 | | List<T> &Get() noexcept |
3580 | | { |
3581 | | return values; |
3582 | | } |
3583 | | |
3584 | | /** Get the value |
3585 | | */ |
3586 | | List<T> &operator *() noexcept |
3587 | | { |
3588 | | return values; |
3589 | | } |
3590 | | |
3591 | | /** Get the values |
3592 | | */ |
3593 | | const List<T> &operator *() const noexcept |
3594 | | { |
3595 | | return values; |
3596 | | } |
3597 | | |
3598 | | /** Get the values |
3599 | | */ |
3600 | | List<T> *operator ->() noexcept |
3601 | | { |
3602 | | return &values; |
3603 | | } |
3604 | | |
3605 | | /** Get the values |
3606 | | */ |
3607 | | const List<T> *operator ->() const noexcept |
3608 | | { |
3609 | | return &values; |
3610 | | } |
3611 | | |
3612 | | iterator begin() noexcept |
3613 | | { |
3614 | | return values.begin(); |
3615 | | } |
3616 | | |
3617 | | const_iterator begin() const noexcept |
3618 | | { |
3619 | | return values.begin(); |
3620 | | } |
3621 | | |
3622 | | const_iterator cbegin() const noexcept |
3623 | | { |
3624 | | return values.cbegin(); |
3625 | | } |
3626 | | |
3627 | | iterator end() noexcept |
3628 | | { |
3629 | | return values.end(); |
3630 | | } |
3631 | | |
3632 | | const_iterator end() const noexcept |
3633 | | { |
3634 | | return values.end(); |
3635 | | } |
3636 | | |
3637 | | const_iterator cend() const noexcept |
3638 | | { |
3639 | | return values.cend(); |
3640 | | } |
3641 | | |
3642 | | virtual void Reset() noexcept override |
3643 | | { |
3644 | | FlagBase::Reset(); |
3645 | | values = defaultValues; |
3646 | | } |
3647 | | |
3648 | | virtual FlagBase *Match(const EitherFlag &arg) override |
3649 | | { |
3650 | | const bool wasMatched = Matched(); |
3651 | | auto me = FlagBase::Match(arg); |
3652 | | if (me && !wasMatched) |
3653 | | { |
3654 | | values.clear(); |
3655 | | } |
3656 | | return me; |
3657 | | } |
3658 | | }; |
3659 | | |
3660 | | /** An argument-accepting flag class that pushes the found values into a list |
3661 | | * |
3662 | | * \tparam T the type to extract the argument as |
3663 | | * \tparam List the list type that houses the values |
3664 | | * \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) |
3665 | | */ |
3666 | | template < |
3667 | | typename T, |
3668 | | template <typename...> class List = detail::vector, |
3669 | | typename Reader = ValueReader> |
3670 | | class ValueFlagList : public ValueFlagBase |
3671 | | { |
3672 | | private: |
3673 | | using Container = List<T>; |
3674 | | Container values; |
3675 | | const Container defaultValues; |
3676 | | Reader reader; |
3677 | | |
3678 | | public: |
3679 | | |
3680 | | typedef T value_type; |
3681 | | typedef typename Container::allocator_type allocator_type; |
3682 | | typedef typename Container::pointer pointer; |
3683 | | typedef typename Container::const_pointer const_pointer; |
3684 | | typedef T& reference; |
3685 | | typedef const T& const_reference; |
3686 | | typedef typename Container::size_type size_type; |
3687 | | typedef typename Container::difference_type difference_type; |
3688 | | typedef typename Container::iterator iterator; |
3689 | | typedef typename Container::const_iterator const_iterator; |
3690 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
3691 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
3692 | | |
3693 | | ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}): |
3694 | | ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_) |
3695 | | { |
3696 | | group_.Add(*this); |
3697 | | } |
3698 | | |
3699 | | virtual ~ValueFlagList() {} |
3700 | | |
3701 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
3702 | | { |
3703 | | const std::string &value_ = values_.at(0); |
3704 | | |
3705 | | T v; |
3706 | | #ifdef ARGS_NOEXCEPT |
3707 | | if (!reader(name, value_, v)) |
3708 | | { |
3709 | | error = Error::Parse; |
3710 | | } |
3711 | | #else |
3712 | | reader(name, value_, v); |
3713 | | #endif |
3714 | | values.insert(std::end(values), v); |
3715 | | } |
3716 | | |
3717 | | /** Get the values |
3718 | | */ |
3719 | | Container &Get() noexcept |
3720 | | { |
3721 | | return values; |
3722 | | } |
3723 | | |
3724 | | /** Get the value |
3725 | | */ |
3726 | | Container &operator *() noexcept |
3727 | | { |
3728 | | return values; |
3729 | | } |
3730 | | |
3731 | | /** Get the values |
3732 | | */ |
3733 | | const Container &operator *() const noexcept |
3734 | | { |
3735 | | return values; |
3736 | | } |
3737 | | |
3738 | | /** Get the values |
3739 | | */ |
3740 | | Container *operator ->() noexcept |
3741 | | { |
3742 | | return &values; |
3743 | | } |
3744 | | |
3745 | | /** Get the values |
3746 | | */ |
3747 | | const Container *operator ->() const noexcept |
3748 | | { |
3749 | | return &values; |
3750 | | } |
3751 | | |
3752 | | virtual std::string Name() const override |
3753 | | { |
3754 | | return name + std::string("..."); |
3755 | | } |
3756 | | |
3757 | | virtual void Reset() noexcept override |
3758 | | { |
3759 | | ValueFlagBase::Reset(); |
3760 | | values = defaultValues; |
3761 | | } |
3762 | | |
3763 | | virtual FlagBase *Match(const EitherFlag &arg) override |
3764 | | { |
3765 | | const bool wasMatched = Matched(); |
3766 | | auto me = FlagBase::Match(arg); |
3767 | | if (me && !wasMatched) |
3768 | | { |
3769 | | values.clear(); |
3770 | | } |
3771 | | return me; |
3772 | | } |
3773 | | |
3774 | | iterator begin() noexcept |
3775 | | { |
3776 | | return values.begin(); |
3777 | | } |
3778 | | |
3779 | | const_iterator begin() const noexcept |
3780 | | { |
3781 | | return values.begin(); |
3782 | | } |
3783 | | |
3784 | | const_iterator cbegin() const noexcept |
3785 | | { |
3786 | | return values.cbegin(); |
3787 | | } |
3788 | | |
3789 | | iterator end() noexcept |
3790 | | { |
3791 | | return values.end(); |
3792 | | } |
3793 | | |
3794 | | const_iterator end() const noexcept |
3795 | | { |
3796 | | return values.end(); |
3797 | | } |
3798 | | |
3799 | | const_iterator cend() const noexcept |
3800 | | { |
3801 | | return values.cend(); |
3802 | | } |
3803 | | }; |
3804 | | |
3805 | | /** A mapping value flag class |
3806 | | * |
3807 | | * \tparam K the type to extract the argument as |
3808 | | * \tparam T the type to store the result as |
3809 | | * \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) |
3810 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
3811 | | */ |
3812 | | template < |
3813 | | typename K, |
3814 | | typename T, |
3815 | | typename Reader = ValueReader, |
3816 | | template <typename...> class Map = detail::unordered_map> |
3817 | | class MapFlag : public ValueFlagBase |
3818 | | { |
3819 | | private: |
3820 | | const Map<K, T> map; |
3821 | | T value; |
3822 | | const T defaultValue; |
3823 | | Reader reader; |
3824 | | |
3825 | | protected: |
3826 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
3827 | | { |
3828 | | return detail::MapKeysToStrings(map); |
3829 | | } |
3830 | | |
3831 | | public: |
3832 | | |
3833 | | 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_) |
3834 | | { |
3835 | | group_.Add(*this); |
3836 | | } |
3837 | | |
3838 | | 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) |
3839 | | { |
3840 | | } |
3841 | | |
3842 | | 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_) |
3843 | | { |
3844 | | } |
3845 | | |
3846 | | virtual ~MapFlag() {} |
3847 | | |
3848 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
3849 | | { |
3850 | | const std::string &value_ = values_.at(0); |
3851 | | |
3852 | | K key; |
3853 | | #ifdef ARGS_NOEXCEPT |
3854 | | if (!reader(name, value_, key)) |
3855 | | { |
3856 | | error = Error::Parse; |
3857 | | } |
3858 | | #else |
3859 | | reader(name, value_, key); |
3860 | | #endif |
3861 | | auto it = map.find(key); |
3862 | | if (it == std::end(map)) |
3863 | | { |
3864 | | std::ostringstream problem; |
3865 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
3866 | | #ifdef ARGS_NOEXCEPT |
3867 | | error = Error::Map; |
3868 | | errorMsg = problem.str(); |
3869 | | #else |
3870 | | throw MapError(problem.str()); |
3871 | | #endif |
3872 | | } else |
3873 | | { |
3874 | | this->value = it->second; |
3875 | | } |
3876 | | } |
3877 | | |
3878 | | /** Get the value |
3879 | | */ |
3880 | | T &Get() noexcept |
3881 | | { |
3882 | | return value; |
3883 | | } |
3884 | | |
3885 | | /** Get the value |
3886 | | */ |
3887 | | T &operator *() noexcept |
3888 | | { |
3889 | | return value; |
3890 | | } |
3891 | | |
3892 | | /** Get the value |
3893 | | */ |
3894 | | const T &operator *() const noexcept |
3895 | | { |
3896 | | return value; |
3897 | | } |
3898 | | |
3899 | | /** Get the value |
3900 | | */ |
3901 | | T *operator ->() noexcept |
3902 | | { |
3903 | | return &value; |
3904 | | } |
3905 | | |
3906 | | /** Get the value |
3907 | | */ |
3908 | | const T *operator ->() const noexcept |
3909 | | { |
3910 | | return &value; |
3911 | | } |
3912 | | |
3913 | | virtual void Reset() noexcept override |
3914 | | { |
3915 | | ValueFlagBase::Reset(); |
3916 | | value = defaultValue; |
3917 | | } |
3918 | | }; |
3919 | | |
3920 | | /** A mapping value flag list class |
3921 | | * |
3922 | | * \tparam K the type to extract the argument as |
3923 | | * \tparam T the type to store the result as |
3924 | | * \tparam List the list type that houses the values |
3925 | | * \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) |
3926 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
3927 | | */ |
3928 | | template < |
3929 | | typename K, |
3930 | | typename T, |
3931 | | template <typename...> class List = detail::vector, |
3932 | | typename Reader = ValueReader, |
3933 | | template <typename...> class Map = detail::unordered_map> |
3934 | | class MapFlagList : public ValueFlagBase |
3935 | | { |
3936 | | private: |
3937 | | using Container = List<T>; |
3938 | | const Map<K, T> map; |
3939 | | Container values; |
3940 | | const Container defaultValues; |
3941 | | Reader reader; |
3942 | | |
3943 | | protected: |
3944 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
3945 | | { |
3946 | | return detail::MapKeysToStrings(map); |
3947 | | } |
3948 | | |
3949 | | public: |
3950 | | typedef T value_type; |
3951 | | typedef typename Container::allocator_type allocator_type; |
3952 | | typedef typename Container::pointer pointer; |
3953 | | typedef typename Container::const_pointer const_pointer; |
3954 | | typedef T& reference; |
3955 | | typedef const T& const_reference; |
3956 | | typedef typename Container::size_type size_type; |
3957 | | typedef typename Container::difference_type difference_type; |
3958 | | typedef typename Container::iterator iterator; |
3959 | | typedef typename Container::const_iterator const_iterator; |
3960 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
3961 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
3962 | | |
3963 | | MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container()): ValueFlagBase(name_, help_, std::move(matcher_)), map(map_), values(defaultValues_), defaultValues(defaultValues_) |
3964 | | { |
3965 | | group_.Add(*this); |
3966 | | } |
3967 | | |
3968 | | virtual ~MapFlagList() {} |
3969 | | |
3970 | | virtual void ParseValue(const std::vector<std::string> &values_) override |
3971 | | { |
3972 | | const std::string &value = values_.at(0); |
3973 | | |
3974 | | K key; |
3975 | | #ifdef ARGS_NOEXCEPT |
3976 | | if (!reader(name, value, key)) |
3977 | | { |
3978 | | error = Error::Parse; |
3979 | | } |
3980 | | #else |
3981 | | reader(name, value, key); |
3982 | | #endif |
3983 | | auto it = map.find(key); |
3984 | | if (it == std::end(map)) |
3985 | | { |
3986 | | std::ostringstream problem; |
3987 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
3988 | | #ifdef ARGS_NOEXCEPT |
3989 | | error = Error::Map; |
3990 | | errorMsg = problem.str(); |
3991 | | #else |
3992 | | throw MapError(problem.str()); |
3993 | | #endif |
3994 | | } else |
3995 | | { |
3996 | | this->values.emplace_back(it->second); |
3997 | | } |
3998 | | } |
3999 | | |
4000 | | /** Get the value |
4001 | | */ |
4002 | | Container &Get() noexcept |
4003 | | { |
4004 | | return values; |
4005 | | } |
4006 | | |
4007 | | /** Get the value |
4008 | | */ |
4009 | | Container &operator *() noexcept |
4010 | | { |
4011 | | return values; |
4012 | | } |
4013 | | |
4014 | | /** Get the values |
4015 | | */ |
4016 | | const Container &operator *() const noexcept |
4017 | | { |
4018 | | return values; |
4019 | | } |
4020 | | |
4021 | | /** Get the values |
4022 | | */ |
4023 | | Container *operator ->() noexcept |
4024 | | { |
4025 | | return &values; |
4026 | | } |
4027 | | |
4028 | | /** Get the values |
4029 | | */ |
4030 | | const Container *operator ->() const noexcept |
4031 | | { |
4032 | | return &values; |
4033 | | } |
4034 | | |
4035 | | virtual std::string Name() const override |
4036 | | { |
4037 | | return name + std::string("..."); |
4038 | | } |
4039 | | |
4040 | | virtual void Reset() noexcept override |
4041 | | { |
4042 | | ValueFlagBase::Reset(); |
4043 | | values = defaultValues; |
4044 | | } |
4045 | | |
4046 | | virtual FlagBase *Match(const EitherFlag &arg) override |
4047 | | { |
4048 | | const bool wasMatched = Matched(); |
4049 | | auto me = FlagBase::Match(arg); |
4050 | | if (me && !wasMatched) |
4051 | | { |
4052 | | values.clear(); |
4053 | | } |
4054 | | return me; |
4055 | | } |
4056 | | |
4057 | | iterator begin() noexcept |
4058 | | { |
4059 | | return values.begin(); |
4060 | | } |
4061 | | |
4062 | | const_iterator begin() const noexcept |
4063 | | { |
4064 | | return values.begin(); |
4065 | | } |
4066 | | |
4067 | | const_iterator cbegin() const noexcept |
4068 | | { |
4069 | | return values.cbegin(); |
4070 | | } |
4071 | | |
4072 | | iterator end() noexcept |
4073 | | { |
4074 | | return values.end(); |
4075 | | } |
4076 | | |
4077 | | const_iterator end() const noexcept |
4078 | | { |
4079 | | return values.end(); |
4080 | | } |
4081 | | |
4082 | | const_iterator cend() const noexcept |
4083 | | { |
4084 | | return values.cend(); |
4085 | | } |
4086 | | }; |
4087 | | |
4088 | | /** A positional argument class |
4089 | | * |
4090 | | * \tparam T the type to extract the argument as |
4091 | | * \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) |
4092 | | */ |
4093 | | template < |
4094 | | typename T, |
4095 | | typename Reader = ValueReader> |
4096 | | class Positional : public PositionalBase |
4097 | | { |
4098 | | private: |
4099 | | T value; |
4100 | | const T defaultValue; |
4101 | | Reader reader; |
4102 | | public: |
4103 | | 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_) |
4104 | | { |
4105 | | group_.Add(*this); |
4106 | | } |
4107 | | |
4108 | | Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_) |
4109 | | { |
4110 | | } |
4111 | | |
4112 | | virtual ~Positional() {} |
4113 | | |
4114 | | virtual void ParseValue(const std::string &value_) override |
4115 | | { |
4116 | | #ifdef ARGS_NOEXCEPT |
4117 | | if (!reader(name, value_, this->value)) |
4118 | | { |
4119 | | error = Error::Parse; |
4120 | | } |
4121 | | #else |
4122 | | reader(name, value_, this->value); |
4123 | | #endif |
4124 | | ready = false; |
4125 | | matched = true; |
4126 | | } |
4127 | | |
4128 | | /** Get the value |
4129 | | */ |
4130 | | T &Get() noexcept |
4131 | | { |
4132 | | return value; |
4133 | | } |
4134 | | |
4135 | | /** Get the value |
4136 | | */ |
4137 | | T &operator *() noexcept |
4138 | | { |
4139 | | return value; |
4140 | | } |
4141 | | |
4142 | | /** Get the value |
4143 | | */ |
4144 | | const T &operator *() const noexcept |
4145 | | { |
4146 | | return value; |
4147 | | } |
4148 | | |
4149 | | /** Get the value |
4150 | | */ |
4151 | | T *operator ->() noexcept |
4152 | | { |
4153 | | return &value; |
4154 | | } |
4155 | | |
4156 | | /** Get the value |
4157 | | */ |
4158 | | const T *operator ->() const noexcept |
4159 | | { |
4160 | | return &value; |
4161 | | } |
4162 | | |
4163 | | virtual void Reset() noexcept override |
4164 | | { |
4165 | | PositionalBase::Reset(); |
4166 | | value = defaultValue; |
4167 | | } |
4168 | | }; |
4169 | | |
4170 | | /** A positional argument class that pushes the found values into a list |
4171 | | * |
4172 | | * \tparam T the type to extract the argument as |
4173 | | * \tparam List the list type that houses the values |
4174 | | * \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) |
4175 | | */ |
4176 | | template < |
4177 | | typename T, |
4178 | | template <typename...> class List = detail::vector, |
4179 | | typename Reader = ValueReader> |
4180 | | class PositionalList : public PositionalBase |
4181 | | { |
4182 | | private: |
4183 | | using Container = List<T>; |
4184 | | Container values; |
4185 | | const Container defaultValues; |
4186 | | Reader reader; |
4187 | | |
4188 | | public: |
4189 | | typedef T value_type; |
4190 | | typedef typename Container::allocator_type allocator_type; |
4191 | | typedef typename Container::pointer pointer; |
4192 | | typedef typename Container::const_pointer const_pointer; |
4193 | | typedef T& reference; |
4194 | | typedef const T& const_reference; |
4195 | | typedef typename Container::size_type size_type; |
4196 | | typedef typename Container::difference_type difference_type; |
4197 | | typedef typename Container::iterator iterator; |
4198 | | typedef typename Container::const_iterator const_iterator; |
4199 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4200 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4201 | | |
4202 | | 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_) |
4203 | | { |
4204 | | group_.Add(*this); |
4205 | | } |
4206 | | |
4207 | | PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_) |
4208 | | { |
4209 | | } |
4210 | | |
4211 | | virtual ~PositionalList() {} |
4212 | | |
4213 | | virtual void ParseValue(const std::string &value_) override |
4214 | | { |
4215 | | T v; |
4216 | | #ifdef ARGS_NOEXCEPT |
4217 | | if (!reader(name, value_, v)) |
4218 | | { |
4219 | | error = Error::Parse; |
4220 | | } |
4221 | | #else |
4222 | | reader(name, value_, v); |
4223 | | #endif |
4224 | | values.insert(std::end(values), v); |
4225 | | matched = true; |
4226 | | } |
4227 | | |
4228 | | virtual std::string Name() const override |
4229 | | { |
4230 | | return name + std::string("..."); |
4231 | | } |
4232 | | |
4233 | | /** Get the values |
4234 | | */ |
4235 | | Container &Get() noexcept |
4236 | | { |
4237 | | return values; |
4238 | | } |
4239 | | |
4240 | | /** Get the value |
4241 | | */ |
4242 | | Container &operator *() noexcept |
4243 | | { |
4244 | | return values; |
4245 | | } |
4246 | | |
4247 | | /** Get the values |
4248 | | */ |
4249 | | const Container &operator *() const noexcept |
4250 | | { |
4251 | | return values; |
4252 | | } |
4253 | | |
4254 | | /** Get the values |
4255 | | */ |
4256 | | Container *operator ->() noexcept |
4257 | | { |
4258 | | return &values; |
4259 | | } |
4260 | | |
4261 | | /** Get the values |
4262 | | */ |
4263 | | const Container *operator ->() const noexcept |
4264 | | { |
4265 | | return &values; |
4266 | | } |
4267 | | |
4268 | | virtual void Reset() noexcept override |
4269 | | { |
4270 | | PositionalBase::Reset(); |
4271 | | values = defaultValues; |
4272 | | } |
4273 | | |
4274 | | virtual PositionalBase *GetNextPositional() override |
4275 | | { |
4276 | | const bool wasMatched = Matched(); |
4277 | | auto me = PositionalBase::GetNextPositional(); |
4278 | | if (me && !wasMatched) |
4279 | | { |
4280 | | values.clear(); |
4281 | | } |
4282 | | return me; |
4283 | | } |
4284 | | |
4285 | | iterator begin() noexcept |
4286 | | { |
4287 | | return values.begin(); |
4288 | | } |
4289 | | |
4290 | | const_iterator begin() const noexcept |
4291 | | { |
4292 | | return values.begin(); |
4293 | | } |
4294 | | |
4295 | | const_iterator cbegin() const noexcept |
4296 | | { |
4297 | | return values.cbegin(); |
4298 | | } |
4299 | | |
4300 | | iterator end() noexcept |
4301 | | { |
4302 | | return values.end(); |
4303 | | } |
4304 | | |
4305 | | const_iterator end() const noexcept |
4306 | | { |
4307 | | return values.end(); |
4308 | | } |
4309 | | |
4310 | | const_iterator cend() const noexcept |
4311 | | { |
4312 | | return values.cend(); |
4313 | | } |
4314 | | }; |
4315 | | |
4316 | | /** A positional argument mapping class |
4317 | | * |
4318 | | * \tparam K the type to extract the argument as |
4319 | | * \tparam T the type to store the result as |
4320 | | * \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) |
4321 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
4322 | | */ |
4323 | | template < |
4324 | | typename K, |
4325 | | typename T, |
4326 | | typename Reader = ValueReader, |
4327 | | template <typename...> class Map = detail::unordered_map> |
4328 | | class MapPositional : public PositionalBase |
4329 | | { |
4330 | | private: |
4331 | | const Map<K, T> map; |
4332 | | T value; |
4333 | | const T defaultValue; |
4334 | | Reader reader; |
4335 | | |
4336 | | protected: |
4337 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
4338 | | { |
4339 | | return detail::MapKeysToStrings(map); |
4340 | | } |
4341 | | |
4342 | | public: |
4343 | | |
4344 | | MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}): |
4345 | | PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_) |
4346 | | { |
4347 | | group_.Add(*this); |
4348 | | } |
4349 | | |
4350 | | virtual ~MapPositional() {} |
4351 | | |
4352 | | virtual void ParseValue(const std::string &value_) override |
4353 | | { |
4354 | | K key; |
4355 | | #ifdef ARGS_NOEXCEPT |
4356 | | if (!reader(name, value_, key)) |
4357 | | { |
4358 | | error = Error::Parse; |
4359 | | } |
4360 | | #else |
4361 | | reader(name, value_, key); |
4362 | | #endif |
4363 | | auto it = map.find(key); |
4364 | | if (it == std::end(map)) |
4365 | | { |
4366 | | std::ostringstream problem; |
4367 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
4368 | | #ifdef ARGS_NOEXCEPT |
4369 | | error = Error::Map; |
4370 | | errorMsg = problem.str(); |
4371 | | #else |
4372 | | throw MapError(problem.str()); |
4373 | | #endif |
4374 | | } else |
4375 | | { |
4376 | | this->value = it->second; |
4377 | | ready = false; |
4378 | | matched = true; |
4379 | | } |
4380 | | } |
4381 | | |
4382 | | /** Get the value |
4383 | | */ |
4384 | | T &Get() noexcept |
4385 | | { |
4386 | | return value; |
4387 | | } |
4388 | | |
4389 | | /** Get the value |
4390 | | */ |
4391 | | T &operator *() noexcept |
4392 | | { |
4393 | | return value; |
4394 | | } |
4395 | | |
4396 | | /** Get the value |
4397 | | */ |
4398 | | const T &operator *() const noexcept |
4399 | | { |
4400 | | return value; |
4401 | | } |
4402 | | |
4403 | | /** Get the value |
4404 | | */ |
4405 | | T *operator ->() noexcept |
4406 | | { |
4407 | | return &value; |
4408 | | } |
4409 | | |
4410 | | /** Get the value |
4411 | | */ |
4412 | | const T *operator ->() const noexcept |
4413 | | { |
4414 | | return &value; |
4415 | | } |
4416 | | |
4417 | | virtual void Reset() noexcept override |
4418 | | { |
4419 | | PositionalBase::Reset(); |
4420 | | value = defaultValue; |
4421 | | } |
4422 | | }; |
4423 | | |
4424 | | /** A positional argument mapping list class |
4425 | | * |
4426 | | * \tparam K the type to extract the argument as |
4427 | | * \tparam T the type to store the result as |
4428 | | * \tparam List the list type that houses the values |
4429 | | * \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) |
4430 | | * \tparam Map The Map type. Should operate like std::map or std::unordered_map |
4431 | | */ |
4432 | | template < |
4433 | | typename K, |
4434 | | typename T, |
4435 | | template <typename...> class List = detail::vector, |
4436 | | typename Reader = ValueReader, |
4437 | | template <typename...> class Map = detail::unordered_map> |
4438 | | class MapPositionalList : public PositionalBase |
4439 | | { |
4440 | | private: |
4441 | | using Container = List<T>; |
4442 | | |
4443 | | const Map<K, T> map; |
4444 | | Container values; |
4445 | | const Container defaultValues; |
4446 | | Reader reader; |
4447 | | |
4448 | | protected: |
4449 | | virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override |
4450 | | { |
4451 | | return detail::MapKeysToStrings(map); |
4452 | | } |
4453 | | |
4454 | | public: |
4455 | | typedef T value_type; |
4456 | | typedef typename Container::allocator_type allocator_type; |
4457 | | typedef typename Container::pointer pointer; |
4458 | | typedef typename Container::const_pointer const_pointer; |
4459 | | typedef T& reference; |
4460 | | typedef const T& const_reference; |
4461 | | typedef typename Container::size_type size_type; |
4462 | | typedef typename Container::difference_type difference_type; |
4463 | | typedef typename Container::iterator iterator; |
4464 | | typedef typename Container::const_iterator const_iterator; |
4465 | | typedef std::reverse_iterator<iterator> reverse_iterator; |
4466 | | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
4467 | | |
4468 | | MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}): |
4469 | | PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_) |
4470 | | { |
4471 | | group_.Add(*this); |
4472 | | } |
4473 | | |
4474 | | virtual ~MapPositionalList() {} |
4475 | | |
4476 | | virtual void ParseValue(const std::string &value_) override |
4477 | | { |
4478 | | K key; |
4479 | | #ifdef ARGS_NOEXCEPT |
4480 | | if (!reader(name, value_, key)) |
4481 | | { |
4482 | | error = Error::Parse; |
4483 | | } |
4484 | | #else |
4485 | | reader(name, value_, key); |
4486 | | #endif |
4487 | | auto it = map.find(key); |
4488 | | if (it == std::end(map)) |
4489 | | { |
4490 | | std::ostringstream problem; |
4491 | | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; |
4492 | | #ifdef ARGS_NOEXCEPT |
4493 | | error = Error::Map; |
4494 | | errorMsg = problem.str(); |
4495 | | #else |
4496 | | throw MapError(problem.str()); |
4497 | | #endif |
4498 | | } else |
4499 | | { |
4500 | | this->values.emplace_back(it->second); |
4501 | | matched = true; |
4502 | | } |
4503 | | } |
4504 | | |
4505 | | /** Get the value |
4506 | | */ |
4507 | | Container &Get() noexcept |
4508 | | { |
4509 | | return values; |
4510 | | } |
4511 | | |
4512 | | /** Get the value |
4513 | | */ |
4514 | | Container &operator *() noexcept |
4515 | | { |
4516 | | return values; |
4517 | | } |
4518 | | |
4519 | | /** Get the values |
4520 | | */ |
4521 | | const Container &operator *() const noexcept |
4522 | | { |
4523 | | return values; |
4524 | | } |
4525 | | |
4526 | | /** Get the values |
4527 | | */ |
4528 | | Container *operator ->() noexcept |
4529 | | { |
4530 | | return &values; |
4531 | | } |
4532 | | |
4533 | | /** Get the values |
4534 | | */ |
4535 | | const Container *operator ->() const noexcept |
4536 | | { |
4537 | | return &values; |
4538 | | } |
4539 | | |
4540 | | virtual std::string Name() const override |
4541 | | { |
4542 | | return name + std::string("..."); |
4543 | | } |
4544 | | |
4545 | | virtual void Reset() noexcept override |
4546 | | { |
4547 | | PositionalBase::Reset(); |
4548 | | values = defaultValues; |
4549 | | } |
4550 | | |
4551 | | virtual PositionalBase *GetNextPositional() override |
4552 | | { |
4553 | | const bool wasMatched = Matched(); |
4554 | | auto me = PositionalBase::GetNextPositional(); |
4555 | | if (me && !wasMatched) |
4556 | | { |
4557 | | values.clear(); |
4558 | | } |
4559 | | return me; |
4560 | | } |
4561 | | |
4562 | | iterator begin() noexcept |
4563 | | { |
4564 | | return values.begin(); |
4565 | | } |
4566 | | |
4567 | | const_iterator begin() const noexcept |
4568 | | { |
4569 | | return values.begin(); |
4570 | | } |
4571 | | |
4572 | | const_iterator cbegin() const noexcept |
4573 | | { |
4574 | | return values.cbegin(); |
4575 | | } |
4576 | | |
4577 | | iterator end() noexcept |
4578 | | { |
4579 | | return values.end(); |
4580 | | } |
4581 | | |
4582 | | const_iterator end() const noexcept |
4583 | | { |
4584 | | return values.end(); |
4585 | | } |
4586 | | |
4587 | | const_iterator cend() const noexcept |
4588 | | { |
4589 | | return values.cend(); |
4590 | | } |
4591 | | }; |
4592 | | } |
4593 | | |
4594 | | #endif |