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> {
|
template <class T> struct parse_number<T, radix_16> {
|
||||||
auto operator()(std::string_view s) -> T {
|
auto operator()(std::string_view s) -> T {
|
||||||
if (auto [ok, rest] = consume_hex_prefix(s); ok) {
|
if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
|
||||||
return do_from_chars<T, radix_16>(rest);
|
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"};
|
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();
|
return value.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> struct can_invoke_to_string {
|
||||||
struct can_invoke_to_string {
|
|
||||||
template <typename U>
|
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;
|
static constexpr bool value = decltype(test<T>(0))::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> struct IsChoiceTypeSupported {
|
||||||
struct IsChoiceTypeSupported {
|
using CleanType = typename std::decay<T>::type;
|
||||||
using CleanType = typename std::decay<T>::type;
|
static const bool value = std::is_integral<CleanType>::value ||
|
||||||
static const bool value = std::is_integral<CleanType>::value ||
|
std::is_same<CleanType, std::string>::value ||
|
||||||
std::is_same<CleanType, std::string>::value ||
|
std::is_same<CleanType, std::string_view>::value ||
|
||||||
std::is_same<CleanType, std::string_view>::value ||
|
std::is_same<CleanType, const char *>::value;
|
||||||
std::is_same<CleanType, const char*>::value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
@ -432,8 +436,7 @@ public:
|
|||||||
|
|
||||||
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
||||||
m_default_value_str = std::string{std::string_view{value}};
|
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);
|
m_default_value_str = std::to_string(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,18 +557,20 @@ public:
|
|||||||
return nargs(nargs_pattern::any);
|
return nargs(nargs_pattern::any);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> void add_choice(T &&choice) {
|
||||||
void add_choice(T&& choice) {
|
static_assert(details::IsChoiceTypeSupported<T>::value,
|
||||||
static_assert(details::IsChoiceTypeSupported<T>::value, "Only string or integer type supported for choice");
|
"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");
|
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()) {
|
if (!m_choices.has_value()) {
|
||||||
m_choices = std::vector<std::string>{};
|
m_choices = std::vector<std::string>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
if constexpr (std::is_convertible_v<T, std::string_view>) {
|
||||||
m_choices.value().push_back(std::string{std::string_view{std::forward<T>(choice)}});
|
m_choices.value().push_back(
|
||||||
}
|
std::string{std::string_view{std::forward<T>(choice)}});
|
||||||
else if constexpr (details::can_invoke_to_string<T>::value) {
|
} else if constexpr (details::can_invoke_to_string<T>::value) {
|
||||||
m_choices.value().push_back(std::to_string(std::forward<T>(choice)));
|
m_choices.value().push_back(std::to_string(std::forward<T>(choice)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -578,7 +583,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... U>
|
template <typename T, typename... U>
|
||||||
Argument &choices(T&& first, U&&...rest) {
|
Argument &choices(T &&first, U &&...rest) {
|
||||||
add_choice(std::forward<T>(first));
|
add_choice(std::forward<T>(first));
|
||||||
choices(std::forward<U>(rest)...);
|
choices(std::forward<U>(rest)...);
|
||||||
return *this;
|
return *this;
|
||||||
@ -1181,7 +1186,8 @@ 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::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};
|
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 &)>;
|
||||||
|
@ -69,18 +69,18 @@ TEST_CASE("Parse multiple arguments one of which is not in the fixed number of "
|
|||||||
std::runtime_error);
|
std::runtime_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE("Parse multiple arguments that are in the fixed number of allowed "
|
||||||
"Parse multiple arguments that are in the fixed number of allowed INTEGER choices" *
|
"INTEGER choices" *
|
||||||
test_suite("choices")) {
|
test_suite("choices")) {
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
program.add_argument("indices").nargs(2).choices(1, 2, 3, 4, 5);
|
program.add_argument("indices").nargs(2).choices(1, 2, 3, 4, 5);
|
||||||
|
|
||||||
program.parse_args({"test", "1", "2"});
|
program.parse_args({"test", "1", "2"});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(
|
TEST_CASE("Parse multiple arguments that are not in fixed number of allowed "
|
||||||
"Parse multiple arguments that are not in fixed number of allowed INTEGER choices" *
|
"INTEGER choices" *
|
||||||
test_suite("choices")) {
|
test_suite("choices")) {
|
||||||
argparse::ArgumentParser program("test");
|
argparse::ArgumentParser program("test");
|
||||||
program.add_argument("indices").nargs(2).choices(1, 2, 3, 4, 5);
|
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")
|
program.add_argument("-inc_chr", "--include_chromes")
|
||||||
.help(std::string{"only process the anchor whose one of the end is contained on the specified "
|
.help(std::string{"only process the anchor whose one of the end is "
|
||||||
"chromatin, used ',' to split."})
|
"contained on the specified "
|
||||||
.default_value("all");
|
"chromatin, used ',' to split."})
|
||||||
|
.default_value("all");
|
||||||
|
|
||||||
program.add_argument("-l").default_value(false).implicit_value(true);
|
program.add_argument("-l").default_value(false).implicit_value(true);
|
||||||
program.add_argument("-o").default_value(false).implicit_value(true);
|
program.add_argument("-o").default_value(false).implicit_value(true);
|
||||||
@ -40,12 +42,12 @@ TEST_CASE("Use a default value with flag arguments" * test_suite("default_value"
|
|||||||
|
|
||||||
SUBCASE("Leading optional argument with default_value") {
|
SUBCASE("Leading optional argument with default_value") {
|
||||||
REQUIRE_NOTHROW(program.parse_args({"test", "-inc_chr", "-lo", "my.log"}));
|
REQUIRE_NOTHROW(program.parse_args({"test", "-inc_chr", "-lo", "my.log"}));
|
||||||
REQUIRE(program.get("-inc_chr") == std::string{"all"});
|
REQUIRE(program.get("-inc_chr") == std::string{"all"});
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("Trailing optional argument with default_value") {
|
SUBCASE("Trailing optional argument with default_value") {
|
||||||
REQUIRE_NOTHROW(program.parse_args({"test", "-lo", "my.log", "-inc_chr"}));
|
REQUIRE_NOTHROW(program.parse_args({"test", "-lo", "my.log", "-inc_chr"}));
|
||||||
REQUIRE(program.get("-inc_chr") == std::string{"all"});
|
REQUIRE(program.get("-inc_chr") == std::string{"all"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +75,8 @@ TEST_CASE("Position of the argument with default value") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("Arg with default value replaces the value if given") {
|
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("-g") == std::string("a_different_value"));
|
||||||
REQUIRE(program.get("-s") == std::string("./src"));
|
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"}),
|
REQUIRE_THROWS_AS(program.parse_args({"test", "-n", "0XFFFFFFFFFFFFFFFF1"}),
|
||||||
std::range_error);
|
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"),
|
TEST_CASE_TEMPLATE("Parse integer argument of any format" * test_suite("scan"),
|
||||||
|
Loading…
Reference in New Issue
Block a user