Commit 6aa546fc427871e2e3bbd99bc40b4d5d790c3ef7

Authored by Philip Top
Committed by Henry Schreiner
1 parent 0631189b

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.
include/CLI/App.hpp
... ... @@ -1045,10 +1045,10 @@ class App {
1045 1045  
1046 1046 /// Check to see if a subcommand is part of this command (text version)
1047 1047 App *get_subcommand(std::string subcom) const {
1048   - for(const App_p &subcomptr : subcommands_)
1049   - if(subcomptr->check_name(subcom))
1050   - return subcomptr.get();
1051   - throw OptionNotFound(subcom);
  1048 + auto subc = _find_subcommand(subcom, false);
  1049 + if(subc == nullptr)
  1050 + throw OptionNotFound(subcom);
  1051 + return subc;
1052 1052 }
1053 1053 /// Get a pointer to subcommand by index
1054 1054 App *get_subcommand(int index = 0) const {
... ... @@ -1736,11 +1736,10 @@ class App {
1736 1736 if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
1737 1737 return parent_ != nullptr && parent_->_valid_subcommand(current);
1738 1738 }
1739   -
1740   - for(const App_p &com : subcommands_)
1741   - if(!com->disabled_ && com->check_name(current) && !*com)
1742   - return true;
1743   -
  1739 + auto com = _find_subcommand(current, true);
  1740 + if((com != nullptr) && !*com) {
  1741 + return true;
  1742 + }
1744 1743 // Check parent if exists, else return false
1745 1744 return parent_ != nullptr && parent_->_valid_subcommand(current);
1746 1745 }
... ... @@ -2151,28 +2150,43 @@ class App {
2151 2150 }
2152 2151 }
2153 2152  
  2153 + /// Locate a subcommand by name
  2154 + App *_find_subcommand(const std::string &subc_name, bool ignore_disabled) const noexcept {
  2155 + for(const App_p &com : subcommands_) {
  2156 + if((com->disabled_) && (ignore_disabled))
  2157 + continue;
  2158 + if(com->get_name().empty()) {
  2159 + auto subc = com->_find_subcommand(subc_name, ignore_disabled);
  2160 + if(subc != nullptr) {
  2161 + return subc;
  2162 + }
  2163 + } else if(com->check_name(subc_name)) {
  2164 + return com.get();
  2165 + }
  2166 + }
  2167 + return nullptr;
  2168 + }
  2169 +
