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>>,
void_action, valued_action>;
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 {
m_action.emplace<action_type>(
m_actions.emplace_back<action_type>(
[f = std::forward<F>(callable),
tup = std::make_tuple(std::forward<Args>(bound_args)...)](
std::string const &opt) mutable {
@ -1014,7 +1014,12 @@ public:
if (num_args_max == 0) {
if (!dry_run) {
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;
}
return start;
@ -1054,7 +1059,12 @@ public:
Argument &self;
};
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;
}
return end;
@ -1604,9 +1614,10 @@ private:
std::optional<std::vector<std::string>> m_choices{std::nullopt};
using valued_action = std::function<std::any(const std::string &)>;
using void_action = std::function<void(const std::string &)>;
std::variant<valued_action, void_action> m_action{
std::in_place_type<valued_action>,
[](const std::string &value) { return value; }};
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>,
[](const std::string &value) { return value; }};
std::vector<std::any> m_values;
NArgsRange m_num_args_range{1, 1};
// Bit field of bool values. Set default value in ctor.

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);
}
}
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");
}
}
}
}