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
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
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; | ... | ... |