From 33146122a243b328650565f72efa39974c3732aa Mon Sep 17 00:00:00 2001 From: Alexey Ismagilov Date: Fri, 5 May 2023 21:43:56 +0300 Subject: [PATCH] NEW: suppress flag for subcommand resolve #258 --- README.md | 35 +++++++++++++++++++++++++++++++++- include/argparse/argparse.hpp | 26 ++++++++++++++++++++++--- test/test_subparsers.cpp | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0a89e96..5d8b79c 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ Optional arguments: -h, --help shows help message and exits -v, --version prints version information and exits --member ALIAS The alias for the member to pass to. - --verbose + --verbose Possible things include betingalw, chiz, and res. ``` @@ -899,6 +899,39 @@ When a help message is requested from a subparser, only the help for that partic Additionally, every parser has the `.is_subcommand_used("")` and `.is_subcommand_used(subparser)` member functions to check if a subcommand was used. +Sometimes there may be a need to hide part of the subcommands from the user +by suppressing information about them in an help message. To do this, +```ArgumentParser``` contains the method ```.set_suppress(bool suppress)```: + +```cpp +argparse::ArgumentParser program("test"); + +argparse::ArgumentParser hidden_cmd("hidden"); +hidden_cmd.add_argument("files").remaining(); +hidden_cmd.set_suppress(true); + +program.add_subparser(hidden_cmd); +``` + +```console +foo@bar:/home/dev/$ ./main -h +Usage: test [--help] [--version] {} + +Optional arguments: + -h, --help shows help message and exits + -v, --version prints version information and exits + +foo@bar:/home/dev/$ ./main hidden -h +Usage: hidden [--help] [--version] files + +Positional arguments: + files [nargs: 0 or more] + +Optional arguments: + -h, --help shows help message and exits + -v, --version prints version information and exits +``` + ### Getting Argument and Subparser Instances ```Argument``` and ```ArgumentParser``` instances added to an ```ArgumentParser``` can be retrieved with ```.at()```. The default return type is ```Argument```. diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index bf8de35..22fffc0 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -1405,7 +1405,8 @@ public: m_assign_chars(other.m_assign_chars), m_is_parsed(other.m_is_parsed), m_positional_arguments(other.m_positional_arguments), m_optional_arguments(other.m_optional_arguments), - m_parser_path(other.m_parser_path), m_subparsers(other.m_subparsers) { + m_parser_path(other.m_parser_path), m_subparsers(other.m_subparsers), + m_suppress(other.m_suppress) { for (auto it = std::begin(m_positional_arguments); it != std::end(m_positional_arguments); ++it) { index_argument(it); @@ -1747,12 +1748,22 @@ public: stream << argument; } - if (!parser.m_subparser_map.empty()) { + bool has_visible_subcommands = std::any_of( + parser.m_subparser_map.begin(), + parser.m_subparser_map.end(), + [] (auto &p) { return !p.second->get().m_suppress; } + ); + + if (has_visible_subcommands) { stream << (parser.m_positional_arguments.empty() ? (parser.m_optional_arguments.empty() ? "" : "\n") : "\n") << "Subcommands:\n"; for (const auto &[command, subparser] : parser.m_subparser_map) { + if (subparser->get().m_suppress) { + continue; + } + stream << std::setw(2) << " "; stream << std::setw(static_cast(longest_arg_length - 2)) << command; @@ -1797,7 +1808,11 @@ public: if (!m_subparser_map.empty()) { stream << " {"; std::size_t i{0}; - for (const auto &[command, unused] : m_subparser_map) { + for (const auto &[command, subparser] : m_subparser_map) { + if (subparser->get().m_suppress) { + continue; + } + if (i == 0) { stream << command; } else { @@ -1827,6 +1842,10 @@ public: m_subparser_used.insert_or_assign(parser.m_program_name, false); } + void set_suppress(bool suppress) { + m_suppress = suppress; + } + private: bool is_valid_prefix_char(char c) const { return m_prefix_chars.find(c) != std::string::npos; @@ -2120,6 +2139,7 @@ private: std::map m_subparser_map; std::map m_subparser_used; std::vector m_mutually_exclusive_groups; + bool m_suppress = false; }; } // namespace argparse diff --git a/test/test_subparsers.cpp b/test/test_subparsers.cpp index a4bfac5..532c9a7 100644 --- a/test/test_subparsers.cpp +++ b/test/test_subparsers.cpp @@ -244,3 +244,39 @@ TEST_CASE("Check is_subcommand_used after parse" * test_suite("subparsers")) { REQUIRE(program.is_subcommand_used(command_2) == false); } } + +static bool contains(const std::string &haystack, const std::string &needle) { + return haystack.find(needle) != std::string::npos; +} + +TEST_CASE("Check set_suppress" * test_suite("subparsers")) { + argparse::ArgumentParser command("cmd"); + command.add_argument("arg").remaining(); + + argparse::ArgumentParser program("test"); + program.add_subparser(command); + + SUBCASE("help message contain info if subcommand not suppressed") { + command.set_suppress(false); + REQUIRE(contains(program.help().str(), "Subcommands") == true); + REQUIRE(contains(program.help().str(), "cmd") == true); + } + + SUBCASE("help message does not contain info if subcommand suppressed") { + command.set_suppress(true); + REQUIRE(contains(program.help().str(), "Subcommands") == false); + REQUIRE(contains(program.help().str(), "cmd") == false); + } + + SUBCASE("help message contain info if not all subcommands suppressed") { + argparse::ArgumentParser command_2("command_2"); + program.add_subparser(command_2); + + command.set_suppress(true); + command_2.set_suppress(false); + + REQUIRE(contains(program.help().str(), "Subcommands") == true); + REQUIRE(contains(program.help().str(), "cmd") == false); + REQUIRE(contains(program.help().str(), "command_2") == true); + } +}