-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcommandline.h
More file actions
1753 lines (1619 loc) · 74.5 KB
/
Copy pathcommandline.h
File metadata and controls
1753 lines (1619 loc) · 74.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: MIT
/// \file commandline.h
///
/// Declaring what a program takes on its command line, and reading back what it was given.
///
/// The shape of the API comes from SysCmdLine, https://github.com/SineStriker/syscmdline, which
/// this replaces. Everything it declares is in the \ref cli module, which carries the prose.
#ifndef STDCORELIB_COMMANDLINE_H
#define STDCORELIB_COMMANDLINE_H
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <stdcorelib/stdc_global.h>
#include <stdcorelib/console.h>
#include <stdcorelib/str.h>
#include <stdcorelib/flags.h>
#include <stdcorelib/adt/array_view.h>
/// \defgroup cli Command line
///
/// Declaring what a program takes on its command line, and reading back what it was given. Replaces
/// SysCmdLine, https://github.com/SineStriker/syscmdline.
///
/// A tree of stdc::cli::Command, each carrying its stdc::cli::Argument and stdc::cli::Option, is
/// handed to a stdc::cli::Parser. What comes back is a stdc::cli::ParseResult, and everything read
/// out of one answers with \c std::optional, so a value that is not there and a value that is empty
/// are different answers.
///
/// \code
/// using namespace stdc;
///
/// cli::Parser parser(cli::Command("prog", "What it is for")
/// .addArgument(cli::Argument("path", "Where to work"))
/// .addOption(cli::Option({"-j", "--jobs"}, "How many at once")
/// .arg(cli::Argument("n").type<int>()))
/// .addHelpOption(true)
/// .addVersionOption("1.0.0"));
///
/// return parser.invoke(system::command_line_arguments());
/// \endcode
///
/// \c invoke() reports a parse that failed, answers \c --help and a \c --version with text to
/// print, and otherwise runs the handler of the command that was reached. Its return value is
/// what \c main returns.
///
/// \section cli_shape What a command line looks like
///
/// \verbatim
/// prog remote add --force file.txt -j 4
/// \________/ \_____________________/
/// the path everything it takes, in any order among themselves
/// \endverbatim
///
/// The names come first and nothing goes between them. A command line names the command it
/// wants by naming each one down to it, and the first token that is not one of those names ends
/// the path and settles what was reached. Everything after belongs to that command: its own
/// options, the ones marked Option::recursive() by the commands above it, and its arguments,
/// with no order among them beyond what a greedy argument forces.
///
/// So \c prog \c --plain \c sub is an error rather than a way to reach \c sub, even where the
/// root declares \c --plain. This is SysCmdLine's rule and it is not git's, whose root options
/// go before the subcommand name. Option::recursive() is how an option of one command is
/// written on a line that reached another.
///
/// \subsection cli_shape_lines What parses and what does not
///
/// \code
/// cli::Command("prog")
/// .addOption(cli::Option({"--plain"}, "The root's own"))
/// .addOption(cli::Option({"--wide"}, "The root's, recursive").recursive())
/// .addCommand(cli::Command("sub")
/// .addArgument(cli::Argument("path"))
/// .addOption(cli::Option({"-f"}, "The subcommand's")));
/// \endcode
///
/// \verbatim
/// prog --plain the root was reached, and --plain is the root's own
/// prog sub -f a sub was reached, -f is its own, a is its argument
/// prog sub --wide -f a --wide came down from the root, being recursive
/// prog sub a -f --wide no order among options and arguments once the path is over
///
/// prog --plain sub no: an option ends the path, so sub is written too late
/// prog --wide sub no: the same. Recursive says where an option may be
/// written, not that the path stops coming first
/// prog sub --plain no: --plain is the root's and was not marked recursive
/// prog -f sub no: -f is sub's, and the root is what --f was written after
/// \endverbatim
///
/// \subsection cli_shape_greedy Arguments that take more than one
///
/// Argument::Multiple leaves a token for each required argument after it, so
/// \c copy \c \<src\>... \c \<dest\> works wherever arguments are declared. Argument::Remainder
/// leaves nothing and stops option reading where it starts, which is how a program says what its
/// own terminator is spelled. Nothing is reserved for \c -- : a program that wants the usual word
/// declares it.
///
/// \code
/// cli::Command("copy")
/// .addArgument(cli::Argument("src").multi())
/// .addArgument(cli::Argument("dest"))
/// .addOption(cli::Option({"-f"}, "Read the names from")
/// .arg(cli::Argument("list").multi()))
/// .addOption(cli::Option({"--"}, "The rest")
/// .arg(cli::Argument("rest").nargs(cli::Argument::Remainder).optional()));
/// \endcode
///
/// \verbatim
/// copy a b c src took a and b, dest the one reserved for it
/// copy a b c -f x y the arguments first, then -f to the end of the line
/// copy a b c -f x -- y -f's run ended at --, that being a declared option
/// copy a b c -- -f x -- reads no options, so -f is one of its values
///
/// copy -f x y a b c no: -f took all five and left <src> and <dest> nothing
/// \endverbatim
///
/// A greedy run ends at the next declared option, so a command that has arguments of its own and
/// an option that is greedy wants the arguments written first, or the option takes them.
///
/// A Remainder starts where the argument before it was filled. Where it is a command's first
/// argument there is none, so it starts at the first token not written as an option, which is
/// what lets a wrapper take options of its own and hand the rest on:
///
/// \code
/// cli::Command("run")
/// .addArgument(cli::Argument("rest").nargs(cli::Argument::Remainder).optional())
/// .addOption(cli::Option({"-u"}, "As user").arg("who"));
/// \endcode
///
/// \verbatim
/// run -u root ls -u x -u root is run's, ls -u x is the tail's, one spelling twice
/// run ls -u x nothing of run's was written, so all of it is the tail
///
/// run -w ls no: written as an option and not declared, so not passed on
/// \endverbatim
///
/// A value may be written against its option, \c --opt=v or \c -Ov, and it is the first value
/// of the option's first argument rather than the whole of it, so \c --opt=a \c b reads as
/// \c --opt \c a \c b wherever that argument takes more than one. The joined spelling
/// Option::shortMatch() allows is the exception: one token carries one value, so it is offered
/// only where the option's one argument takes exactly one.
///
/// \subsection cli_shape_trees What a tree should not be
///
/// These are mistakes in the program rather than in what a user typed. parse() asserts that the
/// whole tree is valid in a debug build. A program that builds one dynamically may call
/// Parser::validate() and report the reason itself.
///
/// An argument should not
/// \li have no name, or share a name with another argument beside it, a command's own and an
/// option's being two lists
/// \li be required where one before it may be left out, since one token could be meant for
/// either
/// \li follow an Argument::Remainder, which leaves nothing to follow it with
/// \li follow an Argument::Multiple unless it is a required Argument::Single, that being the one
/// thing the reservation rule leaves room for
/// \li expect() a value its own type() cannot read
/// \li carry a defaultValue() that its type(), its expect() or its validate() turns down, or
/// carry one at all while being required, a default being what stands in where nothing was
/// given
///
/// An option should not
/// \li have no spelling, or one shorter than two characters, or one starting with neither \c -
/// nor \c /
/// \li repeat one of its own spellings
/// \li share a spelling with another option of the same command
/// \li be required or take arguments where its prior() is Option::AutoSetWhenNoSymbols, since
/// nobody writes one of those and there is nothing to give it
/// \li be told by multi() that it may be given a negative number of times
///
/// A command should not
/// \li hold a subcommand with no name, or two subcommands sharing one
/// \li hold an Argument::Remainder and an option whose argument is greedy, both wanting the rest
/// of the line where only one of them can be written first
///
/// A CommandCatalogue should not name something its command does not contain, or name one thing
/// twice, in one group or across two of the same kind.
///
/// And a tree should not, which is only visible once it is one
/// \li have two options in scope at one command answering to one spelling, the recursive ones of
/// every command above it included
/// \li have two names in one scope that differ only in case, where the parse ignores case
/// \li have two spellings a value may be stuck to where one is the start of the other, \c -D and
/// \c -Da against \c -Dabc, which no rule can settle
///
/// \section cli_help Changing the help text
///
/// Five rungs, and a program climbs only as far as it needs to. Each one is written in terms of
/// the one below it, so nothing is reimplemented to change one thing.
///
/// \verbatim
/// 1 how much room it has parser.setIndent(2)
/// parser.setSpacing(1)
/// parser.setTextWidth(100)
///
/// 2 how it is printed layout.setTitleStyle({console::bold})
/// layout.setBodyStyle(HelpBlock::Epilogue, {...})
///
/// 3 which blocks, in what order HelpLayout().add(HelpBlock::Usage)
/// .add(HelpBlock::Options)
/// .add(myOwnBlock)
///
/// 4 how a block is made or laid struct Mine : HelpFormatter {
/// out, one rung at a time std::vector<HelpBlock> blocks(...) const override {
/// auto res = HelpFormatter::blocks(...);
/// ...
/// }
/// };
///
/// 5 none of the above for (auto &block : result.helpBlocks()) { ... }
/// \endverbatim
///
/// Rungs 1 to 3 are settings on the Parser and need no type of your own. Rung 4 is
/// HelpFormatter, which is a ladder of its own and carries the diagram of it. Rung 5 hands the
/// blocks over and gets out of the way.
namespace stdc::cli {
/// \addtogroup cli
/// @{
/// How a token is turned into a \c T, and what to call \c T in the help text.
///
/// A command line is text, so everything here is stored as text and converted when it is
/// read. Specialize this to accept a type of your own:
///
/// \code
/// template <>
/// struct stdc::cli::value_traits<fs::path> {
/// static bool parse(std::string_view token, fs::path *out) {
/// *out = token;
/// return true;
/// }
/// static const char *type_name() {
/// return "path";
/// }
/// };
/// \endcode
///
/// \c parse returns false for a token the type cannot represent, which is what turns
/// \c --count=x into a diagnostic rather than a zero.
template <class T, class Enable = void>
struct value_traits;
namespace detail {
/// A type's check and its name, as function pointers, so that Argument can hold a
/// type without being a template.
struct value_type_info {
/// Whether the token is a \c T. Null means anything goes, which is the default.
bool (*check)(std::string_view) = nullptr;
/// The name used in diagnostics and in the help text. Must be a literal, since
/// it is held rather than copied.
const char *name = nullptr;
};
template <class T>
bool check_value(std::string_view token) {
T out{};
return value_traits<T>::parse(token, &out);
}
template <class T>
value_type_info type_info_for() {
return {&check_value<T>, value_traits<T>::type_name()};
}
/// What a ParseResult holds.
class parse_data;
STDC_EXPORT bool parse_signed(std::string_view token, int64_t *out, int64_t min,
int64_t max);
STDC_EXPORT bool parse_unsigned(std::string_view token, uint64_t *out, uint64_t max);
STDC_EXPORT bool parse_floating(std::string_view token, float *out);
STDC_EXPORT bool parse_floating(std::string_view token, double *out);
STDC_EXPORT bool parse_floating(std::string_view token, long double *out);
STDC_EXPORT bool parse_boolean(std::string_view token, bool *out);
}
/// Text, which is what a command line already is.
template <>
struct value_traits<std::string> {
static inline bool parse(std::string_view token, std::string *out) {
out->assign(token);
return true;
}
static inline const char *type_name() {
return "string";
}
};
/// A view into the result's own storage, which outlives the read.
template <>
struct value_traits<std::string_view> {
static inline bool parse(std::string_view token, std::string_view *out) {
*out = token;
return true;
}
static inline const char *type_name() {
return "string";
}
};
/// \c true, \c false, \c yes, \c no, \c on, \c off, \c 1 and \c 0, in any case.
template <>
struct value_traits<bool> {
static inline bool parse(std::string_view token, bool *out) {
return detail::parse_boolean(token, out);
}
static inline const char *type_name() {
return "bool";
}
};
/// Every integer type except \c bool, which has its own above. The range of the target
/// type is part of the check, so \c 300 is not a \c uint8_t.
template <class T>
struct value_traits<T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>>> {
static inline bool parse(std::string_view token, T *out) {
if constexpr (std::is_signed_v<T>) {
int64_t v;
if (!detail::parse_signed(token, &v, int64_t(std::numeric_limits<T>::min()),
int64_t(std::numeric_limits<T>::max()))) {
return false;
}
*out = T(v);
} else {
uint64_t v;
if (!detail::parse_unsigned(token, &v, uint64_t(std::numeric_limits<T>::max()))) {
return false;
}
*out = T(v);
}
return true;
}
static inline const char *type_name() {
return std::is_signed_v<T> ? "int" : "uint";
}
};
/// \c float, \c double and \c long double. Uses the matching function from the \c strto*
/// family rather than \c from_chars, which libc++ did not implement for floating point for
/// a long time.
template <class T>
struct value_traits<T, std::enable_if_t<std::is_floating_point_v<T>>> {
static inline bool parse(std::string_view token, T *out) {
return detail::parse_floating(token, out);
}
static inline const char *type_name() {
return "number";
}
};
class ParseResult;
class HelpFormatter;
/// One positional value a command or an option takes.
class Argument {
public:
/// How many tokens it takes.
enum Arity {
/// Exactly one.
Single,
/// One or more, also called greedy. Leaves enough tokens for the required arguments
/// after it, so \c copy \c \<src\>... \c \<dest\> works.
Multiple,
/// Everything left, options included, which is why nothing may follow one. Where it
/// starts is where option reading stops, so this is how a program spells its own
/// terminator.
///
/// Where it starts is where the argument before it was filled. Where it is the
/// first argument there is none, so it starts at the first token not written as
/// an option, which is what lets a wrapper take options of its own:
/// \c run \c -u \c root \c ls \c -l gives \c -u to \c run and \c -l to \c ls.
///
/// \note Required like any other argument, so an empty tail wants optional().
Remainder,
};
/// Answers whether \a token is acceptable, and says why in \a error when it is not.
///
/// It may be called repeatedly with the same token, including while the command tree is
/// validated. It should have no observable side effects and should give the same answer
/// without depending on how many times it has been called or on mutable external state.
using Validator = std::function<bool(std::string_view token, std::string *error)>;
Argument() = default;
inline Argument(std::string name, std::string desc = {}, bool required = true)
: _name(std::move(name)), _desc(std::move(desc)), _required(required) {
}
/// The name to show in the help text, when it should differ from name().
inline Argument &metavar(std::string displayName) {
_displayName = std::move(displayName);
return *this;
}
inline Argument &required(bool on = true) {
_required = on;
return *this;
}
inline Argument &optional(bool on = true) {
_required = !on;
return *this;
}
/// The value the result gives when the argument was not given. Stored as text and
/// converted when read.
///
/// \pre It is readable as whatever type<T>() declared, and is one of the values
/// expect() allows, if either was given.
inline Argument &defaultValue(std::string value) {
_default = std::move(value);
_hasDefault = true;
return *this;
}
/// The only values this accepts, for an argument that is a choice between a few
/// words.
///
/// \pre Every one of them is readable as whatever type<T>() declared.
inline Argument &expect(std::vector<std::string> values) {
_expected = std::move(values);
return *this;
}
/// What this argument accepts beyond being readable as its type.
///
/// \pre It accepts defaultValue(), where there is one. Only what a command line wrote is
/// put past it while parsing, so a default it refuses would be handed back without
/// ever being asked about.
inline Argument &validate(Validator validator) {
_validator = std::move(validator);
return *this;
}
inline Argument &nargs(Arity arity) {
_arity = arity;
return *this;
}
inline Argument &multi(bool on = true) {
_arity = on ? Multiple : Single;
return *this;
}
/// Declares the type. Tokens are checked against it while parsing, and its name
/// appears in the help text. Without it any token is accepted.
///
/// \pre Whatever expect() was given, if anything, is readable as a \c T.
template <class T>
inline Argument &type() {
_type = detail::type_info_for<T>();
return *this;
}
inline const std::string &name() const {
return _name;
}
inline const std::string &description() const {
return _desc;
}
/// The metavar if one was given, and the name otherwise.
inline const std::string &displayName() const {
return _displayName.empty() ? _name : _displayName;
}
inline bool isRequired() const {
return _required;
}
inline bool hasDefaultValue() const {
return _hasDefault;
}
inline const std::string &defaultValue() const {
return _default;
}
inline const std::vector<std::string> &expectedValues() const {
return _expected;
}
inline const Validator &validator() const {
return _validator;
}
inline Arity arity() const {
return _arity;
}
inline const detail::value_type_info &typeInfo() const {
return _type;
}
private:
std::string _name;
std::string _desc;
std::string _displayName;
std::string _default;
std::vector<std::string> _expected;
Validator _validator;
detail::value_type_info _type;
Arity _arity = Single;
bool _required = true;
bool _hasDefault = false;
};
/// A named switch, with any number of arguments of its own.
class Option {
public:
/// What the option means, for the two the library answers by itself. A role brings the
/// usual spellings and description, and lets a caller ask by role rather than by
/// spelling.
///
/// The set is closed, and it is these two because these two are what the library does
/// something with. A role for a switch it only carries the spelling of would be a
/// second way of writing Option({"-V", "--verbose"}, "Print more information").
///
/// \note A role says nothing about scope, which is recursive(), nor about where an option
/// sits in the help text, which is where it was declared.
enum Role {
NoRole,
Version,
Help,
};
/// How much of a short token the parser may take for this option, so that \c -O2 or
/// \c -DKEY=VALUE can be one token rather than two.
enum ShortMatch {
/// \c -D and its value are separate tokens.
NoShortMatch,
/// A single letter may be followed by the value, as in \c -O2.
ShortMatchSingleLetter,
/// A single character, letter or not.
ShortMatchSingleChar,
/// The whole token after the option's own, as in \c -DKEY=VALUE.
ShortMatchAll,
};
/// The highest level among the options given decides. This is what lets \c --help be
/// answered on a command line that is missing everything it requires.
///
/// ###QUESTION: Should automatic activation and exclusivity be split from missing-value
/// priority? This enum prevents combining those policies and orders unrelated values.
enum Prior {
NoPrior,
/// Its own missing arguments are not an error.
IgnoreMissingArguments,
/// Nothing missing anywhere is an error.
IgnoreMissingSymbols,
/// Set it when nothing else was given at all.
AutoSetWhenNoSymbols,
/// Giving it means no arguments may be given.
ExclusiveToArguments,
/// Giving it means no other options may be given.
ExclusiveToOptions,
/// Giving it means nothing else may be given.
ExclusiveToAll,
};
Option() = default;
inline Option(std::vector<std::string> tokens, std::string desc = {})
: _tokens(std::move(tokens)), _desc(std::move(desc)) {
}
inline Option(std::initializer_list<std::string> tokens, std::string desc = {})
: Option(std::vector<std::string>(tokens), std::move(desc)) {
}
inline Option(std::string token, std::string desc = {})
: Option(std::vector<std::string>{std::move(token)}, std::move(desc)) {
}
/// Deliberately not explicit, so that \c addOptions({Option::Help}) reads the way it
/// does. Empty tokens take the usual spelling for the role.
inline Option(Role role, std::vector<std::string> tokens = {}, std::string desc = {})
: _tokens(tokens.empty() ? defaultTokens(role) : std::move(tokens)),
_desc(desc.empty() ? defaultDescription(role) : std::move(desc)), _role(role) {
}
/// Adds an argument. An option's argument needs no description of its own.
inline Option &arg(std::string name, bool required = true) {
return arg(Argument(std::move(name), {}, required));
}
inline Option &arg(Argument argument) {
_args.emplace_back(std::move(argument));
return *this;
}
inline Option &required(bool on = true) {
_required = on;
return *this;
}
/// Whether a value may be stuck to the spelling, \c -Dfoo rather than \c -D \c foo.
///
/// \note A permission rather than a promise. One token carries one value, so an option
/// that takes no argument, takes an optional one, takes more than one, or whose
/// one argument is greedy is never matched this way whatever is set here. Written
/// out with a space they all work.
inline Option &shortMatch(ShortMatch rule) {
_shortMatch = rule;
return *this;
}
inline Option &prior(Prior level) {
_prior = level;
return *this;
}
/// In scope for every command below this one as well as for this one.
///
/// Without it an option can be given only where the command declaring it is the one that
/// was reached, since every option is written after its own command.
inline Option &recursive(bool on = true) {
_recursive = on;
return *this;
}
/// How many times the option may be given, zero meaning without limit.
///
/// What repeats is the whole occurrence: every time the option is written it reads its
/// arguments again, into a set of its own that OptionResult::at() hands back. How many
/// values one argument takes within one occurrence is a different question, and
/// Argument::multi() is where it is asked.
/// \pre \a maxOccurrence is not negative. A negative is not a smaller limit, it is one
/// the count can never reach, which reads back as no limit at all.
inline Option &multi(int maxOccurrence = 0) {
_maxOccurrence = maxOccurrence;
return *this;
}
inline const std::vector<std::string> &tokens() const {
return _tokens;
}
/// The first spelling, which is the one the help text and diagnostics use.
inline const std::string &token() const {
assert(!_tokens.empty() && "an option with no spelling has no token");
return _tokens.front();
}
inline const std::string &description() const {
return _desc;
}
inline const std::vector<Argument> &arguments() const {
return _args;
}
inline bool isRequired() const {
return _required;
}
inline bool isRecursive() const {
return _recursive;
}
inline Role role() const {
return _role;
}
inline ShortMatch shortMatch() const {
return _shortMatch;
}
inline Prior prior() const {
return _prior;
}
inline int maxOccurrence() const {
return _maxOccurrence;
}
/// What a role says about itself in the help text when nothing else was given.
static inline std::string defaultDescription(Role role) {
switch (role) {
case Help:
return "Show this help and exit";
case Version:
return "Show the version and exit";
default:
return {};
}
}
/// The spellings a role answers to when none were given.
static inline std::vector<std::string> defaultTokens(Role role) {
switch (role) {
case Help:
return {"-h", "--help"};
case Version:
return {"-v", "--version"};
default:
return {};
}
}
private:
std::vector<std::string> _tokens;
std::string _desc;
std::vector<Argument> _args;
Role _role = NoRole;
ShortMatch _shortMatch = NoShortMatch;
Prior _prior = NoPrior;
int _maxOccurrence = 1;
bool _required = false;
bool _recursive = false;
};
/// Which heading each name is listed under in the help text. Anything not named here goes
/// under the default heading, so a catalogue only has to mention what it wants to move.
class CommandCatalogue {
public:
struct Group {
std::string name;
std::vector<std::string> members;
};
inline CommandCatalogue &addCommands(std::string group, std::vector<std::string> names) {
_commands.push_back({std::move(group), std::move(names)});
return *this;
}
inline CommandCatalogue &addOptions(std::string group, std::vector<std::string> names) {
_options.push_back({std::move(group), std::move(names)});
return *this;
}
inline CommandCatalogue &addArguments(std::string group, std::vector<std::string> names) {
_arguments.push_back({std::move(group), std::move(names)});
return *this;
}
inline const std::vector<Group> &commandGroups() const {
return _commands;
}
inline const std::vector<Group> &optionGroups() const {
return _options;
}
inline const std::vector<Group> &argumentGroups() const {
return _arguments;
}
inline bool isEmpty() const {
return _commands.empty() && _options.empty() && _arguments.empty();
}
private:
std::vector<Group> _commands;
std::vector<Group> _options;
std::vector<Group> _arguments;
};
/// A command, its arguments, its options and whatever subcommands it has.
class Command {
public:
/// What to run once this command is the one that was named. Its return value is the
/// program's.
using Handler = std::function<int(const ParseResult &)>;
Command() = default;
inline Command(std::string name, std::string desc = {})
: _name(std::move(name)), _desc(std::move(desc)) {
}
inline Command &addArgument(Argument argument) {
_args.emplace_back(std::move(argument));
return *this;
}
inline Command &addArguments(std::vector<Argument> arguments) {
for (auto &item : arguments) {
addArgument(std::move(item));
}
return *this;
}
inline Command &addOption(Option option) {
_options.emplace_back(std::move(option));
return *this;
}
inline Command &addOptions(std::vector<Option> options) {
for (auto &item : options) {
addOption(std::move(item));
}
return *this;
}
inline Command &addCommand(Command command) {
_commands.emplace_back(std::move(command));
return *this;
}
inline Command &addCommands(std::vector<Command> commands) {
for (auto &item : commands) {
addCommand(std::move(item));
}
return *this;
}
inline Command &setHandler(Handler handler) {
_handler = std::move(handler);
return *this;
}
inline Command &setCatalogue(CommandCatalogue catalogue) {
_catalogue = std::move(catalogue);
return *this;
}
/// What a Version option prints.
inline Command &setVersion(std::string version) {
_version = std::move(version);
return *this;
}
/// The version option, with the level that lets it be answered on a command line that is
/// otherwise missing everything it needs.
///
/// The switch is this command's and the string cascades, so a subcommand adds its own
/// switch and says a version of its own or does not. An empty \a version is the second
/// of those: the switch answers here with whatever the nearest command above it said.
/// Where the whole path is empty, invoke() leaves the Version role for the handler.
/// \sa ParseResult::versionText()
///
/// \code
/// root.addVersionOption("1.0");
/// root.findCommand("sub")->addVersionOption(""); // prog sub --version prints 1.0
/// \endcode
inline Command &addVersionOption(std::string version, std::vector<std::string> tokens = {},
std::string desc = {}) {
_version = std::move(version);
return addOption(Option(Option::Version, std::move(tokens), std::move(desc))
.prior(Option::IgnoreMissingSymbols));
}
/// The help option, likewise.
///
/// \param showIfNoArguments Answer a command line with nothing on it at all, so that a
/// bare program name prints its help.
/// \param recursive Keep it in scope for the subcommands as well.
/// \param tokens The spellings, or the usual ones when empty.
/// \param desc The description, or the usual one when empty.
inline Command &addHelpOption(bool showIfNoArguments = false, bool recursive = false,
std::vector<std::string> tokens = {}, std::string desc = {}) {
return addOption(Option(Option::Help, std::move(tokens), std::move(desc))
.prior(showIfNoArguments ? Option::AutoSetWhenNoSymbols
: Option::IgnoreMissingSymbols)
.recursive(recursive));
}
inline Command &setDescription(std::string desc) {
_desc = std::move(desc);
return *this;
}
inline const std::string &name() const {
return _name;
}
inline const std::string &description() const {
return _desc;
}
inline const std::string &version() const {
return _version;
}
inline const std::vector<Argument> &arguments() const {
return _args;
}
inline const std::vector<Option> &options() const {
return _options;
}
inline const std::vector<Command> &commands() const {
return _commands;
}
inline const Handler &handler() const {
return _handler;
}
inline const CommandCatalogue &catalogue() const {
return _catalogue;
}
/// The subcommand named \a name, or null. Only one level down.
inline const Command *findCommand(std::string_view name) const {
for (const auto &item : _commands) {
if (item._name == name) {
return &item;
}
}
return nullptr;
}
/// The option answering to \a token, or null. A token is any of an option's spellings,
/// not only the first.
inline const Option *findOption(std::string_view token) const {
for (const auto &item : _options) {
for (const auto &spelling : item.tokens()) {
if (spelling == token) {
return &item;
}
}
}
return nullptr;
}
private:
std::string _name;
std::string _desc;
std::string _version;
std::vector<Argument> _args;
std::vector<Option> _options;
std::vector<Command> _commands;
Handler _handler;
CommandCatalogue _catalogue;
};
/// What one option was given.
///
/// A view onto the ParseResult it came from, the way \c std::string_view is a view onto a
/// string. It owns nothing and keeps nothing alive. One exists only where the option was
/// given, so there is no such thing as an empty one and nothing here has to ask.
///
/// \warning Do not outlive that result. \c parser.parse(args).option("-f") reads freed
/// storage at the semicolon, since the result it was taken from was a temporary.
/// \sa ParseResult::option()
class STDC_EXPORT OptionResult {
public:
/// One appearance of the option, read the way a ParseResult is read.
///
/// A command is given once and an option may be given many times, which is the whole
/// of the difference between them. So this is what a ParseResult is for a command,
/// with the same four questions asked of it in the same four words, and OptionResult
/// is these in a row.
///
/// \warning A view onto a view, so it lasts as long as the ParseResult and not a line
/// longer. One always stands for an occurrence that happened, which is what
/// OptionResult::at()'s precondition is for.
class STDC_EXPORT Occurrence {
public:
/// The \a index'th argument's first value, as text, or the default value where
/// there is one, or nothing when there is neither.
///
/// An option given an empty value, \c --prefix= , has one, and it is the empty
/// string. That is why this answers with an optional rather than with empty text:
/// whether a token is there and whether the token is empty are different questions.
///
/// \warning Points into the ParseResult and lasts exactly as long as it does. Ask
/// value<T>() for something that owns what it holds.
std::optional<std::string_view> rawValue(int index = 0) const;
/// Every value the \a index'th argument took here, which is more than one only
/// where the argument said it accepts more than one.
///
/// \warning The same. These point into the ParseResult.
std::vector<std::string_view> rawValues(int index = 0) const;
/// Converted, or nothing when there is nothing to convert.
template <class T = std::string>
std::optional<T> value(int index = 0) const {
auto raw = rawValue(index);
if (!raw) {
return std::nullopt;
}
T out{};
if (!value_traits<T>::parse(*raw, &out)) {
return std::nullopt;
}
return out;
}
/// Every value the \a index'th argument took here, converted, or nothing when one
/// of them is not a \c T.
template <class T = std::string>
std::optional<std::vector<T>> values(int index = 0) const {
std::vector<T> out;
for (auto raw : rawValues(index)) {
T item{};
if (!value_traits<T>::parse(raw, &item)) {
return std::nullopt;
}
out.push_back(std::move(item));
}
return out;
}
private:
friend class OptionResult;
inline Occurrence(const void *data, int n) : _data(data), _n(n) {
}
const void *_data;
int _n;
};
/// How many times the option was given, which is at least once.
int count() const;
/// The option itself.
const Option *option() const;
/// The \a n'th time it was given, counting from zero.
///
/// \code
/// for (int n = 0; n < given.count(); ++n) {
/// take(given.at(n).values());
/// }
/// \endcode
///
/// \pre \a n is at least zero and less than count(). Anything else is undefined, and
/// is asserted in a debug build. An Occurrence is a view onto one that happened,
/// so there is no such thing as an empty one to hand back and nothing on it to
/// ask, which is the same reason ParseResult::option() answers with an optional
/// and OptionResult does not.
Occurrence at(int n) const;
/// The four below are the first occurrence's four, said shorter. An option given once
/// is what nearly every option is, and there is nothing else it could mean.
///
/// What an option has that a command has not is that it may be given again, and that
/// is at() and the two all-prefixed below. Neither is folded into these: an option
/// read without saying which occurrence reads the first.
inline std::optional<std::string_view> rawValue(int index = 0) const {
return at(0).rawValue(index);
}
inline std::vector<std::string_view> rawValues(int index = 0) const {
return at(0).rawValues(index);