mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 04:33:53 +00:00
Adding bool check for parsed subcommand
This commit is contained in:
parent
6253e16375
commit
c289d941f9
@ -6,6 +6,7 @@
|
|||||||
* Ini no longer lists the help pointer
|
* Ini no longer lists the help pointer
|
||||||
* Added test for inclusion in multiple files and linking, fixed issues (rarely needed for CLI, but nice for tools)
|
* Added test for inclusion in multiple files and linking, fixed issues (rarely needed for CLI, but nice for tools)
|
||||||
* Support for complex numbers
|
* Support for complex numbers
|
||||||
|
* Subcommands now test true/false directly, cleaner parse
|
||||||
|
|
||||||
## Version 0.8
|
## Version 0.8
|
||||||
|
|
||||||
|
@ -168,7 +168,10 @@ Subcommands are supported, and can be nested infinitely. To add a subcommand, ca
|
|||||||
case).
|
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.
|
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 `.set_callback`. If you throw `CLI::Success`, you can
|
If an App (main or subcommand) has been parsed on the command line, it's "value" is true, and it is false otherwise.
|
||||||
|
All `App`s have a `get_subcommands()` method, which returns a list of pointers to the subcommands passed on the command line. A `got_subcommand(App_or_name)` method is also provided that will check to see if an App pointer or a string name was collected on the command line.
|
||||||
|
|
||||||
|
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 `.set_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.
|
even exit the program through the callback. The main `App` has a callback slot, as well, but it is generally not as useful.
|
||||||
If you want only one, use `app.require_subcommand(1)`. You are allowed to throw `CLI::Success` in the callbacks.
|
If you want only one, use `app.require_subcommand(1)`. You are allowed to throw `CLI::Success` in the callbacks.
|
||||||
Multiple subcommands are allowed, to allow [`Click`][Click] like series of commands (order is preserved).
|
Multiple subcommands are allowed, to allow [`Click`][Click] like series of commands (order is preserved).
|
||||||
|
@ -98,8 +98,8 @@ protected:
|
|||||||
/// A pointer to the parent if this is a subcommand
|
/// A pointer to the parent if this is a subcommand
|
||||||
App* parent_ {nullptr};
|
App* parent_ {nullptr};
|
||||||
|
|
||||||
/// A list of all the subcommand pointers that have been collected on the command line
|
/// True if this command/subcommand was parsed
|
||||||
std::vector<App*> selected_subcommands_;
|
bool parsed_ {false};
|
||||||
|
|
||||||
/// -1 for 1 or more, 0 for not required, # for exact number required
|
/// -1 for 1 or more, 0 for not required, # for exact number required
|
||||||
int require_subcommand_ = 0;
|
int require_subcommand_ = 0;
|
||||||
@ -168,6 +168,9 @@ public:
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check to see if this subcommand was parsed, true only if received on command line.
|
||||||
|
operator bool () const { return parsed_;}
|
||||||
|
|
||||||
/// Require a subcommand to be given (does not affect help call)
|
/// 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.
|
/// Does not return a pointer since it is supposed to be called on the main App.
|
||||||
App* require_subcommand(int value = -1) {
|
App* require_subcommand(int value = -1) {
|
||||||
@ -472,7 +475,29 @@ public:
|
|||||||
return subcommands_.back().get();
|
return subcommands_.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check to see if a subcommand is part of this command (doesn't have to be in command line)
|
||||||
|
App* get_subcommand(App* subcom) const {
|
||||||
|
for(const App_p &subcomptr : subcommands_)
|
||||||
|
if(subcomptr.get() == subcom)
|
||||||
|
return subcom;
|
||||||
|
throw CLI::OptionNotFound(subcom->get_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check to see if a subcommand is part of this command (text version)
|
||||||
|
App* get_subcommand(std::string subcom) {
|
||||||
|
for(const App_p &subcomptr : subcommands_)
|
||||||
|
if(subcomptr->check_name(subcom))
|
||||||
|
return subcomptr.get();
|
||||||
|
throw CLI::OptionNotFound(subcom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check to see if a subcommand is part of this command (text version, const)
|
||||||
|
App* get_subcommand(std::string subcom) const {
|
||||||
|
for(const App_p &subcomptr : subcommands_)
|
||||||
|
if(subcomptr->check_name(subcom))
|
||||||
|
return subcomptr.get();
|
||||||
|
throw CLI::OptionNotFound(subcom);
|
||||||
|
}
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
/// @name Extras for subclassing
|
/// @name Extras for subclassing
|
||||||
@ -524,7 +549,7 @@ public:
|
|||||||
/// Reset the parsed data
|
/// Reset the parsed data
|
||||||
void reset() {
|
void reset() {
|
||||||
|
|
||||||
selected_subcommands_.clear();
|
parsed_ = false;
|
||||||
missing_.clear();
|
missing_.clear();
|
||||||
|
|
||||||
for(const Option_p &opt : options_) {
|
for(const Option_p &opt : options_) {
|
||||||
@ -550,24 +575,24 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a subcommand pointer list to the currently selected subcommands (after parsing)
|
/// Get a subcommand pointer list to the currently selected subcommands (after parsing)
|
||||||
std::vector<App*> get_subcommands() {
|
std::vector<App*> get_subcommands() const {
|
||||||
return selected_subcommands_;
|
std::vector<App*> subcomms;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Check to see if selected subcommand in list
|
|
||||||
bool got_subcommand(App* subcom) const {
|
|
||||||
return std::find(std::begin(selected_subcommands_),
|
|
||||||
std::end(selected_subcommands_), subcom)
|
|
||||||
!= std::end(selected_subcommands_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check with name instead of pointer
|
|
||||||
bool got_subcommand(std::string name) const {
|
|
||||||
for(const App_p &subcomptr : subcommands_)
|
for(const App_p &subcomptr : subcommands_)
|
||||||
if(subcomptr->check_name(name))
|
if(subcomptr->parsed_)
|
||||||
return got_subcommand(subcomptr.get());
|
subcomms.push_back(subcomptr.get());
|
||||||
throw CLI::OptionNotFound(name);
|
return subcomms;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Check to see if given subcommand was selected
|
||||||
|
bool got_subcommand(App* subcom) const {
|
||||||
|
// get subcom needed to verify that this was a real subcommand
|
||||||
|
return get_subcommand(subcom)->parsed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check with name instead of pointer to see if subcommand was selected
|
||||||
|
bool got_subcommand(std::string name) const {
|
||||||
|
return get_subcommand(name)->parsed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
///@}
|
///@}
|
||||||
@ -621,8 +646,9 @@ public:
|
|||||||
else
|
else
|
||||||
prev += " " + name_;
|
prev += " " + name_;
|
||||||
|
|
||||||
if(selected_subcommands_.size() > 0)
|
auto selected_subcommands = get_subcommands();
|
||||||
return selected_subcommands_.at(0)->help(wid, prev);
|
if(selected_subcommands.size() > 0)
|
||||||
|
return selected_subcommands.at(0)->help(wid, prev);
|
||||||
|
|
||||||
std::stringstream out;
|
std::stringstream out;
|
||||||
out << description_ << std::endl;
|
out << description_ << std::endl;
|
||||||
@ -769,7 +795,7 @@ protected:
|
|||||||
pre_callback();
|
pre_callback();
|
||||||
if(callback_)
|
if(callback_)
|
||||||
callback_();
|
callback_();
|
||||||
for(App* subc : selected_subcommands_) {
|
for(App* subc : get_subcommands()) {
|
||||||
subc->run_callback();
|
subc->run_callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -803,6 +829,7 @@ protected:
|
|||||||
|
|
||||||
/// Internal parse function
|
/// Internal parse function
|
||||||
void _parse(std::vector<std::string> &args) {
|
void _parse(std::vector<std::string> &args) {
|
||||||
|
parsed_ = true;
|
||||||
bool positional_only = false;
|
bool positional_only = false;
|
||||||
|
|
||||||
while(args.size()>0) {
|
while(args.size()>0) {
|
||||||
@ -863,9 +890,10 @@ protected:
|
|||||||
throw ExcludesError(opt->get_name(), opt_ex->get_name());
|
throw ExcludesError(opt->get_name(), opt_ex->get_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(require_subcommand_ < 0 && selected_subcommands_.size() == 0)
|
auto selected_subcommands =get_subcommands();
|
||||||
|
if(require_subcommand_ < 0 && selected_subcommands.size() == 0)
|
||||||
throw RequiredError("Subcommand required");
|
throw RequiredError("Subcommand required");
|
||||||
else if(require_subcommand_ > 0 && (int) selected_subcommands_.size() != require_subcommand_)
|
else if(require_subcommand_ > 0 && (int) selected_subcommands.size() != require_subcommand_)
|
||||||
throw RequiredError(std::to_string(require_subcommand_) + " subcommand(s) required");
|
throw RequiredError(std::to_string(require_subcommand_) + " subcommand(s) required");
|
||||||
|
|
||||||
// Convert missing (pairs) to extras (string only)
|
// Convert missing (pairs) to extras (string only)
|
||||||
@ -1002,7 +1030,6 @@ protected:
|
|||||||
for(const App_p &com : subcommands_) {
|
for(const App_p &com : subcommands_) {
|
||||||
if(com->check_name(args.back())){
|
if(com->check_name(args.back())){
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
selected_subcommands_.push_back(com.get());
|
|
||||||
com->_parse(args);
|
com->_parse(args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,18 @@ TEST_F(TApp, MultiSubFallthrough) {
|
|||||||
|
|
||||||
// No explicit fallthrough
|
// No explicit fallthrough
|
||||||
auto sub1 = app.add_subcommand("sub1");
|
auto sub1 = app.add_subcommand("sub1");
|
||||||
app.add_subcommand("sub2");
|
auto sub2 = app.add_subcommand("sub2");
|
||||||
|
|
||||||
|
|
||||||
args = {"sub1", "sub2"};
|
args = {"sub1", "sub2"};
|
||||||
run();
|
run();
|
||||||
EXPECT_TRUE(app.got_subcommand("sub1"));
|
EXPECT_TRUE(app.got_subcommand("sub1"));
|
||||||
EXPECT_TRUE(app.got_subcommand(sub1));
|
EXPECT_TRUE(app.got_subcommand(sub1));
|
||||||
|
EXPECT_TRUE(*sub1);
|
||||||
|
|
||||||
EXPECT_TRUE(app.got_subcommand("sub2"));
|
EXPECT_TRUE(app.got_subcommand("sub2"));
|
||||||
|
EXPECT_TRUE(app.got_subcommand(sub2));
|
||||||
|
EXPECT_TRUE(*sub2);
|
||||||
|
|
||||||
app.reset();
|
app.reset();
|
||||||
app.require_subcommand();
|
app.require_subcommand();
|
||||||
@ -58,6 +62,9 @@ TEST_F(TApp, MultiSubFallthrough) {
|
|||||||
|
|
||||||
EXPECT_TRUE(app.got_subcommand("sub1"));
|
EXPECT_TRUE(app.got_subcommand("sub1"));
|
||||||
EXPECT_FALSE(app.got_subcommand("sub2"));
|
EXPECT_FALSE(app.got_subcommand("sub2"));
|
||||||
|
|
||||||
|
EXPECT_TRUE(*sub1);
|
||||||
|
EXPECT_FALSE(*sub2);
|
||||||
|
|
||||||
EXPECT_THROW(app.got_subcommand("sub3"), CLI::OptionNotFound);
|
EXPECT_THROW(app.got_subcommand("sub3"), CLI::OptionNotFound);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user