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
|
/// Count the required remaining positional arguments
|
||||||
size_t _count_remaining_positionals(bool required_only = false) const {
|
size_t _count_remaining_positionals(bool required_only = false) const {
|
||||||
size_t retval = 0;
|
size_t retval = 0;
|
||||||
for(const Option_p &opt : options_)
|
for(const Option_p &opt : options_) {
|
||||||
if(opt->get_positional() && (!required_only || opt->get_required()) && opt->get_items_expected() > 0 &&
|
if(opt->get_positional() && (!required_only || opt->get_required())) {
|
||||||
static_cast<int>(opt->count()) < opt->get_items_expected())
|
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();
|
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;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2349,6 +2353,32 @@ class App {
|
|||||||
bool _parse_positional(std::vector<std::string> &args) {
|
bool _parse_positional(std::vector<std::string> &args) {
|
||||||
|
|
||||||
const std::string &positional = args.back();
|
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_) {
|
for(const Option_p &opt : options_) {
|
||||||
// Eat options, one by one, until done
|
// Eat options, one by one, until done
|
||||||
if(opt->get_positional() &&
|
if(opt->get_positional() &&
|
||||||
|
@ -992,6 +992,73 @@ TEST_F(TApp, PositionalAtEnd) {
|
|||||||
EXPECT_THROW(run(), CLI::ExtrasError);
|
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
|
// Tests positionals at end
|
||||||
TEST_F(TApp, PositionalValidation) {
|
TEST_F(TApp, PositionalValidation) {
|
||||||
std::string options;
|
std::string options;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user