Commit 3c57be7adf373f730e0dffe0f0f4272ddc94fa0c
Merge branch 'master' into basic-enum
Showing
9 changed files
with
92 additions
and
10 deletions
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; |