diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index 921a4b41..4767b9e1 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -678,9 +678,9 @@ class Argument { std::is_void_v>, void_action, valued_action>; if constexpr (sizeof...(Args) == 0) { - m_action.emplace(std::forward(callable)); + m_actions.emplace_back(std::forward(callable)); } else { - m_action.emplace( + m_actions.emplace_back( [f = std::forward(callable), tup = std::make_tuple(std::forward(bound_args)...)]( std::string const &opt) mutable { @@ -980,7 +980,12 @@ class Argument { 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; @@ -1020,7 +1025,12 @@ class Argument { 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; @@ -1570,9 +1580,10 @@ class Argument { std::optional> m_choices{std::nullopt}; using valued_action = std::function; using void_action = std::function; - std::variant m_action{ - std::in_place_type, - [](const std::string &value) { return value; }}; + std::vector> m_actions; + std::variant m_default_action{ + std::in_place_type, + [](const std::string &value) { return value; }}; std::vector m_values; NArgsRange m_num_args_range{1, 1}; // Bit field of bool values. Set default value in ctor. diff --git a/test/test_actions.cpp b/test/test_actions.cpp index aa8cd3de..01c399f8 100644 --- a/test/test_actions.cpp +++ b/test/test_actions.cpp @@ -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("-V")); + REQUIRE(count == 2); + } + } + } +} diff --git a/test/test_scan.cpp b/test/test_scan.cpp index c556cb90..36d00123 100644 --- a/test/test_scan.cpp +++ b/test/test_scan.cpp @@ -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); + } + } + } + +} diff --git a/test/test_store_into.cpp b/test/test_store_into.cpp index 9717fab8..5fe575fc 100644 --- a/test/test_store_into.cpp +++ b/test/test_store_into.cpp @@ -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"); + } + } + } +} +