mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 20:53:52 +00:00
commit
b88f1f2ac7
@ -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)
|
||||
|
@ -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
|
||||
|
@ -855,7 +855,7 @@ class App {
|
||||
|
||||
bool _valid_subcommand(const std::string ¤t) 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();
|
||||
|
@ -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(); });
|
||||
|
Loading…
x
Reference in New Issue
Block a user