-
Notifications
You must be signed in to change notification settings - Fork 38.7k
ArgsManager: support subcommand-specific options #28802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
e2debfa
42f24f4
6105207
cce7eba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,14 +36,14 @@ static void SetupWalletToolArgs(ArgsManager& argsman) | |
| argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); | ||
| argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); | ||
| argsman.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); | ||
| argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); | ||
| argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::COMMAND_OPTIONS); | ||
| argsman.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); | ||
| argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); | ||
|
|
||
| argsman.AddCommand("info", "Get wallet info"); | ||
| argsman.AddCommand("create", "Create a new descriptor wallet file"); | ||
| argsman.AddCommand("dump", "Print out all of the wallet key-value records"); | ||
| argsman.AddCommand("createfromdump", "Create new wallet file from dumped records"); | ||
| argsman.AddCommand("dump", "Print out all of the wallet key-value records", {"-dumpfile"}); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are there any other such relations between command and arg that we could check this way?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not currently, I think |
||
| argsman.AddCommand("createfromdump", "Create new wallet file from dumped records", {"-dumpfile"}); | ||
| } | ||
|
|
||
| static std::optional<int> WalletAppInit(ArgsManager& args, int argc, char* argv[]) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -363,6 +363,31 @@ std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const | |
| return ret; | ||
| } | ||
|
|
||
| bool ArgsManager::CheckCommandOptions(const std::string& command, std::vector<std::string>* errors) const | ||
| { | ||
| LOCK(cs_args); | ||
|
|
||
| auto command_options = m_available_args.find(OptionsCategory::COMMAND_OPTIONS); | ||
ajtowns marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (command_options == m_available_args.end()) return true; | ||
|
|
||
| const auto command_args = m_command_args.find(command); | ||
| auto is_valid_opt = [&](const auto& opt) EXCLUSIVE_LOCKS_REQUIRED(cs_args) -> bool { | ||
| if (command_args == m_command_args.end()) return false; | ||
| return command_args->second.contains(opt); | ||
| }; | ||
|
|
||
| bool ok = true; | ||
| for (const auto& [arg, _] : command_options->second) { | ||
| if (!IsArgSet(arg)) continue; | ||
| if (is_valid_opt(arg)) continue; | ||
| if (errors != nullptr) { | ||
| errors->emplace_back(strprintf("The %s option cannot be used with the '%s' command.", arg, command)); | ||
| ok = false; | ||
| } | ||
| } | ||
| return ok; | ||
| } | ||
|
|
||
| std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const | ||
| { | ||
| std::vector<std::string> result; | ||
|
|
@@ -553,7 +578,7 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV | |
| m_settings.forced_settings[SettingName(strArg)] = strValue; | ||
| } | ||
|
|
||
| void ArgsManager::AddCommand(const std::string& cmd, const std::string& help) | ||
| void ArgsManager::AddCommand(const std::string& cmd, const std::string& help, std::set<std::string>&& options) | ||
| { | ||
| Assert(cmd.find('=') == std::string::npos); | ||
| Assert(cmd.at(0) != '-'); | ||
|
|
@@ -562,6 +587,9 @@ void ArgsManager::AddCommand(const std::string& cmd, const std::string& help) | |
| m_accept_any_command = false; // latch to false | ||
| std::map<std::string, Arg>& arg_map = m_available_args[OptionsCategory::COMMANDS]; | ||
| auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND}); | ||
| if (!options.empty()) { | ||
| m_command_args.try_emplace(cmd, std::move(options)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In commit "ArgsManager: support command-specific options" (e2debfa) Should the options list be validated against known options? Seems like it could be easy to make a typo and pass a bad list of options to this function, causing help output to be incomplete without it being obvious. |
||
| } | ||
| Assert(ret.second); // Fail on duplicate commands | ||
| } | ||
|
|
||
|
|
@@ -618,14 +646,46 @@ void ArgsManager::CheckMultipleCLIArgs() const | |
| } | ||
| } | ||
|
|
||
| namespace { | ||
| /** Helper class for iterating over COMMAND_OPTIONS applicable to a given command */ | ||
| template <typename T> | ||
| class CommandOptionsGetter | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In commit "ArgsManager: support command-specific options" (e2debfa) Any plans to reuse this class? It would seem simpler to inline: diff
--- a/src/common/args.cpp
+++ b/src/common/args.cpp
@@ -646,35 +646,6 @@ void ArgsManager::CheckMultipleCLIArgs() const
}
}
-namespace {
-/** Helper class for iterating over COMMAND_OPTIONS applicable to a given command */
-template <typename T>
-class CommandOptionsGetter
-{
-private:
- const typename T::const_iterator m_end;
- const typename T::const_iterator m_iter;
-public:
- CommandOptionsGetter(const T& available_args)
- : m_end{available_args.end()},
- m_iter{available_args.find(OptionsCategory::COMMAND_OPTIONS)}
- {
- }
-
- template <typename Fn>
- void Iterate(const std::set<std::string>& select, bool with_debug, Fn&& fn) const
- {
- if (select.empty()) return;
- if (m_iter == m_end) return;
- for (const auto& [cmdopt_name, cmdopt_info] : m_iter->second) {
- if (!with_debug && (cmdopt_info.m_flags & ArgsManager::DEBUG_ONLY)) continue;
- if (!select.contains(cmdopt_name)) continue;
- fn(cmdopt_name, cmdopt_info);
- }
- }
-};
-} // anonymous namespace
-
std::string ArgsManager::GetHelpMessage() const
{
const bool show_debug = GetBoolArg("-help-debug", false);
@@ -682,7 +653,7 @@ std::string ArgsManager::GetHelpMessage() const
std::string usage;
LOCK(cs_args);
- const auto command_options = CommandOptionsGetter(m_available_args);
+ const auto command_options = m_available_args.find(OptionsCategory::COMMAND_OPTIONS);
for (const auto& [category, category_args] : m_available_args) {
switch(category) {
@@ -748,10 +719,12 @@ std::string ArgsManager::GetHelpMessage() const
if (category == OptionsCategory::COMMANDS) {
const auto cmd_args = m_command_args.find(arg_name);
- if (cmd_args != m_command_args.end()) {
- command_options.Iterate(cmd_args->second, show_debug, [&](const auto& cmdopt_name, const auto& cmdopt_info) {
+ if (cmd_args != m_command_args.end() && command_options != m_available_args.end()) {
+ for (const auto& [cmdopt_name, cmdopt_info] : command_options->second) {
+ if (!show_debug && (cmdopt_info.m_flags & ArgsManager::DEBUG_ONLY)) continue;
+ if (!cmd_args->second.contains(cmdopt_name)) continue;
usage += HelpMessageSubOpt(cmdopt_name, cmdopt_info.m_help_param, cmdopt_info.m_help_text);
- });
+ }
}
}
} |
||
| { | ||
| private: | ||
| const typename T::const_iterator m_end; | ||
| const typename T::const_iterator m_iter; | ||
| public: | ||
| CommandOptionsGetter(const T& available_args) | ||
| : m_end{available_args.end()}, | ||
| m_iter{available_args.find(OptionsCategory::COMMAND_OPTIONS)} | ||
| { | ||
| } | ||
|
|
||
| template <typename Fn> | ||
| void Iterate(const std::set<std::string>& select, bool with_debug, Fn&& fn) const | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the new code is completely uncovered: https://corecheck.dev/bitcoin/bitcoin/pulls/28802 Adding temporary exceptions inside indicates that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added some unit test coverage |
||
| { | ||
| if (select.empty()) return; | ||
| if (m_iter == m_end) return; | ||
| for (const auto& [cmdopt_name, cmdopt_info] : m_iter->second) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: when is it |
||
| if (!with_debug && (cmdopt_info.m_flags & ArgsManager::DEBUG_ONLY)) continue; | ||
| if (!select.contains(cmdopt_name)) continue; | ||
| fn(cmdopt_name, cmdopt_info); | ||
| } | ||
| } | ||
| }; | ||
| } // anonymous namespace | ||
|
|
||
| std::string ArgsManager::GetHelpMessage() const | ||
| { | ||
| const bool show_debug = GetBoolArg("-help-debug", false); | ||
|
|
||
| std::string usage; | ||
| LOCK(cs_args); | ||
| for (const auto& arg_map : m_available_args) { | ||
| switch(arg_map.first) { | ||
|
|
||
| const auto command_options = CommandOptionsGetter(m_available_args); | ||
|
|
||
| for (const auto& [category, category_args] : m_available_args) { | ||
| switch(category) { | ||
| case OptionsCategory::OPTIONS: | ||
| usage += HelpMessageGroup("Options:"); | ||
| break; | ||
|
|
@@ -671,22 +731,29 @@ std::string ArgsManager::GetHelpMessage() const | |
| case OptionsCategory::CLI_COMMANDS: | ||
| usage += HelpMessageGroup("CLI Commands:"); | ||
| break; | ||
| case OptionsCategory::COMMAND_OPTIONS: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
|
|
||
| if (category == OptionsCategory::COMMAND_OPTIONS) continue; | ||
|
|
||
| // When we get to the hidden options, stop | ||
| if (arg_map.first == OptionsCategory::HIDDEN) break; | ||
|
|
||
| for (const auto& arg : arg_map.second) { | ||
| if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) { | ||
| std::string name; | ||
| if (arg.second.m_help_param.empty()) { | ||
| name = arg.first; | ||
| } else { | ||
| name = arg.first + arg.second.m_help_param; | ||
| if (category == OptionsCategory::HIDDEN) break; | ||
|
|
||
| for (const auto& [arg_name, arg_info] : category_args) { | ||
| if (show_debug || !(arg_info.m_flags & ArgsManager::DEBUG_ONLY)) { | ||
| usage += HelpMessageOpt(arg_name, arg_info.m_help_param, arg_info.m_help_text); | ||
|
|
||
| if (category == OptionsCategory::COMMANDS) { | ||
| const auto cmd_args = m_command_args.find(arg_name); | ||
| if (cmd_args != m_command_args.end()) { | ||
| command_options.Iterate(cmd_args->second, show_debug, [&](const auto& cmdopt_name, const auto& cmdopt_info) { | ||
| usage += HelpMessageSubOpt(cmdopt_name, cmdopt_info.m_help_param, cmdopt_info.m_help_text); | ||
| }); | ||
| } | ||
| } | ||
| usage += HelpMessageOpt(name, arg.second.m_help_text); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -712,11 +779,11 @@ std::string HelpMessageGroup(const std::string &message) { | |
| return std::string(message) + std::string("\n\n"); | ||
| } | ||
|
|
||
| std::string HelpMessageOpt(const std::string &option, const std::string &message) { | ||
| return std::string(optIndent,' ') + std::string(option) + | ||
| std::string("\n") + std::string(msgIndent,' ') + | ||
| FormatParagraph(message, screenWidth - msgIndent, msgIndent) + | ||
| std::string("\n\n"); | ||
| std::string HelpMessageOpt(std::string_view option, std::string_view help_param, std::string_view message, int indent) | ||
| { | ||
| return strprintf("%*s%s%s\n%*s%s\n\n", | ||
| (optIndent + indent), "", option, help_param, | ||
| (msgIndent + indent), "", FormatParagraph(message, screenWidth - msgIndent - indent, msgIndent + indent)); | ||
| } | ||
|
|
||
| const std::vector<std::string> TEST_OPTIONS_DOC{ | ||
|
|
@@ -733,6 +800,11 @@ bool HasTestOption(const ArgsManager& args, const std::string& test_option) | |
| }); | ||
| } | ||
|
|
||
| std::string HelpMessageSubOpt(std::string_view option, std::string_view help_param, std::string_view message) | ||
| { | ||
| return HelpMessageOpt(option, help_param, message, msgIndent - optIndent); | ||
| } | ||
|
|
||
| fs::path GetDefaultDataDir() | ||
| { | ||
| // Windows: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| #include <optional> | ||
| #include <set> | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <variant> | ||
| #include <vector> | ||
|
|
||
|
|
@@ -66,6 +67,8 @@ enum class OptionsCategory { | |
| CLI_COMMANDS, | ||
| IPC, | ||
|
|
||
| COMMAND_OPTIONS, // Specific to one or more commands | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems safe, just have to make sure: is it safe to add in the middle of this enum (e.g. is its index persisted anywhere?).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Placement doesn't matter, but I don't think moving closer to COMMANDS, REGISTER_COMMANDS or CLI_COMMANDS really makes sense -- COMMAND_OPTIONS relates to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In commit "ArgsManager: support command-specific options" (e2debfa) Might be worth noting that options with this category will be excluded from help output unless they are referenced by an Relatedly it might be good to add a check that every option in the |
||
|
|
||
| HIDDEN // Always the last option to avoid printing these in the help | ||
| }; | ||
|
|
||
|
|
@@ -138,6 +141,7 @@ class ArgsManager | |
| std::set<std::string> m_network_only_args GUARDED_BY(cs_args); | ||
| std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args); | ||
| std::optional<unsigned int> m_default_flags GUARDED_BY(cs_args){}; | ||
| std::map<std::string, std::set<std::string>> m_command_args GUARDED_BY(cs_args); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line seems to conflict with latest nit: is it important for the set to be sorted here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's looked up by the key in |
||
| bool m_accept_any_command GUARDED_BY(cs_args){true}; | ||
| std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args); | ||
| std::optional<fs::path> m_config_path GUARDED_BY(cs_args); | ||
|
|
@@ -213,6 +217,11 @@ class ArgsManager | |
| */ | ||
| std::optional<const Command> GetCommand() const; | ||
|
|
||
| /** | ||
| * Check for invalid command options | ||
| */ | ||
| bool CheckCommandOptions(const std::string& command, std::vector<std::string>* errors = nullptr) const; | ||
ajtowns marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Get blocks directory path | ||
| * | ||
|
|
@@ -348,9 +357,9 @@ class ArgsManager | |
| void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat); | ||
|
|
||
| /** | ||
| * Add subcommand | ||
| * Add command | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this comment still useful now?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a docstring, so shows up in the doxygen docs https://doxygen.bitcoincore.org/class_args_manager.html#ab71d531303f7e6fe3bbf67683a414a2e ; maybe it's not that useful, but doesn't seem worth removing outright |
||
| */ | ||
| void AddCommand(const std::string& cmd, const std::string& help); | ||
| void AddCommand(const std::string& cmd, const std::string& help, std::set<std::string>&& options={}); | ||
|
|
||
| /** | ||
| * Add many hidden arguments | ||
|
|
@@ -474,10 +483,22 @@ std::string HelpMessageGroup(const std::string& message); | |
| /** | ||
| * Format a string to be used as option description in help messages | ||
| * | ||
| * @param option Option message (e.g. "-rpcuser=<user>") | ||
| * @param option Option name (e.g. "-rpcuser") | ||
| * @param help_param Help parameter (e.g. "=<user>" or "") | ||
| * @param message Option description (e.g. "Username for JSON-RPC connections") | ||
| * @param indent Additional indentation | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In commit "ArgsManager: support command-specific options" (e2debfa) Giving this function an diff
--- a/src/common/args.cpp
+++ b/src/common/args.cpp
@@ -750,7 +750,7 @@ std::string ArgsManager::GetHelpMessage() const
const auto cmd_args = m_command_args.find(arg_name);
if (cmd_args != m_command_args.end()) {
command_options.Iterate(cmd_args->second, show_debug, [&](const auto& cmdopt_name, const auto& cmdopt_info) {
- usage += HelpMessageSubOpt(cmdopt_name, cmdopt_info.m_help_param, cmdopt_info.m_help_text);
+ usage += HelpMessageOpt(cmdopt_name, cmdopt_info.m_help_param, cmdopt_info.m_help_text, /*subopt=*/true);
});
}
}
@@ -779,8 +779,9 @@ std::string HelpMessageGroup(const std::string &message) {
return std::string(message) + std::string("\n\n");
}
-std::string HelpMessageOpt(std::string_view option, std::string_view help_param, std::string_view message, int indent)
+std::string HelpMessageOpt(std::string_view option, std::string_view help_param, std::string_view message, bool subopt)
{
+ const int indent{subopt ? msgIndent - optIndent : 0};
return strprintf("%*s%s%s\n%*s%s\n\n",
(optIndent + indent), "", option, help_param,
(msgIndent + indent), "", FormatParagraph(message, screenWidth - msgIndent - indent, msgIndent + indent));
@@ -800,11 +801,6 @@ bool HasTestOption(const ArgsManager& args, const std::string& test_option)
});
}
-std::string HelpMessageSubOpt(std::string_view option, std::string_view help_param, std::string_view message)
-{
- return HelpMessageOpt(option, help_param, message, msgIndent - optIndent);
-}
-
fs::path GetDefaultDataDir()
{
// Windows:
--- a/src/common/args.h
+++ b/src/common/args.h
@@ -486,19 +486,9 @@ std::string HelpMessageGroup(const std::string& message);
* @param option Option name (e.g. "-rpcuser")
* @param help_param Help parameter (e.g. "=<user>" or "")
* @param message Option description (e.g. "Username for JSON-RPC connections")
- * @param indent Additional indentation
+ * @param subopt True if this is suboption, instead of a top level option.
* @return the formatted string
*/
-std::string HelpMessageOpt(std::string_view option, std::string_view help_param, std::string_view message, int indent=0);
-
-/**
- * Same as HelpMessageOpt, but indents for command-specific options
- *
- * @param option Option name (e.g. "-rpcuser")
- * @param help_param Help parameter (e.g. "=<user>" or "")
- * @param message Option description (e.g. "Username for JSON-RPC connections")
- * @return the formatted string
- */
-std::string HelpMessageSubOpt(std::string_view option, std::string_view help_param, std::string_view message);
+std::string HelpMessageOpt(std::string_view option, std::string_view help_param, std::string_view message, bool subopt=false);
#endif // BITCOIN_COMMON_ARGS_H |
||
| * @return the formatted string | ||
| */ | ||
| std::string HelpMessageOpt(std::string_view option, std::string_view help_param, std::string_view message, int indent=0); | ||
|
|
||
| /** | ||
| * Same as HelpMessageOpt, but indents for command-specific options | ||
| * | ||
| * @param option Option name (e.g. "-rpcuser") | ||
| * @param help_param Help parameter (e.g. "=<user>" or "") | ||
| * @param message Option description (e.g. "Username for JSON-RPC connections") | ||
| * @return the formatted string | ||
| */ | ||
| std::string HelpMessageOpt(const std::string& option, const std::string& message); | ||
| std::string HelpMessageSubOpt(std::string_view option, std::string_view help_param, std::string_view message); | ||
|
|
||
| #endif // BITCOIN_COMMON_ARGS_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -640,6 +640,51 @@ BOOST_AUTO_TEST_CASE(util_GetArg) | |
| BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b"); | ||
| } | ||
|
|
||
| BOOST_AUTO_TEST_CASE(util_AddCommand) | ||
| { | ||
| enum TestFail { SUCCESS, PARSE_FAIL, PARSE_ERROR, NO_COMMAND, COMMAND_OPTS }; | ||
|
|
||
| auto testfn = [&](const auto& argv) -> TestFail { | ||
| TestArgsManager test_args; | ||
| test_args.AddArg("-opt1=<file name>", "Opt 1", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::COMMAND_OPTIONS); | ||
| test_args.AddArg("-opt2=<file name>", "Opt 2", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::COMMAND_OPTIONS); | ||
| test_args.AddArg("-opt3=<file name>", "Opt 3", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); | ||
|
|
||
| test_args.AddCommand("cmd1", "No specific options"); | ||
| test_args.AddCommand("cmd2", "Opt 1", {"-opt1"}); | ||
| test_args.AddCommand("cmd3", "Opt 1 or 2", {"-opt1", "-opt2"}); | ||
|
|
||
| std::string error; | ||
| if (!test_args.ParseParameters(argv.size(), argv.data(), error)) return PARSE_FAIL; | ||
| if (!error.empty()) return PARSE_ERROR; | ||
| const auto command = test_args.GetCommand(); | ||
| if (!command) return NO_COMMAND; | ||
| std::vector<std::string> details; | ||
| if (!test_args.CheckCommandOptions(command->command, &details)) return COMMAND_OPTS; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In commit "tests: Add some test coverage for ArgsManager::AddCommand" (cce7eba) Might be good to check details is empty on success, nonempty on failure |
||
| return SUCCESS; | ||
| }; | ||
|
|
||
| BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt1=foo", "cmd1"})); | ||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "cmd1", "-opt1=foo"})); // things after the command are "args" and left unparsed, not options | ||
|
|
||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt1=foo", "cmd2"})); | ||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt1=foo", "cmd3"})); | ||
| BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt2=foo", "cmd1"})); | ||
| BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt2=foo", "cmd2"})); | ||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt2=foo", "cmd3"})); | ||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt3=foo", "cmd1"})); | ||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt3=foo", "cmd2"})); | ||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt3=foo", "cmd3"})); | ||
|
|
||
| BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt1=foo", "-opt3=bar", "-opt2=baz", "cmd1"})); | ||
| BOOST_CHECK_EQUAL(COMMAND_OPTS, testfn(std::array{"x", "-opt1=foo", "-opt3=bar", "-opt2=baz", "cmd2"})); | ||
| BOOST_CHECK_EQUAL(SUCCESS, testfn(std::array{"x", "-opt1=foo", "-opt3=bar", "-opt2=baz", "cmd3"})); | ||
|
|
||
| BOOST_CHECK_EQUAL(PARSE_FAIL, testfn(std::array{"x", "cmd4"})); | ||
| BOOST_CHECK_EQUAL(NO_COMMAND, testfn(std::array{"x", "-opt3=foo"})); | ||
| BOOST_CHECK_EQUAL(PARSE_FAIL, testfn(std::array{"x", "-opt4=foo"})); | ||
| } | ||
|
|
||
| BOOST_AUTO_TEST_CASE(util_GetChainTypeString) | ||
| { | ||
| TestArgsManager test_args; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a future improvement/ follow-up could be specifying the help message description for each command: