mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-03 22:54:39 +00:00
Closes #278
This commit is contained in:
parent
d28188f4d5
commit
5e7ce61ca7
@ -233,8 +233,14 @@ template <class T, auto Param = 0> struct parse_number {
|
||||
|
||||
template <class T> struct parse_number<T, radix_16> {
|
||||
auto operator()(std::string_view s) -> T {
|
||||
if (auto [ok, rest] = consume_hex_prefix(s); ok) {
|
||||
return do_from_chars<T, radix_16>(rest);
|
||||
if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
|
||||
if (auto [ok, rest] = consume_hex_prefix(s); ok) {
|
||||
return do_from_chars<T, radix_16>(rest);
|
||||
}
|
||||
} else {
|
||||
// Allow passing hex numbers without prefix
|
||||
// Shape 'x' already has to be specified
|
||||
return do_from_chars<T, radix_16>(s);
|
||||
}
|
||||
throw std::invalid_argument{"pattern not found"};
|
||||
}
|
||||
@ -350,24 +356,22 @@ std::string join(StrIt first, StrIt last, const std::string &separator) {
|
||||
return value.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct can_invoke_to_string {
|
||||
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{});
|
||||
static auto test(int)
|
||||
-> decltype(std::to_string(std::declval<U>()), std::true_type{});
|
||||
|
||||
template <typename U>
|
||||
static auto test(...) -> std::false_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;
|
||||
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
|
||||
@ -432,8 +436,7 @@ public:
|
||||
|
||||
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) {
|
||||
} else if constexpr (details::can_invoke_to_string<T>::value) {
|
||||
m_default_value_str = std::to_string(value);
|
||||
}
|
||||
|
||||
@ -554,18 +557,20 @@ public:
|
||||
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");
|
||||
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::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)));
|
||||
}
|
||||
}
|
||||
@ -578,7 +583,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename T, typename... U>
|
||||
Argument &choices(T&& first, U&&...rest) {
|
||||
Argument &choices(T &&first, U &&...rest) {
|
||||
add_choice(std::forward<T>(first));
|
||||
choices(std::forward<U>(rest)...);
|
||||
return *this;
|
||||
@ -1181,7 +1186,8 @@ private:
|
||||
std::string m_metavar;
|
||||
std::any m_default_value;
|
||||
std::string m_default_value_repr;
|
||||
std::optional<std::string> m_default_value_str; // used for checking default_value against choices
|
||||
std::optional<std::string>
|
||||
m_default_value_str; // used for checking default_value against choices
|
||||
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 &)>;
|
||||
|
@ -69,18 +69,18 @@ TEST_CASE("Parse multiple arguments one of which is not in the fixed number of "
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Parse multiple arguments that are in the fixed number of allowed INTEGER choices" *
|
||||
test_suite("choices")) {
|
||||
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")) {
|
||||
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);
|
||||
|
||||
|
@ -24,14 +24,16 @@ TEST_CASE("Use a 'string' default value" * test_suite("default_value")) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Use a default value with flag arguments" * test_suite("default_value")) {
|
||||
TEST_CASE("Use a default value with flag arguments" *
|
||||
test_suite("default_value")) {
|
||||
|
||||
argparse::ArgumentParser program("test");
|
||||
argparse::ArgumentParser program("test");
|
||||
|
||||
program.add_argument("-inc_chr", "--include_chromes")
|
||||
.help(std::string{"only process the anchor whose one of the end is contained on the specified "
|
||||
"chromatin, used ',' to split."})
|
||||
.default_value("all");
|
||||
.help(std::string{"only process the anchor whose one of the end is "
|
||||
"contained on the specified "
|
||||
"chromatin, used ',' to split."})
|
||||
.default_value("all");
|
||||
|
||||
program.add_argument("-l").default_value(false).implicit_value(true);
|
||||
program.add_argument("-o").default_value(false).implicit_value(true);
|
||||
@ -73,7 +75,8 @@ TEST_CASE("Position of the argument with default value") {
|
||||
}
|
||||
|
||||
SUBCASE("Arg with default value replaces the value if given") {
|
||||
REQUIRE_NOTHROW(program.parse_args({"test", "-g", "a_different_value", "-s", "./src"}));
|
||||
REQUIRE_NOTHROW(
|
||||
program.parse_args({"test", "-g", "a_different_value", "-s", "./src"}));
|
||||
REQUIRE(program.get("-g") == std::string("a_different_value"));
|
||||
REQUIRE(program.get("-s") == std::string("./src"));
|
||||
}
|
||||
|
@ -112,6 +112,39 @@ TEST_CASE_TEMPLATE("Parse a hexadecimal integer argument" * test_suite("scan"),
|
||||
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "0XFFFFFFFFFFFFFFFF1"}),
|
||||
std::range_error);
|
||||
}
|
||||
|
||||
SUBCASE("with hex digit without prefix") {
|
||||
program.parse_args({"test", "-n", "1a"});
|
||||
REQUIRE(program.get<T>("-n") == 0x1a);
|
||||
}
|
||||
|
||||
SUBCASE("minus sign without prefix produces an optional argument") {
|
||||
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "-1"}),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
SUBCASE("plus sign without prefix is not allowed") {
|
||||
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "+1a"}),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
SUBCASE("without prefix does not fit") {
|
||||
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "FFFFFFFFFFFFFFFF1"}),
|
||||
std::range_error);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Parse multiple hex numbers without prefix" * test_suite("scan")) {
|
||||
argparse::ArgumentParser program("test");
|
||||
program.add_argument("-x", "--hex")
|
||||
.help("bytes in hex separated by spaces")
|
||||
.nargs(1, std::numeric_limits<std::size_t>::max())
|
||||
.scan<'x', uint8_t>();
|
||||
|
||||
REQUIRE_NOTHROW(
|
||||
program.parse_args({"test", "-x", "f2", "b2", "10", "80", "64"}));
|
||||
const auto &input_bytes = program.get<std::vector<uint8_t>>("-x");
|
||||
REQUIRE((input_bytes == std::vector<uint8_t>{0xf2, 0xb2, 0x10, 0x80, 0x64}));
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("Parse integer argument of any format" * test_suite("scan"),
|
||||
|
Loading…
Reference in New Issue
Block a user