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
|
||||
* Added test for inclusion in multiple files and linking, fixed issues (rarely needed for CLI, but nice for tools)
|
||||
* Support for complex numbers
|
||||
* Subcommands now test true/false directly, cleaner parse
|
||||
|
||||
## Version 0.8
|
||||
|
||||
|
@ -168,7 +168,10 @@ Subcommands are supported, and can be nested infinitely. To add a subcommand, ca
|
||||
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 `.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.
|
||||
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).
|
||||
|
@ -98,8 +98,8 @@ protected:
|
||||
/// A pointer to the parent if this is a subcommand
|
||||
App* parent_ {nullptr};
|
||||
|
||||
/// A list of all the subcommand pointers that have been collected on the command line
|
||||
std::vector<App*> selected_subcommands_;
|
||||
/// True if this command/subcommand was parsed
|
||||
bool parsed_ {false};
|
||||
|
||||
/// -1 for 1 or more, 0 for not required, # for exact number required
|
||||
int require_subcommand_ = 0;
|
||||
@ -168,6 +168,9 @@ public:
|
||||
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)
|
||||
/// Does not return a pointer since it is supposed to be called on the main App.
|
||||
App* require_subcommand(int value = -1) {
|
||||
@ -472,7 +475,29 @@ public:
|
||||
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
|
||||
@ -524,7 +549,7 @@ public:
|
||||
/// Reset the parsed data
|
||||
void reset() {
|
||||
|
||||
selected_subcommands_.clear();
|
||||
parsed_ = false;
|
||||
missing_.clear();
|
||||
|
||||
for(const Option_p &opt : options_) {
|
||||
@ -550,24 +575,24 @@ public:
|
||||
}
|
||||
|
||||
/// Get a subcommand pointer list to the currently selected subcommands (after parsing)
|
||||
std::vector<App*> get_subcommands() {
|
||||
return selected_subcommands_;
|
||||
}
|
||||
|
||||
|
||||
/// 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 {
|
||||
std::vector<App*> get_subcommands() const {
|
||||
std::vector<App*> subcomms;
|
||||
for(const App_p &subcomptr : subcommands_)
|
||||
if(subcomptr->check_name(name))
|
||||
return got_subcommand(subcomptr.get());
|
||||
throw CLI::OptionNotFound(name);
|
||||
if(subcomptr->parsed_)
|
||||
subcomms.push_back(subcomptr.get());
|
||||
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
|
||||
prev += " " + name_;
|
||||
|
||||
if(selected_subcommands_.size() > 0)
|
||||
return selected_subcommands_.at(0)->help(wid, prev);
|
||||
auto selected_subcommands = get_subcommands();
|
||||
if(selected_subcommands.size() > 0)
|
||||
return selected_subcommands.at(0)->help(wid, prev);
|
||||
|
||||
std::stringstream out;
|
||||
out << description_ << std::endl;
|
||||
@ -769,7 +795,7 @@ protected:
|
||||
pre_callback();
|
||||
if(callback_)
|
||||
callback_();
|
||||
for(App* subc : selected_subcommands_) {
|
||||
for(App* subc : get_subcommands()) {
|
||||
subc->run_callback();
|
||||
}
|
||||
}
|
||||
@ -803,6 +829,7 @@ protected:
|
||||
|
||||
/// Internal parse function
|
||||
void _parse(std::vector<std::string> &args) {
|
||||
parsed_ = true;
|
||||
bool positional_only = false;
|
||||
|
||||
while(args.size()>0) {
|
||||
@ -863,9 +890,10 @@ protected:
|
||||
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");
|
||||
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");
|
||||
|
||||
// Convert missing (pairs) to extras (string only)
|
||||
@ -1002,7 +1030,6 @@ protected:
|
||||
for(const App_p &com : subcommands_) {
|
||||
if(com->check_name(args.back())){
|
||||
args.pop_back();
|
||||
selected_subcommands_.push_back(com.get());
|
||||
com->_parse(args);
|
||||
return;
|
||||
}
|
||||
|
@ -28,14 +28,18 @@ TEST_F(TApp, MultiSubFallthrough) {
|
||||
|
||||
// No explicit fallthrough
|
||||
auto sub1 = app.add_subcommand("sub1");
|
||||
app.add_subcommand("sub2");
|
||||
auto sub2 = app.add_subcommand("sub2");
|
||||
|
||||
|
||||
args = {"sub1", "sub2"};
|
||||
run();
|
||||
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(*sub2);
|
||||
|
||||
app.reset();
|
||||
app.require_subcommand();
|
||||
@ -59,6 +63,9 @@ TEST_F(TApp, MultiSubFallthrough) {
|
||||
EXPECT_TRUE(app.got_subcommand("sub1"));
|
||||
EXPECT_FALSE(app.got_subcommand("sub2"));
|
||||
|
||||
EXPECT_TRUE(*sub1);
|
||||
EXPECT_FALSE(*sub2);
|
||||
|
||||
EXPECT_THROW(app.got_subcommand("sub3"), CLI::OptionNotFound);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user