Merge pull request #356 from elpaso/multiple-actions

FEATURE: multiple actions
This commit is contained in:
Pranav 2025-01-20 13:16:51 -05:00 committed by GitHub
commit 22b54b8e62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 125 additions and 7 deletions

View File

@ -679,9 +679,9 @@ public:
std::is_void_v<std::invoke_result_t<F, Args..., std::string const>>, std::is_void_v<std::invoke_result_t<F, Args..., std::string const>>,
void_action, valued_action>; void_action, valued_action>;
if constexpr (sizeof...(Args) == 0) { if constexpr (sizeof...(Args) == 0) {
m_action.emplace<action_type>(std::forward<F>(callable)); m_actions.emplace_back<action_type>(std::forward<F>(callable));
} else { } else {
m_action.emplace<action_type>( m_actions.emplace_back<action_type>(
[f = std::forward<F>(callable), [f = std::forward<F>(callable),
tup = std::make_tuple(std::forward<Args>(bound_args)...)]( tup = std::make_tuple(std::forward<Args>(bound_args)...)](
std::string const &opt) mutable { std::string const &opt) mutable {
@ -1014,7 +1014,12 @@ public:
if (num_args_max == 0) { if (num_args_max == 0) {
if (!dry_run) { if (!dry_run) {
m_values.emplace_back(m_implicit_value); m_values.emplace_back(m_implicit_value);
std::visit([](const auto &f) { f({}); }, m_action); for(auto &action: m_actions) {
std::visit([&](const auto &f) { f({}); }, action);
}
if(m_actions.empty()){
std::visit([&](const auto &f) { f({}); }, m_default_action);
}
m_is_used = true; m_is_used = true;
} }
return start; return start;
@ -1054,7 +1059,12 @@ public:
Argument &self; Argument &self;
}; };
if (!dry_run) { if (!dry_run) {
std::visit(ActionApply{start, end, *this}, m_action); for(auto &action: m_actions) {
std::visit(ActionApply{start, end, *this}, action);
}
if(m_actions.empty()){
std::visit(ActionApply{start, end, *this}, m_default_action);
}
m_is_used = true; m_is_used = true;
} }
return end; return end;
@ -1604,7 +1614,8 @@ private:
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 &)>;
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::vector<std::variant<valued_action, void_action>> m_actions;
std::variant<valued_action, void_action> m_default_action{
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;

View File

@ -175,3 +175,28 @@ TEST_CASE("Users can run actions on parameterless optional arguments" *
} }
} }
} }
TEST_CASE("Users can add multiple actions and they are all run" *
test_suite("actions")) {
argparse::ArgumentParser program("test");
GIVEN("a flag argument with two counting actions") {
int count = 0;
program.add_argument("-V", "--verbose")
.action([&](const auto &) { ++count; })
.action([&](const auto &) { ++count; })
.append()
.default_value(false)
.implicit_value(true)
.nargs(0);
WHEN("the flag is parsed") {
program.parse_args({"test", "-V"});
THEN("the count increments twice") {
REQUIRE(program.get<bool>("-V"));
REQUIRE(count == 2);
}
}
}
}

View File

@ -426,3 +426,50 @@ TEST_CASE_TEMPLATE("Parse floating-point argument of fixed format" *
std::invalid_argument); std::invalid_argument);
} }
} }
TEST_CASE("Test that scan also works with a custom action" *
test_suite("scan")) {
GIVEN("an argument with scan followed by a custom action") {
argparse::ArgumentParser program("test");
int res;
program.add_argument("--int").scan<'i', int>().action([&](const auto &s) {res = std::stoi(s);});
WHEN("the argument is parsed") {
SUBCASE("with a valid value") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored") {
REQUIRE(res == 3);
}
}
SUBCASE("with an invalid value") {
REQUIRE_THROWS_AS(program.parse_args({"./test.exe", "--int", "XXX"}),
std::invalid_argument);
}
}
}
GIVEN("an argument with a custom action followed by scan") {
argparse::ArgumentParser program("test");
int res;
program.add_argument("--int").action([&](const auto &s) {res = std::stoi(s);}).scan<'i', int>();
WHEN("the argument is parsed") {
SUBCASE("with a valid value") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored") {
REQUIRE(res == 3);
}
}
SUBCASE("with an invalid value") {
REQUIRE_THROWS_AS(program.parse_args({"./test.exe", "--int", "XXX"}),
std::invalid_argument);
}
}
}
}

View File

@ -286,3 +286,38 @@ TEST_CASE("Test store_into(set of string), default value, multi valued, specifie
} }
} }
TEST_CASE("Test store_into(int) still works with a custom action" *
test_suite("store_into")) {
GIVEN("an argument with store_into followed by a custom action ") {
argparse::ArgumentParser program("test");
int res;
std::string string_res;
program.add_argument("--int").store_into(res).action([&](const auto &s) {string_res.append(s);});
WHEN("the argument is parsed") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored and the action was executed") {
REQUIRE(res == 3);
REQUIRE(string_res == "3");
}
}
}
GIVEN("an argument with a custom action followed by store_into")
{
argparse::ArgumentParser program("test");
int res;
std::string string_res;
program.add_argument("--int").action([&](const auto &s) {string_res.append(s);}).store_into(res);
WHEN("the argument is parsed") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored and the action was executed") {
REQUIRE(res == 3);
REQUIRE(string_res == "3");
}
}
}
}