Commit 1c1a62224e75bfe33596609abc5c3991fa1fbf6d
Committed by
GitHub
Merge pull request #16 from CLIUtils/prefix_program
Prefix program support
Showing
6 changed files
with
66 additions
and
1 deletions
CHANGELOG.md
| 1 | ## Version 1.1 (in progress) | 1 | ## Version 1.1 (in progress) |
| 2 | * Added `app.parse_order()` with original parse order | 2 | * Added `app.parse_order()` with original parse order |
| 3 | +* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns. | ||
| 3 | 4 | ||
| 4 | ## Version 1.0 | 5 | ## Version 1.0 |
| 5 | * Cleanup using `clang-tidy` and `clang-format` | 6 | * Cleanup using `clang-tidy` and `clang-format` |
README.md
| @@ -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/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; |
| @@ -855,7 +864,7 @@ class App { | @@ -855,7 +864,7 @@ class App { | ||
| 855 | return val.first != detail::Classifer::POSITIONAL_MARK; | 864 | return val.first != detail::Classifer::POSITIONAL_MARK; |
| 856 | }); | 865 | }); |
| 857 | 866 | ||
| 858 | - if(num_left_over > 0 && !allow_extras_) | 867 | + if(num_left_over > 0 && !(allow_extras_ || prefix_command_)) |
| 859 | throw ExtrasError("[" + detail::rjoin(args, " ") + "]"); | 868 | throw ExtrasError("[" + detail::rjoin(args, " ") + "]"); |
| 860 | } | 869 | } |
| 861 | } | 870 | } |
| @@ -966,7 +975,15 @@ class App { | @@ -966,7 +975,15 @@ class App { | ||
| 966 | else { | 975 | else { |
| 967 | args.pop_back(); | 976 | args.pop_back(); |
| 968 | missing()->emplace_back(detail::Classifer::NONE, positional); | 977 | missing()->emplace_back(detail::Classifer::NONE, positional); |
| 978 | + | ||
| 979 | + if(prefix_command_) { | ||
| 980 | + while(!args.empty()) { | ||
| 981 | + missing()->emplace_back(detail::Classifer::NONE, args.back()); | ||
| 982 | + args.pop_back(); | ||
| 983 | + } | ||
| 984 | + } | ||
| 969 | } | 985 | } |
| 986 | + | ||
| 970 | } | 987 | } |
| 971 | 988 | ||
| 972 | /// Parse a subcommand, modify args and continue | 989 | /// Parse a subcommand, modify args and continue |
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; |