Commit 3c57be7adf373f730e0dffe0f0f4272ddc94fa0c

Authored by Henry Fredrick Schreiner
2 parents fd0ca5aa 1c1a6222

Merge branch 'master' into basic-enum

CHANGELOG.md
1 ## Version 1.1 (in progress) 1 ## Version 1.1 (in progress)
2 * Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12) 2 * Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12)
3 * Added `app.parse_order()` with original parse order 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 ## Version 1.0 6 ## Version 1.0
6 * Cleanup using `clang-tidy` and `clang-format` 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,7 +47,7 @@ After I wrote this, I also found the following libraries:
47 |---------|-------------------| 47 |---------|-------------------|
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. | 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 | [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). | 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 | [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. | 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 | [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. | 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,7 +90,7 @@ To set up, add options, and run, your main function will look something like thi
90 CLI::App app{"App description"}; 90 CLI::App app{"App description"};
91 91
92 std::string filename = "default"; 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 try { 95 try {
96 app.parse(argc, argv); 96 app.parse(argc, argv);
@@ -194,6 +194,7 @@ There are several options that are supported on the main app and subcommands. Th @@ -194,6 +194,7 @@ There are several options that are supported on the main app and subcommands. Th
194 * `.parsed()`: True if this subcommand was given on the command line 194 * `.parsed()`: True if this subcommand was given on the command line
195 * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point. 195 * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
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). 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 ## Configuration file 199 ## Configuration file
199 200
examples/CMakeLists.txt
@@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp) @@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp)
18 add_cli_exe(subcommands subcommands.cpp) 18 add_cli_exe(subcommands subcommands.cpp)
19 add_cli_exe(groups groups.cpp) 19 add_cli_exe(groups groups.cpp)
20 add_cli_exe(inter_argument_order inter_argument_order.cpp) 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,6 +2,7 @@
2 #include <iostream> 2 #include <iostream>
3 #include <vector> 3 #include <vector>
4 #include <tuple> 4 #include <tuple>
  5 +#include <algorithm>
5 6
6 int main(int argc, char **argv) { 7 int main(int argc, char **argv) {
7 CLI::App app; 8 CLI::App app;
@@ -25,7 +26,7 @@ int main(int argc, char **argv) { @@ -25,7 +26,7 @@ int main(int argc, char **argv) {
25 std::reverse(std::begin(foos), std::end(foos)); 26 std::reverse(std::begin(foos), std::end(foos));
26 std::reverse(std::begin(bars), std::end(bars)); 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 for(auto option : app.parse_order()) { 30 for(auto option : app.parse_order()) {
30 if(option == foo) { 31 if(option == foo) {
31 keyval.emplace_back("foo", foos.back()); 32 keyval.emplace_back("foo", foos.back());
@@ -38,11 +39,7 @@ int main(int argc, char **argv) { @@ -38,11 +39,7 @@ int main(int argc, char **argv) {
38 } 39 }
39 40
40 // Prove the vector is correct 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,6 +57,9 @@ class App {
57 /// If true, allow extra arguments (ie, don't throw an error). 57 /// If true, allow extra arguments (ie, don't throw an error).
58 bool allow_extras_{false}; 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 /// This is a function that runs when complete. Great for subcommands. Can throw. 63 /// This is a function that runs when complete. Great for subcommands. Can throw.
61 std::function<void()> callback_; 64 std::function<void()> callback_;
62 65
@@ -152,6 +155,12 @@ class App { @@ -152,6 +155,12 @@ class App {
152 return this; 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 /// Ignore case. Subcommand inherit value. 164 /// Ignore case. Subcommand inherit value.
156 App *ignore_case(bool value = true) { 165 App *ignore_case(bool value = true) {
157 ignore_case_ = value; 166 ignore_case_ = value;
@@ -949,7 +958,7 @@ class App { @@ -949,7 +958,7 @@ class App {
949 return val.first != detail::Classifer::POSITIONAL_MARK; 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 throw ExtrasError("[" + detail::rjoin(args, " ") + "]"); 962 throw ExtrasError("[" + detail::rjoin(args, " ") + "]");
954 } 963 }
955 } 964 }
@@ -1060,7 +1069,15 @@ class App { @@ -1060,7 +1069,15 @@ class App {
1060 else { 1069 else {
1061 args.pop_back(); 1070 args.pop_back();
1062 missing()->emplace_back(detail::Classifer::NONE, positional); 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 /// Parse a subcommand, modify args and continue 1083 /// Parse a subcommand, modify args and continue
include/CLI/Option.hpp
@@ -433,6 +433,10 @@ class Option { @@ -433,6 +433,10 @@ class Option {
433 /// Set the default value string representation 433 /// Set the default value string representation
434 void set_default_val(std::string val) { defaultval_ = val; } 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 protected: 442 protected:
tests/HelpTest.cpp
@@ -190,6 +190,22 @@ TEST(THelp, ExcludesPositional) { @@ -190,6 +190,22 @@ TEST(THelp, ExcludesPositional) {
190 EXPECT_THAT(help, HasSubstr("Excludes: op1")); 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 TEST(THelp, Subcom) { 209 TEST(THelp, Subcom) {
194 CLI::App app{"My prog"}; 210 CLI::App app{"My prog"};
195 211
tests/SubcommandTest.cpp
@@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) { @@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) {
231 EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound); 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 struct SubcommandProgram : public TApp { 247 struct SubcommandProgram : public TApp {
235 248
236 CLI::App *start; 249 CLI::App *start;