From 2c245a2c3b0caad65fddbd3dd3b4bcad709b30ab Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 2 Apr 2024 23:34:35 +0200 Subject: [PATCH] Fix parsing of a program that accepts a positional argument with 1:* cardinality followed by another positional argument with 1:1 --- include/argparse/argparse.hpp | 16 +++++++++++++ test/test_positional_arguments.cpp | 37 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index ba546e33..58427981 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -2322,6 +2322,22 @@ class ArgumentParser { } } auto argument = positional_argument_it++; + + // Deal with the situation of ... + if (argument->m_num_args_range.get_min() == 1 && + argument->m_num_args_range.get_max() == (std::numeric_limits::max)() && + positional_argument_it != std::end(m_positional_arguments) && + std::next(positional_argument_it) == std::end(m_positional_arguments) && + positional_argument_it->m_num_args_range.get_min() == 1 && + positional_argument_it->m_num_args_range.get_max() == 1 ) { + if (std::next(it) != end) { + positional_argument_it->consume(std::prev(end), end); + end = std::prev(end); + } else { + throw std::runtime_error("Missing " + positional_argument_it->m_names.front()); + } + } + it = argument->consume(it, end); continue; } diff --git a/test/test_positional_arguments.cpp b/test/test_positional_arguments.cpp index 16650488..ae5b99f5 100644 --- a/test/test_positional_arguments.cpp +++ b/test/test_positional_arguments.cpp @@ -264,3 +264,40 @@ TEST_CASE("Square a number" * test_suite("positional_arguments")) { program.parse_args({"./main", "15"}); REQUIRE(program.get("square") == 225); } + +TEST_CASE("At_least_one_followed_by_exactly_one" * test_suite("positional_arguments")) { + GIVEN("a program that accepts a positional argument with at_least_one cardinality followed by another positional argument with 1:1") { + argparse::ArgumentParser program; + + std::vector at_least_one; + program.add_argument("at_least_one") + .nargs(argparse::nargs_pattern::at_least_one) + .store_into(at_least_one); + + std::string exactly_one; + program.add_argument("exactly_one") + .store_into(exactly_one); + + WHEN("provided one, two") { + THEN("parse_args works") { + program.parse_args({"./main", "one", "two"}); + REQUIRE(at_least_one == std::vector{"one"}); + REQUIRE(exactly_one == "two"); + } + } + + WHEN("provided one, two, three") { + THEN("parse_args works") { + program.parse_args({"./main", "one", "two", "three"}); + REQUIRE(at_least_one == std::vector{"one", "two"}); + REQUIRE(exactly_one == "three"); + } + } + + WHEN("provided one, two") { + THEN("parse_args throws") { + REQUIRE_THROWS(program.parse_args({"./main", "one"})); + } + } + } +}