diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index 06d30fd4..b60ad44d 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -698,8 +698,17 @@ class Argument { if (m_default_value.has_value()) { var = std::any_cast(m_default_value); } - action([&var](const auto & /*unused*/) { - var = true; + action([&var](const std::string &s) { + if (s.empty()) { + var = true; // implicit flag + return var; + } + try { + int v = details::parse_number()(s); + var = (v != 0); + } catch (...) { + var = true; // fallback to true + } return var; }); return *this; @@ -1012,6 +1021,15 @@ class Argument { (m_choices.has_value()) ? passed_options : m_num_args_range.get_max(); const auto num_args_min = m_num_args_range.get_min(); std::size_t dist = 0; + auto run_actions_empty = [&]() { + 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; + }; if (num_args_max == 0) { if (!dry_run) { m_values.emplace_back(m_implicit_value); @@ -1041,6 +1059,15 @@ class Argument { std::string(m_used_name) + "'."); } } + if (dist == 0) { + // when nargs(0,1) and no arguments provided + if (!dry_run) { + if (m_implicit_value.has_value()) { + m_values.emplace_back(m_implicit_value); + run_actions_empty(); + } + } + } struct ActionApply { void operator()(valued_action &f) { std::transform(first, last, std::back_inserter(self.m_values), f); diff --git a/test/test_optional_arguments.cpp b/test/test_optional_arguments.cpp index 74a4c25a..70ba0b2a 100644 --- a/test/test_optional_arguments.cpp +++ b/test/test_optional_arguments.cpp @@ -202,3 +202,50 @@ TEST_CASE("Parse arguments of different types" * REQUIRE(program["-string-view"] == true); REQUIRE(program["-builtin"s] == true); } + +TEST_CASE("Flag with nargs(0,1) stores implicit when no value" * + test_suite("optional_arguments")) { + argparse::ArgumentParser program("prog"); + bool value = false; + program.add_argument("--foo").store_into(value).nargs(0, 1); + program.parse_args({"prog", "--foo"}); + REQUIRE(value == true); +} + +TEST_CASE("Flag with nargs(0,1) respects explicit value" * + test_suite("optional_arguments")) { + argparse::ArgumentParser program("prog"); + bool value = false; + program.add_argument("--foo").store_into(value).nargs(0, 1); + SUBCASE("test with explicit '0' value") { + program.parse_args({"prog", "--foo", "0"}); + REQUIRE(value == false); + } + SUBCASE("test with explicit '1' value") { + program.parse_args({"prog", "--foo", "1"}); + REQUIRE(value == true); + } +} + +TEST_CASE("nargs(0,1) default-only without value" * + test_suite("optional_arguments")) { + argparse::ArgumentParser program("prog"); + bool value = false; + program.add_argument("--foo").default_value(true).nargs(0, 1).store_into( + value); + REQUIRE_NOTHROW(program.parse_args({"prog", "--foo"})); + REQUIRE(value == true); + REQUIRE(program.get("--foo") == true); +} + +TEST_CASE("nargs(0,1) implicit when followed by another option" * + test_suite("optional_arguments")) { + argparse::ArgumentParser program("prog"); + bool foo = false; + std::string bar; + program.add_argument("--foo").store_into(foo).nargs(0, 1); + program.add_argument("--bar").store_into(bar); + program.parse_args({"prog", "--foo", "--bar", "value"}); + REQUIRE(foo == true); + REQUIRE(bar == std::string("value")); +}