From ade78c5842640965506b7e74b654ccee224288b2 Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Fri, 24 Feb 2017 12:54:37 -0500 Subject: [PATCH] Adding a number to required_subcommand --- CHANGELOG.md | 4 ++++ README.md | 4 +++- include/CLI/App.hpp | 10 ++++++---- tests/SubcommandTest.cpp | 17 +++++++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 690e72bd..43316fc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Version 0.6 + +* Added the ability to add a number to `.require_subcommand()`. + ## Version 0.5 * Allow `Hidden` options. diff --git a/README.md b/README.md index 7b9ba800..15b086f3 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,9 @@ everything after that is positional only. ## Subcommands -Subcommands are naturally supported, with an infinite depth. To add a subcommand, call the `add_subcommand` method with a name and an optional description. This gives a pointer to an `App` that behaves just like the main app, and can take options or further subcommands. Add `->ignore_case()` to a subcommand to allow any variation of caps to also be accepted. Children inherit the current setting from the parent. You cannot add multiple matching subcommand names at the same level (including ignore case). +Subcommands are naturally supported, with an infinite depth. To add a subcommand, call the `add_subcommand` method with a name and an optional description. This gives a pointer to an `App` that behaves just like the main app, and can take options or further subcommands. Add `->ignore_case()` to a subcommand to allow any variation of caps to also be accepted. Children inherit the current setting from the parent. You cannot add multiple matching subcommand names at the same level (including ignore +case). +If you want to require at least one subcommand is given, use `.require_subcommand()` on the parent app. You can optionally give an exact number of subcommands to require, as well. All `App`s have a `get_subcommands()` method, which returns a list of pointers to the subcommand passed on the command line. A simple compare of these pointers to each subcommand allows choosing based on subcommand, facilitated by a `got_subcommand(App_or_name) method that will check the list for you. For many cases, however, using an app's callback may be easier. Every app executes a callback function after it parses; just use a lambda function (with capture to get parsed values) to `.add_callback`. If you throw `CLI::Success`, you can even exit the program through the callback. The main `App` has a callback slot, as well, but it is generally not as useful. diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 422e1402..d2e32936 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -52,7 +52,7 @@ protected: std::vector subcommands; bool parsed {false}; std::vector selected_subcommands; - bool required_subcommand = false; + int required_subcommand = 0; ///< -1 for 1 or more, 0 for not required, # for exact number required std::string progname {"program"}; Option* help_flag {nullptr}; @@ -476,7 +476,7 @@ public: } if(subcommands.size() > 0) { - if(required_subcommand) + if(required_subcommand != 0) out << " SUBCOMMAND"; else out << " [SUBCOMMAND]"; @@ -571,7 +571,7 @@ public: /// Require a subcommand to be given (does not affect help call) /// Does not return a pointer since it is supposed to be called on the main App. - void require_subcommand(bool value = true) { + void require_subcommand(int value = -1) { required_subcommand = value; } @@ -729,8 +729,10 @@ protected: throw ExcludesError(opt->get_name(), opt_ex->get_name()); } - if(required_subcommand && selected_subcommands.size() == 0) + if(required_subcommand < 0 && selected_subcommands.size() == 0) throw RequiredError("Subcommand required"); + else if(required_subcommand > 0 && selected_subcommands.size() != required_subcommand) + throw RequiredError(std::to_string(required_subcommand) + " subcommand(s) required"); // Convert missing (pairs) to extras (string only) args.resize(missing.size()); diff --git a/tests/SubcommandTest.cpp b/tests/SubcommandTest.cpp index f147ab4f..b7f5e121 100644 --- a/tests/SubcommandTest.cpp +++ b/tests/SubcommandTest.cpp @@ -60,6 +60,23 @@ TEST_F(TApp, RequiredSubCom) { } +TEST_F(TApp, Required1SubCom) { + app.require_subcommand(1); + app.add_subcommand("sub1"); + app.add_subcommand("sub2"); + app.add_subcommand("sub3"); + + EXPECT_THROW(run(), CLI::RequiredError); + + app.reset(); + args = {"sub1"}; + EXPECT_NO_THROW(run()); + + app.reset(); + args = {"sub1", "sub2"}; + EXPECT_THROW(run(), CLI::RequiredError); +} + struct SubcommandProgram : public TApp { CLI::App* start;