Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefix program support #16

Merged
merged 5 commits into from
Jun 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
32 changes: 32 additions & 0 deletions examples/prefix_command.cpp
Original file line number Diff line number Diff line change
@@ -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<int> vals;
app.add_option("--vals,-v", vals)
->expected(1);

std::vector<std::string> 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;
}
19 changes: 18 additions & 1 deletion include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void()> callback_;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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, " ") + "]");
}
}
Expand Down Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions tests/SubcommandTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>({"--mine", "--simple", "other"}));

}

struct SubcommandProgram : public TApp {

CLI::App *start;
Expand Down