1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 12:13:52 +00:00

merge upstream App (#246)

Make sure that nameless subcommands can handle subcommands and that App will treat subcommands in a group nearly the same as if they were in the in the app to begin with.
This commit is contained in:
Philip Top 2019-03-02 03:14:58 -08:00 committed by Henry Schreiner
parent 0631189b4d
commit 6aa546fc42
2 changed files with 140 additions and 23 deletions

View File

@ -1045,10 +1045,10 @@ class App {
/// Check to see if a subcommand is part of this command (text version)
App *get_subcommand(std::string subcom) const {
for(const App_p &subcomptr : subcommands_)
if(subcomptr->check_name(subcom))
return subcomptr.get();
throw OptionNotFound(subcom);
auto subc = _find_subcommand(subcom, false);
if(subc == nullptr)
throw OptionNotFound(subcom);
return subc;
}
/// Get a pointer to subcommand by index
App *get_subcommand(int index = 0) const {
@ -1736,11 +1736,10 @@ class App {
if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
return parent_ != nullptr && parent_->_valid_subcommand(current);
}
for(const App_p &com : subcommands_)
if(!com->disabled_ && com->check_name(current) && !*com)
return true;
auto com = _find_subcommand(current, true);
if((com != nullptr) && !*com) {
return true;
}
// Check parent if exists, else return false
return parent_ != nullptr && parent_->_valid_subcommand(current);
}
@ -2151,28 +2150,43 @@ class App {
}
}
/// Locate a subcommand by name
App *_find_subcommand(const std::string &subc_name, bool ignore_disabled) const noexcept {
for(const App_p &com : subcommands_) {
if((com->disabled_) && (ignore_disabled))
continue;
if(com->get_name().empty()) {
auto subc = com->_find_subcommand(subc_name, ignore_disabled);
if(subc != nullptr) {
return subc;
}
} else if(com->check_name(subc_name)) {
return com.get();
}
}
return nullptr;
}
/// Parse a subcommand, modify args and continue
///
/// Unlike the others, this one will always allow fallthrough
void _parse_subcommand(std::vector<std::string> &args) {
if(_count_remaining_positionals(/* required */ true) > 0)
return _parse_positional(args);
for(const App_p &com : subcommands_) {
if(com->disabled_)
continue;
if(com->check_name(args.back())) {
args.pop_back();
if(std::find(std::begin(parsed_subcommands_), std::end(parsed_subcommands_), com.get()) ==
std::end(parsed_subcommands_))
parsed_subcommands_.push_back(com.get());
com->_parse(args);
return;
}
auto com = _find_subcommand(args.back(), true);
if(com != nullptr) {
args.pop_back();
if(std::find(std::begin(parsed_subcommands_), std::end(parsed_subcommands_), com) ==
std::end(parsed_subcommands_))
parsed_subcommands_.push_back(com);
com->_parse(args);
return;
}
if(parent_ != nullptr)
return parent_->_parse_subcommand(args);
else
if(parent_ == nullptr)
throw HorribleError("Subcommand " + args.back() + " missing");
return parent_->_parse_subcommand(args);
}
/// Parse a short (false) or long (true) argument, must be at the top of the list

View File

@ -234,6 +234,109 @@ TEST_F(TApp, NamelessSubComPositionals) {
EXPECT_EQ(val, 2);
}
TEST_F(TApp, NamelessSubWithSub) {
auto sub = app.add_subcommand();
auto subsub = sub->add_subcommand("val");
args = {"val"};
run();
EXPECT_TRUE(subsub->parsed());
EXPECT_TRUE(app.got_subcommand("val"));
}
TEST_F(TApp, NamelessSubWithMultipleSub) {
auto sub1 = app.add_subcommand();
auto sub2 = app.add_subcommand();
auto sub1sub1 = sub1->add_subcommand("val1");
auto sub1sub2 = sub1->add_subcommand("val2");
auto sub2sub1 = sub2->add_subcommand("val3");
auto sub2sub2 = sub2->add_subcommand("val4");
args = {"val1"};
run();
EXPECT_TRUE(sub1sub1->parsed());
EXPECT_TRUE(app.got_subcommand("val1"));
args = {"val2"};
run();
EXPECT_TRUE(sub1sub2->parsed());
EXPECT_TRUE(app.got_subcommand("val2"));
args = {"val3"};
run();
EXPECT_TRUE(sub2sub1->parsed());
EXPECT_TRUE(app.got_subcommand("val3"));
args = {"val4"};
run();
EXPECT_TRUE(sub2sub2->parsed());
EXPECT_TRUE(app.got_subcommand("val4"));
args = {"val4", "val1"};
run();
EXPECT_TRUE(sub2sub2->parsed());
EXPECT_TRUE(app.got_subcommand("val4"));
EXPECT_TRUE(sub1sub1->parsed());
EXPECT_TRUE(app.got_subcommand("val1"));
}
TEST_F(TApp, Nameless4LayerDeep) {
auto sub = app.add_subcommand();
auto ssub = sub->add_subcommand();
auto sssub = ssub->add_subcommand();
auto ssssub = sssub->add_subcommand();
auto sssssub = ssssub->add_subcommand("val");
args = {"val"};
run();
EXPECT_TRUE(sssssub->parsed());
EXPECT_TRUE(app.got_subcommand("val"));
}
/// Put subcommands in some crazy pattern and make everything still works
TEST_F(TApp, Nameless4LayerDeepMulit) {
auto sub1 = app.add_subcommand();
auto sub2 = app.add_subcommand();
auto ssub1 = sub1->add_subcommand();
auto ssub2 = sub2->add_subcommand();
auto sssub1 = ssub1->add_subcommand();
auto sssub2 = ssub2->add_subcommand();
sssub1->add_subcommand("val1");
ssub2->add_subcommand("val2");
sub2->add_subcommand("val3");
ssub1->add_subcommand("val4");
sssub2->add_subcommand("val5");
args = {"val1"};
run();
EXPECT_TRUE(app.got_subcommand("val1"));
args = {"val2"};
run();
EXPECT_TRUE(app.got_subcommand("val2"));
args = {"val3"};
run();
EXPECT_TRUE(app.got_subcommand("val3"));
args = {"val4"};
run();
EXPECT_TRUE(app.got_subcommand("val4"));
args = {"val5"};
run();
EXPECT_TRUE(app.got_subcommand("val5"));
args = {"val4", "val1", "val5"};
run();
EXPECT_TRUE(app.got_subcommand("val4"));
EXPECT_TRUE(app.got_subcommand("val1"));
EXPECT_TRUE(app.got_subcommand("val5"));
}
TEST_F(TApp, FallThroughRegular) {
app.fallthrough();
int val = 1;