mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 07:04:39 +00:00
Merge pull request #125 from hokacci/feature/variable-length-nargs
Improve nargs
This commit is contained in:
commit
24c599dfde
23
README.md
23
README.md
@ -402,6 +402,29 @@ catch (const std::runtime_error& err) {
|
|||||||
auto query_point = program.get<std::vector<double>>("--query_point"); // {3.5, 4.7, 9.2}
|
auto query_point = program.get<std::vector<double>>("--query_point"); // {3.5, 4.7, 9.2}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also make a variable length list of arguments with the ```.nargs```.
|
||||||
|
Below are some examples.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
program.add_argument("--input_files")
|
||||||
|
.nargs(1, 3); // This accepts 1 to 3 arguments.
|
||||||
|
```
|
||||||
|
|
||||||
|
Some useful patterns are defined like "?", "*", "+" of argparse in Python.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
program.add_argument("--input_files")
|
||||||
|
.nargs(argparse::nargs_pattern::any); // "*" in Python. This accepts any number of arguments including 0.
|
||||||
|
```
|
||||||
|
```cpp
|
||||||
|
program.add_argument("--input_files")
|
||||||
|
.nargs(argparse::nargs_pattern::at_least_one); // "+" in Python. This accepts one or more number of arguments.
|
||||||
|
```
|
||||||
|
```cpp
|
||||||
|
program.add_argument("--input_files")
|
||||||
|
.nargs(argparse::nargs_pattern::optional); // "?" in Python. This accepts an argument optionally.
|
||||||
|
```
|
||||||
|
|
||||||
### Compound Arguments
|
### Compound Arguments
|
||||||
|
|
||||||
Compound arguments are optional arguments that are combined and provided as a single argument. Example: ```ps -aux```
|
Compound arguments are optional arguments that are combined and provided as a single argument. Example: ```ps -aux```
|
||||||
|
@ -38,6 +38,7 @@ SOFTWARE.
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@ -327,6 +328,12 @@ template <class T> struct parse_number<T, chars_format::fixed> {
|
|||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
|
|
||||||
|
enum class nargs_pattern {
|
||||||
|
optional,
|
||||||
|
any,
|
||||||
|
at_least_one
|
||||||
|
};
|
||||||
|
|
||||||
enum class default_arguments : unsigned int {
|
enum class default_arguments : unsigned int {
|
||||||
none = 0,
|
none = 0,
|
||||||
help = 1,
|
help = 1,
|
||||||
@ -383,7 +390,7 @@ public:
|
|||||||
|
|
||||||
Argument &implicit_value(std::any value) {
|
Argument &implicit_value(std::any value) {
|
||||||
m_implicit_value = std::move(value);
|
m_implicit_value = std::move(value);
|
||||||
m_num_args = 0;
|
m_num_args_range = NArgsRange{0, 0};
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,17 +459,34 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Argument &nargs(int num_args) {
|
Argument &nargs(std::size_t num_args) {
|
||||||
if (num_args < 0) {
|
m_num_args_range = NArgsRange{num_args, num_args};
|
||||||
throw std::logic_error("Number of arguments must be non-negative");
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Argument &nargs(std::size_t num_args_min, std::size_t num_args_max) {
|
||||||
|
m_num_args_range = NArgsRange{num_args_min, num_args_max};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Argument &nargs(nargs_pattern pattern) {
|
||||||
|
switch (pattern) {
|
||||||
|
case nargs_pattern::optional:
|
||||||
|
m_num_args_range = NArgsRange{0, 1};
|
||||||
|
break;
|
||||||
|
case nargs_pattern::any:
|
||||||
|
m_num_args_range = NArgsRange{0, std::numeric_limits<std::size_t>::max()};
|
||||||
|
break;
|
||||||
|
case nargs_pattern::at_least_one:
|
||||||
|
m_num_args_range = NArgsRange{1, std::numeric_limits<std::size_t>::max()};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
m_num_args = num_args;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Argument &remaining() {
|
Argument &remaining() {
|
||||||
m_num_args = -1;
|
m_accepts_optional_like_value = true;
|
||||||
return *this;
|
return nargs(nargs_pattern::any);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
@ -473,16 +497,23 @@ public:
|
|||||||
}
|
}
|
||||||
m_is_used = true;
|
m_is_used = true;
|
||||||
m_used_name = used_name;
|
m_used_name = used_name;
|
||||||
if (m_num_args == 0) {
|
|
||||||
|
const auto num_args_max = m_num_args_range.get_max();
|
||||||
|
const auto num_args_min = m_num_args_range.get_min();
|
||||||
|
std::size_t dist = 0;
|
||||||
|
if (num_args_max == 0) {
|
||||||
m_values.emplace_back(m_implicit_value);
|
m_values.emplace_back(m_implicit_value);
|
||||||
std::visit([](const auto &f) { f({}); }, m_action);
|
std::visit([](const auto &f) { f({}); }, m_action);
|
||||||
return start;
|
return start;
|
||||||
}
|
} else if ((dist = static_cast<std::size_t>(std::distance(start, end))) >= num_args_min) {
|
||||||
if (m_num_args <= std::distance(start, end)) {
|
if (num_args_max < dist) {
|
||||||
if (auto expected = maybe_nargs()) {
|
end = std::next(start, num_args_max);
|
||||||
end = std::next(start, *expected);
|
}
|
||||||
if (std::any_of(start, end, Argument::is_optional)) {
|
if (!m_accepts_optional_like_value) {
|
||||||
throw std::runtime_error("optional argument in parameter sequence");
|
end = std::find_if(start, end, Argument::is_optional);
|
||||||
|
dist = static_cast<std::size_t>(std::distance(start, end));
|
||||||
|
if (dist < num_args_min) {
|
||||||
|
throw std::runtime_error("Too few arguments");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,9 +525,8 @@ public:
|
|||||||
void operator()(void_action &f) {
|
void operator()(void_action &f) {
|
||||||
std::for_each(first, last, f);
|
std::for_each(first, last, f);
|
||||||
if (!self.m_default_value.has_value()) {
|
if (!self.m_default_value.has_value()) {
|
||||||
if (auto expected = self.maybe_nargs()) {
|
if (!self.m_accepts_optional_like_value)
|
||||||
self.m_values.resize(*expected);
|
self.m_values.resize(std::distance(first, last));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,38 +547,21 @@ public:
|
|||||||
* @throws std::runtime_error if argument values are not valid
|
* @throws std::runtime_error if argument values are not valid
|
||||||
*/
|
*/
|
||||||
void validate() const {
|
void validate() const {
|
||||||
if (auto expected = maybe_nargs()) {
|
if (m_is_optional) {
|
||||||
if (m_is_optional) {
|
// TODO: check if an implicit value was programmed for this argument
|
||||||
// TODO: check if an implicit value was programmed for this argument
|
if (!m_is_used && !m_default_value.has_value() && m_is_required) {
|
||||||
if (!m_is_used && !m_default_value.has_value() && m_is_required) {
|
throw_required_arg_not_used_error();
|
||||||
std::stringstream stream;
|
}
|
||||||
stream << m_names[0] << ": required.";
|
if (m_is_used && m_is_required && m_values.empty()) {
|
||||||
throw std::runtime_error(stream.str());
|
throw_required_arg_no_value_provided_error();
|
||||||
}
|
}
|
||||||
if (m_is_used && m_is_required && m_values.empty()) {
|
} else {
|
||||||
std::stringstream stream;
|
if (!m_num_args_range.contains(m_values.size()) && !m_default_value.has_value()) {
|
||||||
stream << m_used_name << ": no value provided.";
|
throw_nargs_range_validation_error();
|
||||||
throw std::runtime_error(stream.str());
|
|
||||||
}
|
|
||||||
} else if (m_values.size() != expected && !m_default_value.has_value()) {
|
|
||||||
std::stringstream stream;
|
|
||||||
if (!m_used_name.empty()) {
|
|
||||||
stream << m_used_name << ": ";
|
|
||||||
}
|
|
||||||
stream << *expected << " argument(s) expected. " << m_values.size()
|
|
||||||
<< " provided.";
|
|
||||||
throw std::runtime_error(stream.str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto maybe_nargs() const -> std::optional<std::size_t> {
|
|
||||||
if (m_num_args < 0) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return static_cast<std::size_t>(m_num_args);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t get_arguments_length() const {
|
std::size_t get_arguments_length() const {
|
||||||
return std::accumulate(std::begin(m_names), std::end(m_names),
|
return std::accumulate(std::begin(m_names), std::end(m_names),
|
||||||
std::size_t(0), [](const auto &sum, const auto &s) {
|
std::size_t(0), [](const auto &sum, const auto &s) {
|
||||||
@ -600,6 +613,66 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
class NArgsRange {
|
||||||
|
std::size_t m_min;
|
||||||
|
std::size_t m_max;
|
||||||
|
|
||||||
|
public:
|
||||||
|
NArgsRange(std::size_t minimum, std::size_t maximum) : m_min(minimum), m_max(maximum) {
|
||||||
|
if (minimum > maximum)
|
||||||
|
throw std::logic_error("Range of number of arguments is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(std::size_t value) const {
|
||||||
|
return value >= m_min && value <= m_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_exact() const {
|
||||||
|
return m_min == m_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_right_bounded() const {
|
||||||
|
return m_max < std::numeric_limits<std::size_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t get_min() const {
|
||||||
|
return m_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t get_max() const {
|
||||||
|
return m_max;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void throw_nargs_range_validation_error() const {
|
||||||
|
std::stringstream stream;
|
||||||
|
if (!m_used_name.empty())
|
||||||
|
stream << m_used_name << ": ";
|
||||||
|
if (m_num_args_range.is_exact()) {
|
||||||
|
stream << m_num_args_range.get_min();
|
||||||
|
} else if (m_num_args_range.is_right_bounded()) {
|
||||||
|
stream << m_num_args_range.get_min() << " to " << m_num_args_range.get_max();
|
||||||
|
} else {
|
||||||
|
stream << m_num_args_range.get_min() << " or more";
|
||||||
|
}
|
||||||
|
stream << " argument(s) expected. "
|
||||||
|
<< m_values.size() << " provided.";
|
||||||
|
throw std::runtime_error(stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void throw_required_arg_not_used_error() const {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << m_names[0] << ": required.";
|
||||||
|
throw std::runtime_error(stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void throw_required_arg_no_value_provided_error() const {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << m_used_name << ": no value provided.";
|
||||||
|
throw std::runtime_error(stream.str());
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr int eof = std::char_traits<char>::eof();
|
static constexpr int eof = std::char_traits<char>::eof();
|
||||||
|
|
||||||
static auto lookahead(std::string_view s) -> int {
|
static auto lookahead(std::string_view s) -> int {
|
||||||
@ -789,6 +862,10 @@ private:
|
|||||||
}
|
}
|
||||||
if (m_default_value.has_value()) {
|
if (m_default_value.has_value()) {
|
||||||
return std::any_cast<T>(m_default_value);
|
return std::any_cast<T>(m_default_value);
|
||||||
|
} else {
|
||||||
|
if constexpr (details::IsContainer<T>)
|
||||||
|
if (!m_accepts_optional_like_value)
|
||||||
|
return any_cast_container<T>(m_values);
|
||||||
}
|
}
|
||||||
throw std::logic_error("No value provided for '" + m_names.back() + "'.");
|
throw std::logic_error("No value provided for '" + m_names.back() + "'.");
|
||||||
}
|
}
|
||||||
@ -834,7 +911,8 @@ private:
|
|||||||
std::in_place_type<valued_action>,
|
std::in_place_type<valued_action>,
|
||||||
[](const std::string &value) { return value; }};
|
[](const std::string &value) { return value; }};
|
||||||
std::vector<std::any> m_values;
|
std::vector<std::any> m_values;
|
||||||
int m_num_args = 1;
|
NArgsRange m_num_args_range {1, 1};
|
||||||
|
bool m_accepts_optional_like_value = false;
|
||||||
bool m_is_optional : true;
|
bool m_is_optional : true;
|
||||||
bool m_is_required : true;
|
bool m_is_required : true;
|
||||||
bool m_is_repeatable : true;
|
bool m_is_repeatable : true;
|
||||||
|
@ -121,12 +121,12 @@ TEST_CASE("Users can bind arguments to actions" * test_suite("actions")) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Users can use actions on remaining arguments" *
|
TEST_CASE("Users can use actions on nargs=ANY arguments" *
|
||||||
test_suite("actions")) {
|
test_suite("actions")) {
|
||||||
argparse::ArgumentParser program("sum");
|
argparse::ArgumentParser program("sum");
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
program.add_argument("all").remaining().action(
|
program.add_argument("all").nargs(argparse::nargs_pattern::any).action(
|
||||||
[](int &sum, std::string const &value) { sum += std::stoi(value); },
|
[](int &sum, std::string const &value) { sum += std::stoi(value); },
|
||||||
std::ref(result));
|
std::ref(result));
|
||||||
|
|
||||||
@ -134,6 +134,19 @@ TEST_CASE("Users can use actions on remaining arguments" *
|
|||||||
REQUIRE(result == 119);
|
REQUIRE(result == 119);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Users can use actions on remaining arguments" *
|
||||||
|
test_suite("actions")) {
|
||||||
|
argparse::ArgumentParser program("concat");
|
||||||
|
|
||||||
|
std::string result = "";
|
||||||
|
program.add_argument("all").remaining().action(
|
||||||
|
[](std::string &sum, const std::string &value) { sum += value; },
|
||||||
|
std::ref(result));
|
||||||
|
|
||||||
|
program.parse_args({"concat", "a", "-b", "-c", "--d"});
|
||||||
|
REQUIRE(result == "a-b-c--d");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Users can run actions on parameterless optional arguments" *
|
TEST_CASE("Users can run actions on parameterless optional arguments" *
|
||||||
test_suite("actions")) {
|
test_suite("actions")) {
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
|
@ -110,6 +110,86 @@ TEST_CASE("Parse optional arguments of many values" *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse 2 optional arguments of many values" *
|
||||||
|
test_suite("optional_arguments")) {
|
||||||
|
GIVEN("a program that accepts 2 optional arguments of many values") {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("-i").nargs(argparse::nargs_pattern::any).scan<'i', int>();
|
||||||
|
program.add_argument("-s").nargs(argparse::nargs_pattern::any);
|
||||||
|
|
||||||
|
WHEN("provided no argument") {
|
||||||
|
THEN("the program accepts it and gets empty container") {
|
||||||
|
REQUIRE_NOTHROW(program.parse_args({"test"}));
|
||||||
|
auto i = program.get<std::vector<int>>("-i");
|
||||||
|
REQUIRE(i.size() == 0);
|
||||||
|
|
||||||
|
auto s = program.get<std::vector<std::string>>("-s");
|
||||||
|
REQUIRE(s.size() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided 2 options with many arguments") {
|
||||||
|
program.parse_args(
|
||||||
|
{"test", "-i", "-42", "8", "100", "300", "-s", "ok", "this", "works"});
|
||||||
|
|
||||||
|
THEN("the optional parameter consumes each arguments") {
|
||||||
|
auto i = program.get<std::vector<int>>("-i");
|
||||||
|
REQUIRE(i.size() == 4);
|
||||||
|
REQUIRE(i[0] == -42);
|
||||||
|
REQUIRE(i[1] == 8);
|
||||||
|
REQUIRE(i[2] == 100);
|
||||||
|
REQUIRE(i[3] == 300);
|
||||||
|
|
||||||
|
auto s = program.get<std::vector<std::string>>("-s");
|
||||||
|
REQUIRE(s.size() == 3);
|
||||||
|
REQUIRE(s[0] == "ok");
|
||||||
|
REQUIRE(s[1] == "this");
|
||||||
|
REQUIRE(s[2] == "works");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse an optional argument of many values"
|
||||||
|
" and a positional argument of many values" *
|
||||||
|
test_suite("optional_arguments")) {
|
||||||
|
GIVEN("a program that accepts an optional argument of many values"
|
||||||
|
" and a positional argument of many values") {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("-s").nargs(argparse::nargs_pattern::any);
|
||||||
|
program.add_argument("input").nargs(argparse::nargs_pattern::any);
|
||||||
|
|
||||||
|
WHEN("provided no argument") {
|
||||||
|
program.parse_args({"test"});
|
||||||
|
THEN("the program accepts it and gets empty containers") {
|
||||||
|
auto s = program.get<std::vector<std::string>>("-s");
|
||||||
|
REQUIRE(s.size() == 0);
|
||||||
|
|
||||||
|
auto input = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(input.size() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided many arguments followed by an option with many arguments") {
|
||||||
|
program.parse_args(
|
||||||
|
{"test", "foo", "bar", "-s", "ok", "this", "works"});
|
||||||
|
|
||||||
|
THEN("the parameters consume each arguments") {
|
||||||
|
auto s = program.get<std::vector<std::string>>("-s");
|
||||||
|
REQUIRE(s.size() == 3);
|
||||||
|
REQUIRE(s[0] == "ok");
|
||||||
|
REQUIRE(s[1] == "this");
|
||||||
|
REQUIRE(s[2] == "works");
|
||||||
|
|
||||||
|
auto input = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(input.size() == 2);
|
||||||
|
REQUIRE(input[0] == "foo");
|
||||||
|
REQUIRE(input[1] == "bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Parse arguments of different types" *
|
TEST_CASE("Parse arguments of different types" *
|
||||||
test_suite("optional_arguments")) {
|
test_suite("optional_arguments")) {
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
@ -61,6 +61,130 @@ TEST_CASE("Parse positional arguments with optional arguments in the middle" *
|
|||||||
REQUIRE_THROWS(program.parse_args({ "test", "rocket.mesh", "thrust_profile.csv", "--num_iterations", "15", "output.mesh" }));
|
REQUIRE_THROWS(program.parse_args({ "test", "rocket.mesh", "thrust_profile.csv", "--num_iterations", "15", "output.mesh" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse positional nargs=1..2 arguments" *
|
||||||
|
test_suite("positional_arguments")) {
|
||||||
|
GIVEN("a program that accepts an optional argument and nargs=1..2 positional arguments") {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("-o");
|
||||||
|
program.add_argument("input").nargs(1, 2);
|
||||||
|
|
||||||
|
WHEN("provided no argument") {
|
||||||
|
THEN("the program does not accept it") {
|
||||||
|
REQUIRE_THROWS(program.parse_args({"test"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided 1 argument") {
|
||||||
|
THEN("the program accepts it") {
|
||||||
|
REQUIRE_NOTHROW(program.parse_args({"test", "a.c"}));
|
||||||
|
|
||||||
|
auto inputs = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(inputs.size() == 1);
|
||||||
|
REQUIRE(inputs[0] == "a.c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided 2 arguments") {
|
||||||
|
THEN("the program accepts it") {
|
||||||
|
REQUIRE_NOTHROW(program.parse_args({"test", "a.c", "b.c"}));
|
||||||
|
|
||||||
|
auto inputs = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(inputs.size() == 2);
|
||||||
|
REQUIRE(inputs[0] == "a.c");
|
||||||
|
REQUIRE(inputs[1] == "b.c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided 3 arguments") {
|
||||||
|
THEN("the program does not accept it") {
|
||||||
|
REQUIRE_THROWS(program.parse_args({"test", "a.c", "b.c", "main.c"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided an optional followed by positional arguments") {
|
||||||
|
program.parse_args({"test", "-o", "a.out", "a.c", "b.c"});
|
||||||
|
|
||||||
|
THEN("the optional parameter consumes an argument") {
|
||||||
|
using namespace std::literals;
|
||||||
|
REQUIRE(program["-o"] == "a.out"s);
|
||||||
|
|
||||||
|
auto inputs = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(inputs.size() == 2);
|
||||||
|
REQUIRE(inputs[0] == "a.c");
|
||||||
|
REQUIRE(inputs[1] == "b.c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided an optional preceded by positional arguments") {
|
||||||
|
program.parse_args({"test", "a.c", "b.c", "-o", "a.out"});
|
||||||
|
|
||||||
|
THEN("the optional parameter consumes an argument") {
|
||||||
|
using namespace std::literals;
|
||||||
|
REQUIRE(program["-o"] == "a.out"s);
|
||||||
|
|
||||||
|
auto inputs = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(inputs.size() == 2);
|
||||||
|
REQUIRE(inputs[0] == "a.c");
|
||||||
|
REQUIRE(inputs[1] == "b.c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided an optional in between positional arguments") {
|
||||||
|
THEN("the program does not accept it") {
|
||||||
|
REQUIRE_THROWS(program.parse_args({"test", "a.c", "-o", "a.out", "b.c"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse positional nargs=ANY arguments" *
|
||||||
|
test_suite("positional_arguments")) {
|
||||||
|
GIVEN("a program that accepts an optional argument and nargs=ANY positional arguments") {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("-o");
|
||||||
|
program.add_argument("input").nargs(argparse::nargs_pattern::any);
|
||||||
|
|
||||||
|
WHEN("provided no argument") {
|
||||||
|
THEN("the program accepts it and gets empty container") {
|
||||||
|
REQUIRE_NOTHROW(program.parse_args({"test"}));
|
||||||
|
|
||||||
|
auto inputs = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(inputs.size() == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided an optional followed by positional arguments") {
|
||||||
|
program.parse_args({"test", "-o", "a.out", "a.c", "b.c", "main.c"});
|
||||||
|
|
||||||
|
THEN("the optional parameter consumes an argument") {
|
||||||
|
using namespace std::literals;
|
||||||
|
REQUIRE(program["-o"] == "a.out"s);
|
||||||
|
|
||||||
|
auto inputs = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(inputs.size() == 3);
|
||||||
|
REQUIRE(inputs[0] == "a.c");
|
||||||
|
REQUIRE(inputs[1] == "b.c");
|
||||||
|
REQUIRE(inputs[2] == "main.c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("provided an optional preceded by positional arguments") {
|
||||||
|
program.parse_args({"test", "a.c", "b.c", "main.c", "-o", "a.out"});
|
||||||
|
|
||||||
|
THEN("the optional parameter consumes an argument") {
|
||||||
|
using namespace std::literals;
|
||||||
|
REQUIRE(program["-o"] == "a.out"s);
|
||||||
|
|
||||||
|
auto inputs = program.get<std::vector<std::string>>("input");
|
||||||
|
REQUIRE(inputs.size() == 3);
|
||||||
|
REQUIRE(inputs[0] == "a.c");
|
||||||
|
REQUIRE(inputs[1] == "b.c");
|
||||||
|
REQUIRE(inputs[2] == "main.c");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Parse remaining arguments deemed positional" *
|
TEST_CASE("Parse remaining arguments deemed positional" *
|
||||||
test_suite("positional_arguments")) {
|
test_suite("positional_arguments")) {
|
||||||
GIVEN("a program that accepts an optional argument and remaining arguments") {
|
GIVEN("a program that accepts an optional argument and remaining arguments") {
|
||||||
@ -109,10 +233,10 @@ TEST_CASE("Parse remaining arguments deemed positional" *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Negative nargs is not allowed" *
|
TEST_CASE("Reversed order nargs is not allowed" *
|
||||||
test_suite("positional_arguments")) {
|
test_suite("positional_arguments")) {
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
REQUIRE_THROWS_AS(program.add_argument("output").nargs(-1), std::logic_error);
|
REQUIRE_THROWS_AS(program.add_argument("output").nargs(2, 1), std::logic_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Square a number" * test_suite("positional_arguments")) {
|
TEST_CASE("Square a number" * test_suite("positional_arguments")) {
|
||||||
|
Loading…
Reference in New Issue
Block a user