Commit 3c57be7adf373f730e0dffe0f0f4272ddc94fa0c

Authored by Henry Fredrick Schreiner
2 parents fd0ca5aa 1c1a6222

Merge branch 'master' into basic-enum

CHANGELOG.md
1 1 ## Version 1.1 (in progress)
2 2 * Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12)
3 3 * Added `app.parse_order()` with original parse order
  4 +* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns.
4 5  
5 6 ## Version 1.0
6 7 * Cleanup using `clang-tidy` and `clang-format`
... ...
README.md
... ... @@ -47,7 +47,7 @@ After I wrote this, I also found the following libraries:
47 47 |---------|-------------------|
48 48 | [GFlags] | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. |
49 49 | [GetOpt] | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors). |
50   -| [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. |
  50 +| [ProgramOptions.hxx] | Intresting library, less powerful and no subcommands. Nice callback system. |
51 51 | [Args] | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose. |
52 52 | [Argument Aggregator] | I'm a big fan of the [fmt] library, and the try-catch statement looks familiar. :thumbsup: Doesn't seem to support subcommands. |
53 53  
... ... @@ -90,7 +90,7 @@ To set up, add options, and run, your main function will look something like thi
90 90 CLI::App app{"App description"};
91 91  
92 92 std::string filename = "default";
93   -app.add_option("-f,--file", file, "A help string");
  93 +app.add_option("-f,--file", filename, "A help string");
94 94  
95 95 try {
96 96 app.parse(argc, argv);
... ... @@ -194,6 +194,7 @@ There are several options that are supported on the main app and subcommands. Th
194 194 * `.parsed()`: True if this subcommand was given on the command line
195 195 * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
196 196 * `.allow_extras()`: Do not throw an error if extra arguments are left over (Only useful on the main `App`, as that's the one that throws errors).
  197 +* `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognised item. It is ideal for allowing your app to be a "prefix" to calling another app.
197 198  
198 199 ## Configuration file
199 200  
... ...
examples/CMakeLists.txt
... ... @@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp)
18 18 add_cli_exe(subcommands subcommands.cpp)
19 19 add_cli_exe(groups groups.cpp)
20 20 add_cli_exe(inter_argument_order inter_argument_order.cpp)
  21 +add_cli_exe(prefix_command prefix_command.cpp)
... ...
examples/inter_argument_order.cpp
... ... @@ -2,6 +2,7 @@
2 2 #include <iostream>
3 3 #include <vector>
4 4 #include <tuple>
  5 +#include <algorithm>
5 6  
6 7 int main(int argc, char **argv) {
7 8 CLI::App app;
... ... @@ -25,7 +26,7 @@ int main(int argc, char **argv) {
25 26 std::reverse(std::begin(foos), std::end(foos));
26 27 std::reverse(std::begin(bars), std::end(bars));
27 28  
28   - std::vector<std::tuple<std::string, int>> keyval;
  29 + std::vector<std::pair<std::string, int>> keyval;
29 30 for(auto option : app.parse_order()) {
30 31 if(option == foo) {
31 32 keyval.emplace_back("foo", foos.back());
... ... @@ -38,11 +39,7 @@ int main(int argc, char **argv) {
38 39 }
39 40  
40 41 // Prove the vector is correct
41   - std::string name;
42   - int value;
43   -
44   - for(auto &tuple : keyval) {
45   - std::tie(name, value) = tuple;
46   - std::cout << name << " : " << value << std::endl;
  42 + for(auto &pair : keyval) {
  43 + std::cout << pair.first << " : " << pair.second << std::endl;
47 44 }
48 45 }
... ...
examples/prefix_command.cpp 0 → 100644
  1 +#include "CLI/CLI.hpp"
  2 +
  3 +int main(int argc, char **argv) {
  4 +
  5 + CLI::App app("Prefix command app");
  6 + app.prefix_command();
  7 +
  8 + std::vector<int> vals;
  9 + app.add_option("--vals,-v", vals)
  10 + ->expected(1);
  11 +
  12 + std::vector<std::string> more_comms;
  13 + try {
  14 + more_comms = app.parse(argc, argv);
  15 + } catch(const CLI::ParseError &e) {
  16 + return app.exit(e);
  17 + }
  18 +
  19 + std::cout << "Prefix:";
  20 + for(int v : vals)
  21 + std::cout << v << ":";
  22 +
  23 + std::cout << std::endl << "Remaining commands: ";
  24 +
  25 + // Perfer to loop over from beginning, not "pop" order
  26 + std::reverse(std::begin(more_comms), std::end(more_comms));
  27 + for(auto com : more_comms)
  28 + std::cout << com << " ";
  29 + std::cout << std::endl;
  30 +
  31 + return 0;
  32 +}
... ...
include/CLI/App.hpp
... ... @@ -57,6 +57,9 @@ class App {
57 57 /// If true, allow extra arguments (ie, don't throw an error).
58 58 bool allow_extras_{false};
59 59  
  60 + /// If true, return immediatly on an unrecognised option (implies allow_extras)
  61 + bool prefix_command_{false};
  62 +
60 63 /// This is a function that runs when complete. Great for subcommands. Can throw.
61 64 std::function<void()> callback_;
62 65  
... ... @@ -152,6 +155,12 @@ class App {
152 155 return this;
153 156 }
154 157  
  158 + /// Do not parse anything after the first unrecongnised option and return
  159 + App *prefix_command(bool allow = true) {
  160 + prefix_command_ = allow;
  161 + return this;
  162 + }
  163 +
155 164 /// Ignore case. Subcommand inherit value.
156 165 App *ignore_case(bool value = true) {
157 166 ignore_case_ = value;
... ... @@ -949,7 +958,7 @@ class App {
949 958 return val.first != detail::Classifer::POSITIONAL_MARK;
950 959 });
951 960  
952   - if(num_left_over > 0 && !allow_extras_)
  961 + if(num_left_over > 0 && !(allow_extras_ || prefix_command_))
953 962 throw ExtrasError("[" + detail::rjoin(args, " ") + "]");
954 963 }
955 964 }
... ... @@ -1060,7 +1069,15 @@ class App {
1060 1069 else {
1061 1070 args.pop_back();
1062 1071 missing()->emplace_back(detail::Classifer::NONE, positional);
  1072 +
  1073 + if(prefix_command_) {
  1074 + while(!args.empty()) {
  1075 + missing()->emplace_back(detail::Classifer::NONE, args.back());
  1076 + args.pop_back();
  1077 + }
  1078 + }
1063 1079 }
  1080 +
1064 1081 }
1065 1082  
1066 1083 /// Parse a subcommand, modify args and continue
... ...
include/CLI/Option.hpp
... ... @@ -433,6 +433,10 @@ class Option {
433 433 /// Set the default value string representation
434 434 void set_default_val(std::string val) { defaultval_ = val; }
435 435  
  436 +
  437 + /// Set the type name displayed on this option
  438 + void set_type_name(std::string val) {typeval_ = val;}
  439 +
436 440 ///@}
437 441  
438 442 protected:
... ...
tests/HelpTest.cpp
... ... @@ -190,6 +190,22 @@ TEST(THelp, ExcludesPositional) {
190 190 EXPECT_THAT(help, HasSubstr("Excludes: op1"));
191 191 }
192 192  
  193 +TEST(THelp, ManualSetters) {
  194 +
  195 + CLI::App app{"My prog"};
  196 +
  197 + int x;
  198 +
  199 + CLI::Option *op1 = app.add_option("--op", x);
  200 + op1->set_default_val("12");
  201 + op1->set_type_name("BIGGLES");
  202 +
  203 + std::string help = app.help();
  204 +
  205 + EXPECT_THAT(help, HasSubstr("=12"));
  206 + EXPECT_THAT(help, HasSubstr("BIGGLES"));
  207 +}
  208 +
193 209 TEST(THelp, Subcom) {
194 210 CLI::App app{"My prog"};
195 211  
... ...
tests/SubcommandTest.cpp
... ... @@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) {
231 231 EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound);
232 232 }
233 233  
  234 +TEST_F(TApp, PrefixProgram) {
  235 +
  236 + app.prefix_command();
  237 +
  238 + app.add_flag("--simple");
  239 +
  240 + args = {"--simple", "other", "--simple", "--mine"};
  241 + auto ret_args = run();
  242 +
  243 + EXPECT_EQ(ret_args, std::vector<std::string>({"--mine", "--simple", "other"}));
  244 +
  245 +}
  246 +
234 247 struct SubcommandProgram : public TApp {
235 248  
236 249 CLI::App *start;
... ...