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

Merge pull request #25 from CLIUtils/subcom

Subcom
This commit is contained in:
Henry Schreiner 2017-08-23 13:38:27 -07:00 committed by GitHub
commit b88f1f2ac7
4 changed files with 97 additions and 1 deletions

View File

@ -1,3 +1,7 @@
## Version 1.2 (in progress)
* Required positionals now take priority over subcommands [#23](https://github.com/CLIUtils/CLI11/issues/23)
## Version 1.1
* Added simple support for enumerations, allow non-printable objects [#12](https://github.com/CLIUtils/CLI11/issues/12)

View File

@ -198,6 +198,8 @@ There are several options that are supported on the main app and subcommands. Th
* `.allow_extras()`: Do not throw an error if extra arguments are left over (Only useful on the main `App`, as that's the one that throws errors).
* `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognised item. It is ideal for allowing your app to be a "prefix" to calling another app.
> Note: if you have a fixed number of required positional options, that will match before subcommand names.
## Configuration file
```cpp

View File

@ -855,7 +855,7 @@ class App {
bool _valid_subcommand(const std::string &current) const {
for(const App_p &com : subcommands_)
if(com->check_name(current))
if(com->check_name(current) && !*com)
return true;
if(parent_ != nullptr)
return parent_->_valid_subcommand(current);
@ -1064,6 +1064,19 @@ class App {
}
}
/// Count the required remaining positional arguments
size_t _count_remaining_required_positionals() const {
size_t retval = 0;
for(const Option_p &opt : options_)
if(opt->get_positional()
&& opt->get_required()
&& opt->get_expected() > 0
&& static_cast<int>(opt->count()) < opt->get_expected())
retval = static_cast<size_t>(opt->get_expected()) - opt->count();
return retval;
}
/// Parse a positional, go up the tree to check
void _parse_positional(std::vector<std::string> &args) {
@ -1100,6 +1113,8 @@ class App {
///
/// Unlike the others, this one will always allow fallthrough
void _parse_subcommand(std::vector<std::string> &args) {
if(_count_remaining_required_positionals() > 0)
return _parse_positional(args);
for(const App_p &com : subcommands_) {
if(com->check_name(args.back())) {
args.pop_back();

View File

@ -74,6 +74,81 @@ TEST_F(TApp, MultiSubFallthrough) {
EXPECT_THROW(app.got_subcommand("sub3"), CLI::OptionNotFound);
}
TEST_F(TApp, RequiredAndSubcoms) { // #23
std::string baz;
app.add_option("baz", baz, "Baz Description", true)->required();
auto foo = app.add_subcommand("foo");
auto bar = app.add_subcommand("bar");
args = {"bar", "foo"};
EXPECT_NO_THROW(run());
EXPECT_TRUE(*foo);
EXPECT_FALSE(*bar);
EXPECT_EQ(baz, "bar");
app.reset();
args = {"foo"};
EXPECT_NO_THROW(run());
EXPECT_FALSE(*foo);
EXPECT_EQ(baz, "foo");
app.reset();
args = {"foo", "foo"};
EXPECT_NO_THROW(run());
EXPECT_TRUE(*foo);
EXPECT_EQ(baz, "foo");
app.reset();
args = {"foo", "other"};
EXPECT_THROW(run(), CLI::ExtrasError);
}
TEST_F(TApp, RequiredAndSubcomFallthrough) {
std::string baz;
app.add_option("baz", baz)->required();
app.add_subcommand("foo");
auto bar = app.add_subcommand("bar");
app.fallthrough();
args = {"other", "bar"};
run();
EXPECT_TRUE(bar);
EXPECT_EQ(baz, "other");
app.reset();
args = {"bar", "other2"};
EXPECT_THROW(run(), CLI::ExtrasError);
}
TEST_F(TApp, FooFooProblem) {
std::string baz_str, other_str;
auto baz = app.add_option("baz", baz_str);
auto foo = app.add_subcommand("foo");
auto other = foo->add_option("other", other_str);
args = {"foo", "foo"};
run();
EXPECT_TRUE(*foo);
EXPECT_FALSE(*baz);
EXPECT_TRUE(*other);
EXPECT_EQ(baz_str, "");
EXPECT_EQ(other_str, "foo");
app.reset();
baz_str = "";
other_str = "";
baz->required();
run();
EXPECT_TRUE(*foo);
EXPECT_TRUE(*baz);
EXPECT_FALSE(*other);
EXPECT_EQ(baz_str, "foo");
EXPECT_EQ(other_str, "");
}
TEST_F(TApp, Callbacks) {
auto sub1 = app.add_subcommand("sub1");
sub1->set_callback([]() { throw CLI::Success(); });