2154 2170 /// Parse a subcommand, modify args and continue
2155 2171 ///
2156 2172 /// Unlike the others, this one will always allow fallthrough
2157 2173 void _parse_subcommand(std::vector<std::string> &args) {
2158 2174 if(_count_remaining_positionals(/* required */ true) > 0)
2159 2175 return _parse_positional(args);
2160   - for(const App_p &com : subcommands_) {
2161   - if(com->disabled_)
2162   - continue;
2163   - if(com->check_name(args.back())) {
2164   - args.pop_back();
2165   - if(std::find(std::begin(parsed_subcommands_), std::end(parsed_subcommands_), com.get()) ==
2166   - std::end(parsed_subcommands_))
2167   - parsed_subcommands_.push_back(com.get());
2168   - com->_parse(args);
2169   - return;
2170   - }
  2176 + auto com = _find_subcommand(args.back(), true);
  2177 + if(com != nullptr) {
  2178 + args.pop_back();
  2179 + if(std::find(std::begin(parsed_subcommands_), std::end(parsed_subcommands_), com) ==
  2180 + std::end(parsed_subcommands_))
  2181 + parsed_subcommands_.push_back(com);
  2182 + com->_parse(args);
  2183 + return;
2171 2184 }
2172   - if(parent_ != nullptr)
2173   - return parent_->_parse_subcommand(args);
2174   - else
  2185 +
  2186 + if(parent_ == nullptr)
2175 2187 throw HorribleError("Subcommand " + args.back() + " missing");
  2188 +
  2189 + return parent_->_parse_subcommand(args);
2176 2190 }
2177 2191  
2178 2192 /// Parse a short (false) or long (true) argument, must be at the top of the list
... ...
tests/SubcommandTest.cpp
... ... @@ -234,6 +234,109 @@ TEST_F(TApp, NamelessSubComPositionals) {
234 234 EXPECT_EQ(val, 2);
235 235 }
236 236  
  237 +TEST_F(TApp, NamelessSubWithSub) {
  238 +
  239 + auto sub = app.add_subcommand();
  240 + auto subsub = sub->add_subcommand("val");
  241 +
  242 + args = {"val"};
  243 + run();
  244 + EXPECT_TRUE(subsub->parsed());
  245 + EXPECT_TRUE(app.got_subcommand("val"));
  246 +}
  247 +
  248 +TEST_F(TApp, NamelessSubWithMultipleSub) {
  249 +
  250 + auto sub1 = app.add_subcommand();
  251 + auto sub2 = app.add_subcommand();
  252 + auto sub1sub1 = sub1->add_subcommand("val1");
  253 + auto sub1sub2 = sub1->add_subcommand("val2");
  254 + auto sub2sub1 = sub2->add_subcommand("val3");
  255 + auto sub2sub2 = sub2->add_subcommand("val4");
  256 + args = {"val1"};
  257 + run();
  258 + EXPECT_TRUE(sub1sub1->parsed());
  259 + EXPECT_TRUE(app.got_subcommand("val1"));
  260 +
  261 + args = {"val2"};
  262 + run();
  263 + EXPECT_TRUE(sub1sub2->parsed());
  264 + EXPECT_TRUE(app.got_subcommand("val2"));
  265 +
  266 + args = {"val3"};
  267 + run();
  268 + EXPECT_TRUE(sub2sub1->parsed());
  269 + EXPECT_TRUE(app.got_subcommand("val3"));
  270 +
  271 + args = {"val4"};
  272 + run();
  273 + EXPECT_TRUE(sub2sub2->parsed());
  274 + EXPECT_TRUE(app.got_subcommand("val4"));
  275 +
  276 + args = {"val4", "val1"};
  277 + run();
  278 + EXPECT_TRUE(sub2sub2->parsed());
  279 + EXPECT_TRUE(app.got_subcommand("val4"));
  280 + EXPECT_TRUE(sub1sub1->parsed());
  281 + EXPECT_TRUE(app.got_subcommand("val1"));
  282 +}
  283 +
  284 +TEST_F(TApp, Nameless4LayerDeep) {
  285 +
  286 + auto sub = app.add_subcommand();
  287 + auto ssub = sub->add_subcommand();
  288 + auto sssub = ssub->add_subcommand();
  289 +
  290 + auto ssssub = sssub->add_subcommand();
  291 + auto sssssub = ssssub->add_subcommand("val");
  292 +
  293 + args = {"val"};
  294 + run();
  295 + EXPECT_TRUE(sssssub->parsed());
  296 + EXPECT_TRUE(app.got_subcommand("val"));
  297 +}
  298 +
  299 +/// Put subcommands in some crazy pattern and make everything still works
  300 +TEST_F(TApp, Nameless4LayerDeepMulit) {
  301 +
  302 + auto sub1 = app.add_subcommand();
  303 + auto sub2 = app.add_subcommand();
  304 + auto ssub1 = sub1->add_subcommand();
  305 + auto ssub2 = sub2->add_subcommand();
  306 +
  307 + auto sssub1 = ssub1->add_subcommand();
  308 + auto sssub2 = ssub2->add_subcommand();
  309 + sssub1->add_subcommand("val1");
  310 + ssub2->add_subcommand("val2");
  311 + sub2->add_subcommand("val3");
  312 + ssub1->add_subcommand("val4");
  313 + sssub2->add_subcommand("val5");
  314 + args = {"val1"};
  315 + run();
  316 + EXPECT_TRUE(app.got_subcommand("val1"));
  317 +
  318 + args = {"val2"};
  319 + run();
  320 + EXPECT_TRUE(app.got_subcommand("val2"));
  321 +
  322 + args = {"val3"};
  323 + run();
  324 + EXPECT_TRUE(app.got_subcommand("val3"));
  325 +
  326 + args = {"val4"};
  327 + run();
  328 + EXPECT_TRUE(app.got_subcommand("val4"));
  329 + args = {"val5"};
  330 + run();
  331 + EXPECT_TRUE(app.got_subcommand("val5"));
  332 +
  333 + args = {"val4", "val1", "val5"};
  334 + run();
  335 + EXPECT_TRUE(app.got_subcommand("val4"));
  336 + EXPECT_TRUE(app.got_subcommand("val1"));
  337 + EXPECT_TRUE(app.got_subcommand("val5"));
  338 +}
  339 +
237 340 TEST_F(TApp, FallThroughRegular) {
238 341 app.fallthrough();
239 342 int val = 1;
... ...