mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
required positional arguments and a vector positionals (#306)
* add a check loop for missing required positional, when the number of arguments get small. * fix a few warnings on signed/unsigned checks * add check for a required positional vector.
This commit is contained in:
parent
eb6f759f51
commit
4bfce43795
@ -2326,11 +2326,15 @@ class App {
|
||||
/// Count the required remaining positional arguments
|
||||
size_t _count_remaining_positionals(bool required_only = false) const {
|
||||
size_t retval = 0;
|
||||
for(const Option_p &opt : options_)
|
||||
if(opt->get_positional() && (!required_only || opt->get_required()) && opt->get_items_expected() > 0 &&
|
||||
static_cast<int>(opt->count()) < opt->get_items_expected())
|
||||
retval = static_cast<size_t>(opt->get_items_expected()) - opt->count();
|
||||
|
||||
for(const Option_p &opt : options_) {
|
||||
if(opt->get_positional() && (!required_only || opt->get_required())) {
|
||||
if(opt->get_items_expected() > 0 && static_cast<int>(opt->count()) < opt->get_items_expected()) {
|
||||
retval += static_cast<size_t>(opt->get_items_expected()) - opt->count();
|
||||
} else if(opt->get_required() && opt->get_items_expected() < 0 && opt->count() == 0ul) {
|
||||
retval += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -2349,6 +2353,32 @@ class App {
|
||||
bool _parse_positional(std::vector<std::string> &args) {
|
||||
|
||||
const std::string &positional = args.back();
|
||||
|
||||
if(positionals_at_end_) {
|
||||
// deal with the case of required arguments at the end which should take precedence over other arguments
|
||||
auto arg_rem = args.size();
|
||||
auto remreq = _count_remaining_positionals(true);
|
||||
if(arg_rem <= remreq) {
|
||||
for(const Option_p &opt : options_) {
|
||||
if(opt->get_positional() && opt->required_) {
|
||||
if(static_cast<int>(opt->count()) < opt->get_items_expected() ||
|
||||
(opt->get_items_expected() < 0 && opt->count() == 0lu)) {
|
||||
if(validate_positionals_) {
|
||||
std::string pos = positional;
|
||||
pos = opt->_validate(pos);
|
||||
if(!pos.empty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
opt->add_result(positional);
|
||||
parse_order_.push_back(opt.get());
|
||||
args.pop_back();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(const Option_p &opt : options_) {
|
||||
// Eat options, one by one, until done
|
||||
if(opt->get_positional() &&
|
||||
|
@ -992,6 +992,73 @@ TEST_F(TApp, PositionalAtEnd) {
|
||||
EXPECT_THROW(run(), CLI::ExtrasError);
|
||||
}
|
||||
|
||||
// Tests positionals at end
|
||||
TEST_F(TApp, RequiredPositionals) {
|
||||
std::vector<std::string> sources;
|
||||
std::string dest;
|
||||
app.add_option("src", sources);
|
||||
app.add_option("dest", dest)->required();
|
||||
app.positionals_at_end();
|
||||
|
||||
args = {"1", "2", "3"};
|
||||
run();
|
||||
|
||||
EXPECT_EQ(sources.size(), 2u);
|
||||
EXPECT_EQ(dest, "3");
|
||||
|
||||
args = {"a"};
|
||||
sources.clear();
|
||||
run();
|
||||
|
||||
EXPECT_EQ(sources.size(), 0u);
|
||||
EXPECT_EQ(dest, "a");
|
||||
}
|
||||
|
||||
TEST_F(TApp, RequiredPositionalVector) {
|
||||
std::string d1;
|
||||
std::string d2;
|
||||
std::string d3;
|
||||
std::vector<std::string> sources;
|
||||
|
||||
app.add_option("dest1", d1);
|
||||
app.add_option("dest2", d2);
|
||||
app.add_option("dest3", d3);
|
||||
app.add_option("src", sources)->required();
|
||||
|
||||
app.positionals_at_end();
|
||||
|
||||
args = {"1", "2", "3"};
|
||||
run();
|
||||
|
||||
EXPECT_EQ(sources.size(), 1u);
|
||||
EXPECT_EQ(d1, "1");
|
||||
EXPECT_EQ(d2, "2");
|
||||
EXPECT_TRUE(d3.empty());
|
||||
args = {"a"};
|
||||
sources.clear();
|
||||
run();
|
||||
|
||||
EXPECT_EQ(sources.size(), 1u);
|
||||
}
|
||||
|
||||
// Tests positionals at end
|
||||
TEST_F(TApp, RequiredPositionalValidation) {
|
||||
std::vector<std::string> sources;
|
||||
int dest;
|
||||
std::string d2;
|
||||
app.add_option("src", sources);
|
||||
app.add_option("dest", dest)->required()->check(CLI::PositiveNumber);
|
||||
app.add_option("dest2", d2)->required();
|
||||
app.positionals_at_end()->validate_positionals();
|
||||
|
||||
args = {"1", "2", "string", "3"};
|
||||
run();
|
||||
|
||||
EXPECT_EQ(sources.size(), 2u);
|
||||
EXPECT_EQ(dest, 3);
|
||||
EXPECT_EQ(d2, "string");
|
||||
}
|
||||
|
||||
// Tests positionals at end
|
||||
TEST_F(TApp, PositionalValidation) {
|
||||
std::string options;
|
||||
|
Loading…
x
Reference in New Issue
Block a user