diff --git a/README.md b/README.md index 0b63101..4b5b5e7 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ * [Parent Parsers](#parent-parsers) * [Subcommands](#subcommands) * [Parse Known Args](#parse-known-args) + * [ArgumentParser in bool Context](#argumentparser-in-bool-context) * [Custom Prefix Characters](#custom-prefix-characters) * [Custom Assignment Characters](#custom-assignment-characters) * [Further Examples](#further-examples) @@ -848,7 +849,13 @@ int main(int argc, char *argv[]) { } ``` -### Custom Prefix Characters +### ArgumentParser in bool Context + +An `ArgumentParser` is `false` until it (or one of its subparsers) have extracted +known value(s) with `.parse_args` or `.parse_known_args`. When using `.parse_known_args`, +unknown arguments will not make a parser `true`. + +### Custom Prefix Characters Most command-line options will use `-` as the prefix, e.g. `-f/--foo`. Parsers that need to support different or additional prefix characters, e.g. for options like `+f` or `/foo`, may specify them using the `set_prefix_chars()`. diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index a807919..4beb269 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -1104,6 +1104,21 @@ public: return *this; } + explicit operator bool() const { + auto arg_used = std::any_of(m_argument_map.cbegin(), + m_argument_map.cend(), + [](auto &it) { + return it.second->m_is_used; + }); + auto subparser_used = std::any_of(m_subparser_used.cbegin(), + m_subparser_used.cend(), + [](auto &it) { + return it.second; + }); + + return m_is_parsed && (arg_used || subparser_used); + } + // Parameter packing // Call add_argument with variadic number of string arguments template Argument &add_argument(Targs... f_args) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c7a5657..47b9ba5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,7 @@ file(GLOB ARGPARSE_TEST_SOURCES main.cpp test_actions.cpp test_append.cpp + test_bool_operator.cpp test_compound_arguments.cpp test_container_arguments.cpp test_const_correct.cpp diff --git a/test/test_bool_operator.cpp b/test/test_bool_operator.cpp new file mode 100644 index 0000000..93a3be2 --- /dev/null +++ b/test/test_bool_operator.cpp @@ -0,0 +1,85 @@ +#include +#include + +using doctest::test_suite; + +TEST_CASE("ArgumentParser in bool context" * + test_suite("argument_parser")) { + argparse::ArgumentParser program("test"); + program.add_argument("cases").remaining(); + + program.parse_args({"test"}); + REQUIRE_FALSE(program); + + program.parse_args({"test", "one", "two"}); + REQUIRE(program); +} + +TEST_CASE("With subparsers in bool context" * test_suite("argument_parser")) { + argparse::ArgumentParser program("test"); + + argparse::ArgumentParser cmd_fly("fly"); + cmd_fly.add_argument("plane"); + + argparse::ArgumentParser cmd_soar("soar"); + cmd_soar.add_argument("direction"); + + program.add_subparser(cmd_fly); + program.add_subparser(cmd_soar); + + program.parse_args({"test", "fly", "glider"}); + REQUIRE(program); + REQUIRE(cmd_fly); + REQUIRE_FALSE(cmd_soar); +} + +TEST_CASE("Parsers remain false with unknown arguments" * + test_suite("argument_parser")) { + argparse::ArgumentParser program("test"); + + argparse::ArgumentParser cmd_build("build"); + cmd_build.add_argument("--file").nargs(1); + + argparse::ArgumentParser cmd_run("run"); + cmd_run.add_argument("--file").nargs(1); + + program.add_subparser(cmd_build); + program.add_subparser(cmd_run); + + auto unknowns = + program.parse_known_args({"test", "badger", "--add-meal", "grubs"}); + REQUIRE_FALSE(program); + REQUIRE_FALSE(cmd_build); + REQUIRE_FALSE(cmd_run); +} + +TEST_CASE("Multi-level parsers match subparser bool" * + test_suite("argument_parser")) { + argparse::ArgumentParser program("test"); + + argparse::ArgumentParser cmd_cook("cook"); + cmd_cook.add_argument("--temperature"); + + argparse::ArgumentParser cmd_cook_boil("boil"); + cmd_cook_boil.add_argument("--rate"); + + argparse::ArgumentParser cmd_cook_boil_stir("stir"); + cmd_cook_boil_stir.add_argument("--rate"); + + argparse::ArgumentParser cmd_wash("wash"); + + program.add_subparser(cmd_cook); + cmd_cook.add_subparser(cmd_cook_boil); + cmd_cook_boil.add_subparser(cmd_cook_boil_stir); + + program.add_subparser(cmd_wash); + + auto unknowns = program.parse_known_args( + {"test", "cook", "boil", "stir", "--rate", "fast"}); + + REQUIRE(program); + REQUIRE(cmd_cook); + REQUIRE(cmd_cook_boil); + REQUIRE(cmd_cook_boil_stir); + REQUIRE_FALSE(cmd_wash); +}