Commit 1c1a62224e75bfe33596609abc5c3991fa1fbf6d

Authored by Henry Schreiner
Committed by GitHub
2 parents e3423bb5 36ac4c1c

Merge pull request #16 from CLIUtils/prefix_program

Prefix program support
CHANGELOG.md
1 1 ## Version 1.1 (in progress)
2 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 5 ## Version 1.0
5 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 194 * `.parsed()`: True if this subcommand was given on the command line
195 195 * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
196 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 199 ## Configuration file
199 200  
... ...
examples/CMakeLists.txt
... ... @@ -18,3 +18,4 @@ add_cli_exe(simple simple.cpp)
18 18 add_cli_exe(subcommands subcommands.cpp)
19 19 add_cli_exe(groups groups.cpp)
20 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 57 /// If true, allow extra arguments (ie, don't throw an error).
58 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 63 /// This is a function that runs when complete. Great for subcommands. Can throw.
61 64 std::function<void()> callback_;
62 65  
... ... @@ -152,6 +155,12 @@ class App {
152 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 164 /// Ignore case. Subcommand inherit value.
156 165 App *ignore_case(bool value = true) {
157 166 ignore_case_ = value;
... ... @@ -855,7 +864,7 @@ class App {
855 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 868 throw ExtrasError("[" + detail::rjoin(args, " ") + "]");
860 869 }
861 870 }
... ... @@ -966,7 +975,15 @@ class App {
966 975 else {
967 976 args.pop_back();
968 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 989 /// Parse a subcommand, modify args and continue
... ...
tests/SubcommandTest.cpp
... ... @@ -231,6 +231,19 @@ TEST_F(TApp, BadSubcomSearch) {
231 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 247 struct SubcommandProgram : public TApp {
235 248  
236 249 CLI::App *start;
... ...