mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-03 22:54:39 +00:00
commit
b10afeb50c
37
README.md
37
README.md
@ -1060,13 +1060,7 @@ argparse::ArgumentParser program("test");
|
|||||||
|
|
||||||
program.add_argument("input")
|
program.add_argument("input")
|
||||||
.default_value(std::string{"baz"})
|
.default_value(std::string{"baz"})
|
||||||
.action([](const std::string& value) {
|
.choices("foo", "bar", "baz");
|
||||||
static const std::vector<std::string> choices = { "foo", "bar", "baz" };
|
|
||||||
if (std::find(choices.begin(), choices.end(), value) != choices.end()) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return std::string{ "baz" };
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
program.parse_args(argc, argv);
|
program.parse_args(argc, argv);
|
||||||
@ -1083,7 +1077,34 @@ std::cout << input << std::endl;
|
|||||||
|
|
||||||
```console
|
```console
|
||||||
foo@bar:/home/dev/$ ./main fex
|
foo@bar:/home/dev/$ ./main fex
|
||||||
baz
|
Invalid argument "fex" - allowed options: {foo, bar, baz}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using choices also works with integer types, e.g.,
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
|
||||||
|
program.add_argument("input")
|
||||||
|
.default_value(0)
|
||||||
|
.choices(0, 1, 2, 3, 4, 5);
|
||||||
|
|
||||||
|
try {
|
||||||
|
program.parse_args(argc, argv);
|
||||||
|
}
|
||||||
|
catch (const std::exception& err) {
|
||||||
|
std::cerr << err.what() << std::endl;
|
||||||
|
std::cerr << program;
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto input = program.get("input");
|
||||||
|
std::cout << input << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
foo@bar:/home/dev/$ ./main 6
|
||||||
|
Invalid argument "6" - allowed options: {0, 1, 2, 3, 4, 5}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using `option=value` syntax
|
## Using `option=value` syntax
|
||||||
|
@ -350,6 +350,26 @@ std::string join(StrIt first, StrIt last, const std::string &separator) {
|
|||||||
return value.str();
|
return value.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct can_invoke_to_string {
|
||||||
|
template <typename U>
|
||||||
|
static auto test(int) -> decltype(std::to_string(std::declval<U>()), std::true_type{});
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static auto test(...) -> std::false_type;
|
||||||
|
|
||||||
|
static constexpr bool value = decltype(test<T>(0))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct IsChoiceTypeSupported {
|
||||||
|
using CleanType = typename std::decay<T>::type;
|
||||||
|
static const bool value = std::is_integral<CleanType>::value ||
|
||||||
|
std::is_same<CleanType, std::string>::value ||
|
||||||
|
std::is_same<CleanType, std::string_view>::value ||
|
||||||
|
std::is_same<CleanType, const char*>::value;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
|
|
||||||
enum class nargs_pattern { optional, any, at_least_one };
|
enum class nargs_pattern { optional, any, at_least_one };
|
||||||
@ -408,6 +428,14 @@ public:
|
|||||||
|
|
||||||
template <typename T> Argument &default_value(T &&value) {
|
template <typename T> Argument &default_value(T &&value) {
|
||||||
m_default_value_repr = details::repr(value);
|
m_default_value_repr = details::repr(value);
|
||||||
|
|
||||||
|
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
||||||
|
m_default_value_str = std::string{std::string_view{value}};
|
||||||
|
}
|
||||||
|
else if constexpr (details::can_invoke_to_string<T>::value) {
|
||||||
|
m_default_value_str = std::to_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
m_default_value = std::forward<T>(value);
|
m_default_value = std::forward<T>(value);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -428,7 +456,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class F, class... Args>
|
template <class F, class... Args>
|
||||||
auto action(F &&callable, Args &&... bound_args)
|
auto action(F &&callable, Args &&...bound_args)
|
||||||
-> std::enable_if_t<std::is_invocable_v<F, Args..., std::string const>,
|
-> std::enable_if_t<std::is_invocable_v<F, Args..., std::string const>,
|
||||||
Argument &> {
|
Argument &> {
|
||||||
using action_type = std::conditional_t<
|
using action_type = std::conditional_t<
|
||||||
@ -509,10 +537,12 @@ public:
|
|||||||
m_num_args_range = NArgsRange{0, 1};
|
m_num_args_range = NArgsRange{0, 1};
|
||||||
break;
|
break;
|
||||||
case nargs_pattern::any:
|
case nargs_pattern::any:
|
||||||
m_num_args_range = NArgsRange{0, (std::numeric_limits<std::size_t>::max)()};
|
m_num_args_range =
|
||||||
|
NArgsRange{0, (std::numeric_limits<std::size_t>::max)()};
|
||||||
break;
|
break;
|
||||||
case nargs_pattern::at_least_one:
|
case nargs_pattern::at_least_one:
|
||||||
m_num_args_range = NArgsRange{1, (std::numeric_limits<std::size_t>::max)()};
|
m_num_args_range =
|
||||||
|
NArgsRange{1, (std::numeric_limits<std::size_t>::max)()};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
@ -523,6 +553,80 @@ public:
|
|||||||
return nargs(nargs_pattern::any);
|
return nargs(nargs_pattern::any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void add_choice(T&& choice) {
|
||||||
|
static_assert(details::IsChoiceTypeSupported<T>::value, "Only string or integer type supported for choice");
|
||||||
|
static_assert(std::is_convertible_v<T, std::string_view> || details::can_invoke_to_string<T>::value, "Choice is not convertible to string_type");
|
||||||
|
if (!m_choices.has_value()) {
|
||||||
|
m_choices = std::vector<std::string>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
||||||
|
m_choices.value().push_back(std::string{std::string_view{std::forward<T>(choice)}});
|
||||||
|
}
|
||||||
|
else if constexpr (details::can_invoke_to_string<T>::value) {
|
||||||
|
m_choices.value().push_back(std::to_string(std::forward<T>(choice)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Argument &choices() {
|
||||||
|
if (!m_choices.has_value()) {
|
||||||
|
throw std::runtime_error("Zero choices provided");
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... U>
|
||||||
|
Argument &choices(T&& first, U&&...rest) {
|
||||||
|
add_choice(std::forward<T>(first));
|
||||||
|
choices(std::forward<U>(rest)...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void find_default_value_in_choices_or_throw() const {
|
||||||
|
|
||||||
|
const auto &choices = m_choices.value();
|
||||||
|
|
||||||
|
if (m_default_value.has_value()) {
|
||||||
|
if (std::find(choices.begin(), choices.end(), m_default_value_str) ==
|
||||||
|
choices.end()) {
|
||||||
|
// provided arg not in list of allowed choices
|
||||||
|
// report error
|
||||||
|
|
||||||
|
std::string choices_as_csv =
|
||||||
|
std::accumulate(choices.begin(), choices.end(), std::string(),
|
||||||
|
[](const std::string &a, const std::string &b) {
|
||||||
|
return a + (a.empty() ? "" : ", ") + b;
|
||||||
|
});
|
||||||
|
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string{"Invalid default value "} + m_default_value_repr +
|
||||||
|
" - allowed options: {" + choices_as_csv + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iterator>
|
||||||
|
void find_value_in_choices_or_throw(Iterator it) const {
|
||||||
|
|
||||||
|
const auto &choices = m_choices.value();
|
||||||
|
|
||||||
|
if (std::find(choices.begin(), choices.end(), *it) == choices.end()) {
|
||||||
|
// provided arg not in list of allowed choices
|
||||||
|
// report error
|
||||||
|
|
||||||
|
std::string choices_as_csv =
|
||||||
|
std::accumulate(choices.begin(), choices.end(), std::string(),
|
||||||
|
[](const std::string &a, const std::string &b) {
|
||||||
|
return a + (a.empty() ? "" : ", ") + b;
|
||||||
|
});
|
||||||
|
|
||||||
|
throw std::runtime_error(std::string{"Invalid argument "} +
|
||||||
|
details::repr(*it) + " - allowed options: {" +
|
||||||
|
choices_as_csv + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
Iterator consume(Iterator start, Iterator end,
|
Iterator consume(Iterator start, Iterator end,
|
||||||
std::string_view used_name = {}) {
|
std::string_view used_name = {}) {
|
||||||
@ -532,6 +636,14 @@ public:
|
|||||||
m_is_used = true;
|
m_is_used = true;
|
||||||
m_used_name = used_name;
|
m_used_name = used_name;
|
||||||
|
|
||||||
|
if (m_choices.has_value()) {
|
||||||
|
// Check each value in (start, end) and make sure
|
||||||
|
// it is in the list of allowed choices/options
|
||||||
|
for (auto it = start; it != end; ++it) {
|
||||||
|
find_value_in_choices_or_throw(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const auto num_args_max = m_num_args_range.get_max();
|
const auto num_args_max = m_num_args_range.get_max();
|
||||||
const auto num_args_min = m_num_args_range.get_min();
|
const auto num_args_min = m_num_args_range.get_min();
|
||||||
std::size_t dist = 0;
|
std::size_t dist = 0;
|
||||||
@ -602,6 +714,12 @@ public:
|
|||||||
throw_nargs_range_validation_error();
|
throw_nargs_range_validation_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_choices.has_value()) {
|
||||||
|
// Make sure the default value (if provided)
|
||||||
|
// is in the list of choices
|
||||||
|
find_default_value_in_choices_or_throw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_inline_usage() const {
|
std::string get_inline_usage() const {
|
||||||
@ -738,8 +856,7 @@ public:
|
|||||||
using ValueType = typename T::value_type;
|
using ValueType = typename T::value_type;
|
||||||
auto lhs = get<T>();
|
auto lhs = get<T>();
|
||||||
return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
|
return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
|
||||||
std::end(rhs),
|
std::end(rhs), [](const auto &a, const auto &b) {
|
||||||
[](const auto &a, const auto &b) {
|
|
||||||
return std::any_cast<const ValueType &>(a) == b;
|
return std::any_cast<const ValueType &>(a) == b;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1063,7 +1180,9 @@ private:
|
|||||||
std::string m_metavar;
|
std::string m_metavar;
|
||||||
std::any m_default_value;
|
std::any m_default_value;
|
||||||
std::string m_default_value_repr;
|
std::string m_default_value_repr;
|
||||||
|
std::optional<std::string> m_default_value_str; // used for checking default_value against choices
|
||||||
std::any m_implicit_value;
|
std::any m_implicit_value;
|
||||||
|
std::optional<std::vector<std::string>> m_choices{std::nullopt};
|
||||||
using valued_action = std::function<std::any(const std::string &)>;
|
using valued_action = std::function<std::any(const std::string &)>;
|
||||||
using void_action = std::function<void(const std::string &)>;
|
using void_action = std::function<void(const std::string &)>;
|
||||||
std::variant<valued_action, void_action> m_action{
|
std::variant<valued_action, void_action> m_action{
|
||||||
@ -1152,16 +1271,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
auto arg_used = std::any_of(m_argument_map.cbegin(),
|
auto arg_used = std::any_of(m_argument_map.cbegin(), m_argument_map.cend(),
|
||||||
m_argument_map.cend(),
|
[](auto &it) { return it.second->m_is_used; });
|
||||||
[](auto &it) {
|
auto subparser_used =
|
||||||
return it.second->m_is_used;
|
std::any_of(m_subparser_used.cbegin(), m_subparser_used.cend(),
|
||||||
});
|
[](auto &it) { return it.second; });
|
||||||
auto subparser_used = std::any_of(m_subparser_used.cbegin(),
|
|
||||||
m_subparser_used.cend(),
|
|
||||||
[](auto &it) {
|
|
||||||
return it.second;
|
|
||||||
});
|
|
||||||
|
|
||||||
return m_is_parsed && (arg_used || subparser_used);
|
return m_is_parsed && (arg_used || subparser_used);
|
||||||
}
|
}
|
||||||
@ -1186,7 +1300,7 @@ public:
|
|||||||
// Parameter packed add_parents method
|
// Parameter packed add_parents method
|
||||||
// Accepts a variadic number of ArgumentParser objects
|
// Accepts a variadic number of ArgumentParser objects
|
||||||
template <typename... Targs>
|
template <typename... Targs>
|
||||||
ArgumentParser &add_parents(const Targs &... f_args) {
|
ArgumentParser &add_parents(const Targs &...f_args) {
|
||||||
for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) {
|
for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) {
|
||||||
for (const auto &argument : parent_parser.m_positional_arguments) {
|
for (const auto &argument : parent_parser.m_positional_arguments) {
|
||||||
auto it = m_positional_arguments.insert(
|
auto it = m_positional_arguments.insert(
|
||||||
@ -1215,8 +1329,7 @@ public:
|
|||||||
/* Getter for arguments and subparsers.
|
/* Getter for arguments and subparsers.
|
||||||
* @throws std::logic_error in case of an invalid argument or subparser name
|
* @throws std::logic_error in case of an invalid argument or subparser name
|
||||||
*/
|
*/
|
||||||
template <typename T = Argument>
|
template <typename T = Argument> T &at(std::string_view name) {
|
||||||
T& at(std::string_view name) {
|
|
||||||
if constexpr (std::is_same_v<T, Argument>) {
|
if constexpr (std::is_same_v<T, Argument>) {
|
||||||
return (*this)[name];
|
return (*this)[name];
|
||||||
} else {
|
} else {
|
||||||
@ -1692,7 +1805,8 @@ private:
|
|||||||
}
|
}
|
||||||
std::size_t max_size = 0;
|
std::size_t max_size = 0;
|
||||||
for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
|
for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
|
||||||
max_size = std::max<std::size_t>(max_size, argument->get_arguments_length());
|
max_size =
|
||||||
|
std::max<std::size_t>(max_size, argument->get_arguments_length());
|
||||||
}
|
}
|
||||||
for ([[maybe_unused]] const auto &[command, unused] : m_subparser_map) {
|
for ([[maybe_unused]] const auto &[command, unused] : m_subparser_map) {
|
||||||
max_size = std::max<std::size_t>(max_size, command.size());
|
max_size = std::max<std::size_t>(max_size, command.size());
|
||||||
|
@ -29,6 +29,7 @@ file(GLOB ARGPARSE_TEST_SOURCES
|
|||||||
test_append.cpp
|
test_append.cpp
|
||||||
test_as_container.cpp
|
test_as_container.cpp
|
||||||
test_bool_operator.cpp
|
test_bool_operator.cpp
|
||||||
|
test_choices.cpp
|
||||||
test_compound_arguments.cpp
|
test_compound_arguments.cpp
|
||||||
test_container_arguments.cpp
|
test_container_arguments.cpp
|
||||||
test_const_correct.cpp
|
test_const_correct.cpp
|
||||||
|
@ -5,8 +5,8 @@ import argparse;
|
|||||||
#endif
|
#endif
|
||||||
#include <doctest.hpp>
|
#include <doctest.hpp>
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ TEST_CASE("Get argument with .at()" * test_suite("as_container")) {
|
|||||||
|
|
||||||
SUBCASE("with unknown argument") {
|
SUBCASE("with unknown argument") {
|
||||||
program.parse_args({"test"});
|
program.parse_args({"test"});
|
||||||
REQUIRE_THROWS_WITH_AS(program.at("--folder"),
|
REQUIRE_THROWS_WITH_AS(program.at("--folder"), "No such argument: --folder",
|
||||||
"No such argument: --folder", std::logic_error);
|
std::logic_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,8 @@ TEST_CASE("Get subparser with .at()" * test_suite("as_container")) {
|
|||||||
SUBCASE("and its argument") {
|
SUBCASE("and its argument") {
|
||||||
program.parse_args({"test", "walk", "4km/h"});
|
program.parse_args({"test", "walk", "4km/h"});
|
||||||
REQUIRE(&(program.at<argparse::ArgumentParser>("walk")) == &walk_cmd);
|
REQUIRE(&(program.at<argparse::ArgumentParser>("walk")) == &walk_cmd);
|
||||||
REQUIRE(&(program.at<argparse::ArgumentParser>("walk").at("speed")) == &speed);
|
REQUIRE(&(program.at<argparse::ArgumentParser>("walk").at("speed")) ==
|
||||||
|
&speed);
|
||||||
REQUIRE(program.at<argparse::ArgumentParser>("walk").is_used("speed"));
|
REQUIRE(program.at<argparse::ArgumentParser>("walk").is_used("speed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,7 @@ import argparse;
|
|||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
|
||||||
TEST_CASE("ArgumentParser in bool context" *
|
TEST_CASE("ArgumentParser in bool context" * test_suite("argument_parser")) {
|
||||||
test_suite("argument_parser")) {
|
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
program.add_argument("cases").remaining();
|
program.add_argument("cases").remaining();
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ TEST_CASE("With subparsers in bool context" * test_suite("argument_parser")) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Parsers remain false with unknown arguments" *
|
TEST_CASE("Parsers remain false with unknown arguments" *
|
||||||
test_suite("argument_parser")) {
|
test_suite("argument_parser")) {
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
|
|
||||||
argparse::ArgumentParser cmd_build("build");
|
argparse::ArgumentParser cmd_build("build");
|
||||||
@ -59,7 +58,7 @@ TEST_CASE("Parsers remain false with unknown arguments" *
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Multi-level parsers match subparser bool" *
|
TEST_CASE("Multi-level parsers match subparser bool" *
|
||||||
test_suite("argument_parser")) {
|
test_suite("argument_parser")) {
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
|
|
||||||
argparse::ArgumentParser cmd_cook("cook");
|
argparse::ArgumentParser cmd_cook("cook");
|
||||||
|
91
test/test_choices.cpp
Normal file
91
test/test_choices.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#ifdef WITH_MODULE
|
||||||
|
import argparse;
|
||||||
|
#else
|
||||||
|
#include <argparse/argparse.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <doctest.hpp>
|
||||||
|
|
||||||
|
using doctest::test_suite;
|
||||||
|
|
||||||
|
TEST_CASE("Parse argument that is provided zero choices" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
REQUIRE_THROWS_WITH_AS(program.add_argument("color").choices(),
|
||||||
|
"Zero choices provided", std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse argument that is in the fixed number of allowed choices" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("color").choices("red", "green", "blue");
|
||||||
|
|
||||||
|
program.parse_args({"test", "red"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse argument that is in the fixed number of allowed choices, with "
|
||||||
|
"invalid default" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("color").default_value("yellow").choices("red", "green",
|
||||||
|
"blue");
|
||||||
|
|
||||||
|
REQUIRE_THROWS_WITH_AS(
|
||||||
|
program.parse_args({"test"}),
|
||||||
|
"Invalid default value \"yellow\" - allowed options: {red, green, blue}",
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse invalid argument that is not in the fixed number of allowed "
|
||||||
|
"choices" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("color").choices("red", "green", "blue");
|
||||||
|
|
||||||
|
REQUIRE_THROWS_WITH_AS(
|
||||||
|
program.parse_args({"test", "red2"}),
|
||||||
|
"Invalid argument \"red2\" - allowed options: {red, green, blue}",
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Parse multiple arguments that are in the fixed number of allowed choices" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("color").nargs(2).choices("red", "green", "blue");
|
||||||
|
|
||||||
|
program.parse_args({"test", "red", "green"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse multiple arguments one of which is not in the fixed number of "
|
||||||
|
"allowed choices" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("color").nargs(2).choices("red", "green", "blue");
|
||||||
|
|
||||||
|
REQUIRE_THROWS_WITH_AS(
|
||||||
|
program.parse_args({"test", "red", "green2"}),
|
||||||
|
"Invalid argument \"green2\" - allowed options: {red, green, blue}",
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Parse multiple arguments that are in the fixed number of allowed INTEGER choices" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("indices").nargs(2).choices(1, 2, 3, 4, 5);
|
||||||
|
|
||||||
|
program.parse_args({"test", "1", "2"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(
|
||||||
|
"Parse multiple arguments that are not in fixed number of allowed INTEGER choices" *
|
||||||
|
test_suite("choices")) {
|
||||||
|
argparse::ArgumentParser program("test");
|
||||||
|
program.add_argument("indices").nargs(2).choices(1, 2, 3, 4, 5);
|
||||||
|
|
||||||
|
REQUIRE_THROWS_WITH_AS(
|
||||||
|
program.parse_args({"test", "6", "7"}),
|
||||||
|
"Invalid argument \"6\" - allowed options: {1, 2, 3, 4, 5}",
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
@ -5,9 +5,9 @@ import argparse;
|
|||||||
#endif
|
#endif
|
||||||
#include <doctest.hpp>
|
#include <doctest.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ TEST_CASE("Do not exit on default arguments" * test_suite("default_args")) {
|
|||||||
argparse::ArgumentParser parser("test", "1.0",
|
argparse::ArgumentParser parser("test", "1.0",
|
||||||
argparse::default_arguments::all, false);
|
argparse::default_arguments::all, false);
|
||||||
std::stringstream buf;
|
std::stringstream buf;
|
||||||
std::streambuf* saved_cout_buf = std::cout.rdbuf(buf.rdbuf());
|
std::streambuf *saved_cout_buf = std::cout.rdbuf(buf.rdbuf());
|
||||||
parser.parse_args({"test", "--help"});
|
parser.parse_args({"test", "--help"});
|
||||||
std::cout.rdbuf(saved_cout_buf);
|
std::cout.rdbuf(saved_cout_buf);
|
||||||
REQUIRE(parser.is_used("--help"));
|
REQUIRE(parser.is_used("--help"));
|
||||||
|
@ -6,8 +6,8 @@ import argparse;
|
|||||||
#include <doctest.hpp>
|
#include <doctest.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ TEST_CASE("Implicit argument" * test_suite("ArgumentParser::get")) {
|
|||||||
|
|
||||||
TEST_CASE("Mismatched type for argument" * test_suite("ArgumentParser::get")) {
|
TEST_CASE("Mismatched type for argument" * test_suite("ArgumentParser::get")) {
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
program.add_argument("-s", "--stuff"); // as default type, a std::string
|
program.add_argument("-s", "--stuff"); // as default type, a std::string
|
||||||
REQUIRE_NOTHROW(program.parse_args({"test", "-s", "321"}));
|
REQUIRE_NOTHROW(program.parse_args({"test", "-s", "321"}));
|
||||||
REQUIRE_THROWS_AS(program.get<int>("--stuff"), std::bad_any_cast);
|
REQUIRE_THROWS_AS(program.get<int>("--stuff"), std::bad_any_cast);
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@ import argparse;
|
|||||||
#endif
|
#endif
|
||||||
#include <doctest.hpp>
|
#include <doctest.hpp>
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
|
||||||
@ -82,22 +82,19 @@ TEST_CASE("Users can replace default -h/--help" * test_suite("help")) {
|
|||||||
|
|
||||||
TEST_CASE("Multiline help message alignment") {
|
TEST_CASE("Multiline help message alignment") {
|
||||||
// '#' is used at the beginning of each help message line to simplify testing.
|
// '#' is used at the beginning of each help message line to simplify testing.
|
||||||
// It is important to ensure that this character doesn't appear elsewhere in the test case.
|
// It is important to ensure that this character doesn't appear elsewhere in
|
||||||
// Default arguments (e.g., -h/--help, -v/--version) are not included in this test.
|
// the test case. Default arguments (e.g., -h/--help, -v/--version) are not
|
||||||
|
// included in this test.
|
||||||
argparse::ArgumentParser program("program");
|
argparse::ArgumentParser program("program");
|
||||||
program.add_argument("INPUT1")
|
program.add_argument("INPUT1").help(
|
||||||
.help(
|
"#This is the first line of help message.\n"
|
||||||
"#This is the first line of help message.\n"
|
"#And this is the second line of help message.");
|
||||||
"#And this is the second line of help message."
|
program.add_argument("program_input2").help("#There is only one line.");
|
||||||
);
|
|
||||||
program.add_argument("program_input2")
|
|
||||||
.help("#There is only one line.");
|
|
||||||
program.add_argument("-p", "--prog_input3")
|
program.add_argument("-p", "--prog_input3")
|
||||||
.help(
|
.help(
|
||||||
R"(#Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
R"(#Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
#Sed ut perspiciatis unde omnis iste natus error sit voluptatem
|
#Sed ut perspiciatis unde omnis iste natus error sit voluptatem
|
||||||
#accusantium doloremque laudantium, totam rem aperiam...)"
|
#accusantium doloremque laudantium, totam rem aperiam...)");
|
||||||
);
|
|
||||||
program.add_argument("--verbose").default_value(false).implicit_value(true);
|
program.add_argument("--verbose").default_value(false).implicit_value(true);
|
||||||
|
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
@ -107,7 +104,8 @@ R"(#Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|||||||
auto help_message_start = std::string::npos;
|
auto help_message_start = std::string::npos;
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(iss, line)) {
|
while (std::getline(iss, line)) {
|
||||||
// Find the position of '#', which indicates the start of the help message line
|
// Find the position of '#', which indicates the start of the help message
|
||||||
|
// line
|
||||||
auto pos = line.find('#');
|
auto pos = line.find('#');
|
||||||
|
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) {
|
||||||
|
@ -5,8 +5,8 @@ import argparse;
|
|||||||
#endif
|
#endif
|
||||||
#include <doctest.hpp>
|
#include <doctest.hpp>
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ import argparse.details;
|
|||||||
#endif
|
#endif
|
||||||
#include <doctest.hpp>
|
#include <doctest.hpp>
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
@ -6,8 +6,8 @@ import argparse;
|
|||||||
#include <doctest.hpp>
|
#include <doctest.hpp>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
using doctest::test_suite;
|
using doctest::test_suite;
|
||||||
|
|
||||||
@ -213,8 +213,8 @@ TEST_CASE("Check is_subcommand_used after parse" * test_suite("subparsers")) {
|
|||||||
|
|
||||||
argparse::ArgumentParser command_2("clean");
|
argparse::ArgumentParser command_2("clean");
|
||||||
command_2.add_argument("--fullclean")
|
command_2.add_argument("--fullclean")
|
||||||
.default_value(false)
|
.default_value(false)
|
||||||
.implicit_value(true);
|
.implicit_value(true);
|
||||||
|
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
program.add_subparser(command_1);
|
program.add_subparser(command_1);
|
||||||
|
Loading…
Reference in New Issue
Block a user