Merge #20715: util: Add ArgsManager::GetCommand() and use it in bitcoin-wallet

fa61b9d1a6 util: Add ArgsManager::GetCommand() and use it in bitcoin-wallet (MarcoFalke)
7777105a24 refactor: Move all command dependend checks to ExecuteWalletToolFunc (MarcoFalke)
fa06bce4ac test: Add tests (MarcoFalke)
fac05ccdad wallet: [refactor] Pass ArgsManager to WalletAppInit (MarcoFalke)

Pull request description:

  This not only moves the parsing responsibility out from the wallet tool, but it also makes it easier to implement bitcoin-util #19937

  Fixes: #20902

ACKs for top commit:
  ajtowns:
    ACK fa61b9d1a6
  fjahr:
    Code review ACK fa61b9d1a6

Tree-SHA512: 79622b806e8bf9dcd0dc24a8a6687345710df57720992e83a41cd8d6762a6dc112044ebc58fcf6e8fbf45de29a79b04873c5b8c2494a1eaaf902a2884703e47b
This commit is contained in:
MarcoFalke
2021-02-04 09:11:54 +01:00
7 changed files with 115 additions and 51 deletions

View File

@@ -3,7 +3,6 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
#include <util/system.h>
#ifdef HAVE_BOOST_PROCESS
@@ -11,6 +10,8 @@
#endif // HAVE_BOOST_PROCESS
#include <chainparamsbase.h>
#include <sync.h>
#include <util/check.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/translation.h>
@@ -310,8 +311,22 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
key[0] = '-';
#endif
if (key[0] != '-')
if (key[0] != '-') {
if (!m_accept_any_command && m_command.empty()) {
// The first non-dash arg is a registered command
Optional<unsigned int> flags = GetArgFlags(key);
if (!flags || !(*flags & ArgsManager::COMMAND)) {
error = strprintf("Invalid command '%s'", argv[i]);
return false;
}
}
m_command.push_back(key);
while (++i < argc) {
// The remaining args are command args
m_command.push_back(argv[i]);
}
break;
}
// Transform --foo to -foo
if (key.length() > 1 && key[1] == '-')
@@ -359,6 +374,26 @@ Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
return nullopt;
}
std::optional<const ArgsManager::Command> ArgsManager::GetCommand() const
{
Command ret;
LOCK(cs_args);
auto it = m_command.begin();
if (it == m_command.end()) {
// No command was passed
return std::nullopt;
}
if (!m_accept_any_command) {
// The registered command
ret.command = *(it++);
}
while (it != m_command.end()) {
// The unregistered command and args (if any)
ret.args.push_back(*(it++));
}
return ret;
}
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
{
std::vector<std::string> result;
@@ -504,8 +539,22 @@ 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, const OptionsCategory& cat)
{
Assert(cmd.find('=') == std::string::npos);
Assert(cmd.at(0) != '-');
LOCK(cs_args);
m_accept_any_command = false; // latch to false
std::map<std::string, Arg>& arg_map = m_available_args[cat];
auto ret = arg_map.emplace(cmd, Arg{"", help, ArgsManager::COMMAND});
Assert(ret.second); // Fail on duplicate commands
}
void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
{
Assert((flags & ArgsManager::COMMAND) == 0); // use AddCommand
// Split arg name from its help param
size_t eq_index = name.find('=');
if (eq_index == std::string::npos) {

View File

@@ -181,6 +181,7 @@ public:
NETWORK_ONLY = 0x200,
// This argument's value is sensitive (such as a password).
SENSITIVE = 0x400,
COMMAND = 0x800,
};
protected:
@@ -193,9 +194,11 @@ protected:
mutable RecursiveMutex cs_args;
util::Settings m_settings GUARDED_BY(cs_args);
std::vector<std::string> m_command GUARDED_BY(cs_args);
std::string m_network GUARDED_BY(cs_args);
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);
bool m_accept_any_command GUARDED_BY(cs_args){true};
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
@@ -246,6 +249,20 @@ public:
*/
const std::list<SectionInfo> GetUnrecognizedSections() const;
struct Command {
/** The command (if one has been registered with AddCommand), or empty */
std::string command;
/**
* If command is non-empty: Any args that followed it
* If command is empty: The unregistered command and any args that followed it
*/
std::vector<std::string> args;
};
/**
* Get the command and command args (returns std::nullopt if no command provided)
*/
std::optional<const Command> GetCommand() const;
/**
* Return a vector of strings of the given argument
*
@@ -331,6 +348,11 @@ public:
*/
void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
/**
* Add subcommand
*/
void AddCommand(const std::string& cmd, const std::string& help, const OptionsCategory& cat);
/**
* Add many hidden arguments
*/