mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-15 17:21:09 +02:00
ArgsManager: support command-specific options
This commit is contained in:
@@ -598,7 +598,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) != '-');
|
||||
@@ -607,6 +607,18 @@ 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()) {
|
||||
auto& cmdopts = m_available_args[OptionsCategory::COMMAND_OPTIONS];
|
||||
bool command_has_all_options_defined = true;
|
||||
for (const auto& opt : options) {
|
||||
if (!cmdopts.contains(opt)) {
|
||||
command_has_all_options_defined = false;
|
||||
}
|
||||
}
|
||||
Assert(command_has_all_options_defined);
|
||||
|
||||
m_command_args.try_emplace(cmd, std::move(options));
|
||||
}
|
||||
Assert(ret.second); // Fail on duplicate commands
|
||||
}
|
||||
|
||||
@@ -643,6 +655,7 @@ void ArgsManager::ClearArgs()
|
||||
LOCK(cs_args);
|
||||
m_settings = {};
|
||||
m_available_args.clear();
|
||||
m_command_args.clear();
|
||||
m_network_only_args.clear();
|
||||
m_config_sections.clear();
|
||||
}
|
||||
@@ -670,8 +683,20 @@ std::string ArgsManager::GetHelpMessage() const
|
||||
|
||||
std::string usage;
|
||||
LOCK(cs_args);
|
||||
for (const auto& arg_map : m_available_args) {
|
||||
switch(arg_map.first) {
|
||||
|
||||
const auto command_options = m_available_args.find(OptionsCategory::COMMAND_OPTIONS);
|
||||
const auto for_matching_cmd_opts = [&](const std::set<std::string>& select, auto&& fn) EXCLUSIVE_LOCKS_REQUIRED(cs_args) {
|
||||
if (select.empty()) return;
|
||||
if (command_options == m_available_args.end()) return;
|
||||
for (const auto& [name, info] : command_options->second) {
|
||||
if (!show_debug && (info.m_flags & ArgsManager::DEBUG_ONLY)) continue;
|
||||
if (!select.contains(name)) continue;
|
||||
fn(name, info);
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& [category, category_args] : m_available_args) {
|
||||
switch(category) {
|
||||
case OptionsCategory::OPTIONS:
|
||||
usage += HelpMessageGroup("Options:");
|
||||
break;
|
||||
@@ -717,22 +742,27 @@ std::string ArgsManager::GetHelpMessage() const
|
||||
case OptionsCategory::CLI_COMMANDS:
|
||||
usage += HelpMessageGroup("CLI Commands:");
|
||||
break;
|
||||
case OptionsCategory::COMMAND_OPTIONS:
|
||||
case OptionsCategory::HIDDEN:
|
||||
break;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
|
||||
// When we get to the hidden options, stop
|
||||
if (arg_map.first == OptionsCategory::HIDDEN) break;
|
||||
if (category == OptionsCategory::COMMAND_OPTIONS) continue;
|
||||
|
||||
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;
|
||||
// When we get to the hidden options, stop
|
||||
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()) continue;
|
||||
for_matching_cmd_opts(cmd_args->second, [&](const auto& cmdopt_name, const auto& cmdopt_info) {
|
||||
usage += HelpMessageOpt(cmdopt_name, cmdopt_info.m_help_param, cmdopt_info.m_help_text, /*subopt=*/true);
|
||||
});
|
||||
}
|
||||
usage += HelpMessageOpt(name, arg.second.m_help_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -750,19 +780,26 @@ void SetupHelpOptions(ArgsManager& args)
|
||||
args.AddHiddenArgs({"-h", "-?"});
|
||||
}
|
||||
|
||||
static const int screenWidth = 79;
|
||||
static const int optIndent = 2;
|
||||
static const int msgIndent = 7;
|
||||
|
||||
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, bool subopt)
|
||||
{
|
||||
constexpr int screen_width = 79;
|
||||
int opt_indent = 2;
|
||||
int msg_indent = 7;
|
||||
|
||||
if (subopt) {
|
||||
int bump = msg_indent - opt_indent;
|
||||
opt_indent += bump; // opt_indent now at the old msg_indent level
|
||||
msg_indent += bump; // indent by the same amount
|
||||
}
|
||||
int msg_width = screen_width - msg_indent;
|
||||
|
||||
return strprintf("%*s%s%s\n%*s%s\n\n",
|
||||
opt_indent, "", option, help_param,
|
||||
msg_indent, "", FormatParagraph(message, msg_width, msg_indent));
|
||||
}
|
||||
|
||||
const std::vector<std::string> TEST_OPTIONS_DOC{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
@@ -67,6 +68,10 @@ enum class OptionsCategory {
|
||||
CLI_COMMANDS,
|
||||
IPC,
|
||||
|
||||
// Specific to one or more commands (OptionsCategory::COMMANDS)
|
||||
// These are only included in help with their associated commands.
|
||||
COMMAND_OPTIONS,
|
||||
|
||||
HIDDEN // Always the last option to avoid printing these in the help
|
||||
};
|
||||
|
||||
@@ -142,6 +147,7 @@ private:
|
||||
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);
|
||||
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);
|
||||
@@ -360,9 +366,9 @@ public:
|
||||
void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
|
||||
|
||||
/**
|
||||
* Add subcommand
|
||||
* Add command
|
||||
*/
|
||||
void AddCommand(const std::string& cmd, const std::string& help) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
|
||||
void AddCommand(const std::string& cmd, const std::string& help, std::set<std::string> options = {}) EXCLUSIVE_LOCKS_REQUIRED(!cs_args);
|
||||
|
||||
/**
|
||||
* Add many hidden arguments
|
||||
@@ -491,10 +497,12 @@ 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 subopt True if this is a suboption, instead of a top-level option.
|
||||
* @return the formatted string
|
||||
*/
|
||||
std::string HelpMessageOpt(const std::string& option, const std::string& 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
|
||||
|
||||
@@ -67,7 +67,8 @@ FUZZ_TARGET(string)
|
||||
(void)HelpExampleCli(random_string_1, random_string_2);
|
||||
(void)HelpExampleRpc(random_string_1, random_string_2);
|
||||
(void)HelpMessageGroup(random_string_1);
|
||||
(void)HelpMessageOpt(random_string_1, random_string_2);
|
||||
(void)HelpMessageOpt(random_string_1, "", random_string_2);
|
||||
(void)HelpMessageOpt(random_string_1, random_string_2, "");
|
||||
(void)IsDeprecatedRPCEnabled(random_string_1);
|
||||
(void)Join(random_string_vector, random_string_1);
|
||||
(void)JSONRPCError(fuzzed_data_provider.ConsumeIntegral<int>(), random_string_1);
|
||||
|
||||
Reference in New Issue
Block a user