diff --git a/CHANGELOG.md b/CHANGELOG.md index a066720..4814a7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Version 1.1 (in progress) * Added `app.parse_order()` with original parse order +* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns. ## Version 1.0 * Cleanup using `clang-tidy` and `clang-format` diff --git a/README.md b/README.md index 49f8bd6..42a32f5 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ There are several options that are supported on the main app and subcommands. Th * `.parsed()`: True if this subcommand was given on the command line * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point. * `.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). +* `.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. ## Configuration file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f41d948..1b6f2ab 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp) add_cli_exe(subcommands subcommands.cpp) add_cli_exe(groups groups.cpp) add_cli_exe(inter_argument_order inter_argument_order.cpp) +add_cli_exe(prefix_command prefix_command.cpp) diff --git a/examples/prefix_command.cpp b/examples/prefix_command.cpp new file mode 100644 index 0000000..103ee83 --- /dev/null +++ b/examples/prefix_command.cpp @@ -0,0 +1,32 @@ +#include "CLI/CLI.hpp" + +int main(int argc, char **argv) { + + CLI::App app("Prefix command app"); + app.prefix_command(); + + std::vector vals; + app.add_option("--vals,-v", vals) + ->expected(1); + + std::vector more_comms; + try { + more_comms = app.parse(argc, argv); + } catch(const CLI::ParseError &e) { + return app.exit(e); + } + + std::cout << "Prefix:"; + for(int v : vals) + std::cout << v << ":"; + + std::cout << std::endl << "Remaining commands: "; + + // Perfer to loop over from beginning, not "pop" order + std::reverse(std::begin(more_comms), std::end(more_comms)); + for(auto com : more_comms) + std::cout << com << " "; + std::cout << std::endl; + + return 0; +} diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index f4729cd..85035f7 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -57,6 +57,9 @@ class App { /// If true, allow extra arguments (ie, don't throw an error). bool allow_extras_{false}; + /// If true, return immediatly on an unrecognised option (implies allow_extras) + bool prefix_command_{false}; + /// This is a function that runs when complete. Great for subcommands. Can throw. std::function callback_; @@ -152,6 +155,12 @@ class App { return this; } + /// Do not parse anything after the first unrecongnised option and return + App *prefix_command(bool allow = true) { + prefix_command_ = allow; + return this; + } + /// Ignore case. Subcommand inherit value. App *ignore_case(bool value = true) { ignore_case_ = value; @@ -855,7 +864,7 @@ class App { return val.first != detail::Classifer::POSITIONAL_MARK; }); - if(num_left_over > 0 && !allow_extras_) + if(num_left_over > 0 && !(allow_extras_ || prefix_command_)) throw ExtrasError("[" + detail::rjoin(args, " ") + "]"); } } @@ -966,7 +975,15 @@ class App { else { args.pop_back(); missing()->emplace_back(detail::Classifer::NONE, positional); + + if(prefix_command_) { + while(!args.empty()) { + missing()->emplace_back(detail::Classifer::NONE, args.back()); + args.pop_back(); + } + } } + } /// Parse a subcommand, modify args and continue diff --git a/tests/SubcommandTest.cpp b/tests/SubcommandTest.cpp index ef363ab..c275370 100644 --- a/tests/SubcommandTest.cpp +++ b/tests/SubcommandTest.cpp @@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) { EXPECT_THROW(app.get_subcommand(two), CLI::OptionNotFound); } +TEST_F(TApp, PrefixProgram) { + + app.prefix_command(); + + app.add_flag("--simple"); + + args = {"--simple", "other", "--simple", "--mine"}; + auto ret_args = run(); + + EXPECT_EQ(ret_args, std::vector({"--mine", "--simple", "other"})); + +} + struct SubcommandProgram : public TApp { CLI::App *start;