Skip to content

Commit 1bf0c07

Browse files
committed
qt: refactor console-only command parsing
1 parent cad2df2 commit 1bf0c07

File tree

1 file changed

+86
-28
lines changed

1 file changed

+86
-28
lines changed

src/qt/rpcconsole.cpp

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,33 @@ public Q_SLOTS:
9696

9797
private:
9898
interfaces::Node& m_node;
99+
bool executeConsoleHelpConsole(const std::vector<std::string>& parsed_command, const WalletModel* wallet_model, const bool exec_help = false);
100+
bool executeConsoleOnlyCommand(const std::string& command, const WalletModel* wallet_model);
101+
// std::map mapping strings to methods member of RPCExecutor class
102+
// Keys must be strings with commands and (optionally) parameters in "canonical" form (separated by single space)
103+
// Keys should match the beggining of user input commands (user commands can have more parameters than the key)
104+
std::map<std::string, bool (RPCExecutor::*)(const std::vector<std::string>&, const WalletModel*, const bool)> m_method_map{
105+
{"help-console", &RPCExecutor::executeConsoleHelpConsole}};
99106
};
100107

108+
/**
109+
* Small and fast parser supporting console command syntax, with limited functionality.
110+
* Splits a command line string into a vector with command at position 0, and parameters after
111+
*
112+
* @param[in] strCommand Command line to parse
113+
*
114+
* @return a vector of strings with command and parameters
115+
*/
116+
std::vector<std::string> parseHelper(const std::string& strCommand)
117+
{
118+
// Split while recognizing the several characters that can be used as separators in the GUI console
119+
std::vector<std::string> vec{SplitString(strCommand, " (),")};
120+
// Remove empty strings produced by consecutive separators
121+
auto should_remove{[](const std::string& str) { return str.empty(); }};
122+
vec.erase(std::remove_if(vec.begin(), vec.end(), should_remove), vec.end());
123+
return vec;
124+
}
125+
101126
/** Class for handling RPC timers
102127
* (used for e.g. re-locking the wallet after a timeout)
103128
*/
@@ -415,35 +440,15 @@ void RPCExecutor::request(const QString &command, const WalletModel* wallet_mode
415440
std::string result;
416441
std::string executableCommand = command.toStdString() + "\n";
417442

418-
// Catch the console-only-help command before RPC call is executed and reply with help text as-if a RPC reply.
419-
if(executableCommand == "help-console\n") {
420-
Q_EMIT reply(RPCConsole::CMD_REPLY, QString(("\n"
421-
"This console accepts RPC commands using the standard syntax.\n"
422-
" example: getblockhash 0\n\n"
423-
424-
"This console can also accept RPC commands using the parenthesized syntax.\n"
425-
" example: getblockhash(0)\n\n"
426-
427-
"Commands may be nested when specified with the parenthesized syntax.\n"
428-
" example: getblock(getblockhash(0) 1)\n\n"
429-
430-
"A space or a comma can be used to delimit arguments for either syntax.\n"
431-
" example: getblockhash 0\n"
432-
" getblockhash,0\n\n"
433-
434-
"Named results can be queried with a non-quoted key string in brackets using the parenthesized syntax.\n"
435-
" example: getblock(getblockhash(0) 1)[tx]\n\n"
436-
437-
"Results without keys can be queried with an integer in brackets using the parenthesized syntax.\n"
438-
" example: getblock(getblockhash(0),1)[tx][0]\n\n")));
439-
return;
440-
}
441-
if (!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, wallet_model)) {
442-
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
443-
return;
443+
// Attempt to execute console-only commands
444+
if (!RPCExecutor::executeConsoleOnlyCommand(command.toStdString(), wallet_model)) {
445+
// Send to the RPC command parser if not console-only
446+
if (!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, wallet_model)) {
447+
Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
448+
return;
449+
}
450+
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
444451
}
445-
446-
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
447452
}
448453
catch (UniValue& objError)
449454
{
@@ -464,6 +469,59 @@ void RPCExecutor::request(const QString &command, const WalletModel* wallet_mode
464469
}
465470
}
466471

472+
/**
473+
* @brief Executes the console-only command "help-console".
474+
* @param parsed_command A vector of strings with command and parameters, usually generated by RPCExecutor::parseHelper
475+
* @param wallet_model WalletModel to use for the command
476+
* @return True if the command was executed, false otherwise.
477+
*/
478+
bool RPCExecutor::executeConsoleHelpConsole(const std::vector<std::string>& parsed_command, const WalletModel* wallet_model, const bool exec_help)
479+
{
480+
// Reply with help text as-if a RPC reply.
481+
Q_EMIT reply(RPCConsole::CMD_REPLY,
482+
QString("\n"
483+
"This console accepts RPC commands using the standard syntax.\n"
484+
" example: getblockhash 0\n\n"
485+
"This console can also accept RPC commands using the parenthesized syntax.\n"
486+
" example: getblockhash(0)\n\n"
487+
"Commands may be nested when specified with the parenthesized syntax.\n"
488+
" example: getblock(getblockhash(0) 1)\n\n"
489+
"A space or a comma can be used to delimit arguments for either syntax.\n"
490+
" example: getblockhash 0\n"
491+
" getblockhash,0\n\n"
492+
"Named results can be queried with a non-quoted key string in brackets using the parenthesized syntax.\n"
493+
" example: getblock(getblockhash(0) 1)[tx]\n\n"
494+
"Results without keys can be queried with an integer in brackets using the parenthesized syntax.\n"
495+
" example: getblock(getblockhash(0),1)[tx][0]\n\n"));
496+
return true;
497+
}
498+
499+
/**
500+
* Catches console-only command before a RPC call is executed
501+
*
502+
* @param[in] command Command line to execute
503+
* @param[in] wallet_model Wallet model to use
504+
* @return true if command was handled by this method (even on errors), false otherwise
505+
*
506+
*/
507+
bool RPCExecutor::executeConsoleOnlyCommand(const std::string& command, const WalletModel* wallet_model)
508+
{
509+
// Parse command line into a vector of strings
510+
const std::vector<std::string> parsed_command{parseHelper(command)};
511+
512+
if (parsed_command.empty()) return false;
513+
514+
std::string method = parsed_command[0];
515+
bool exec_help = false;
516+
if (method == "help" && parsed_command.size() > 1) {
517+
exec_help = true;
518+
method = parsed_command[1];
519+
}
520+
auto it_method = m_method_map.find(method);
521+
if (it_method == m_method_map.end()) return false; // method not found
522+
return (this->*(it_method->second))(parsed_command, wallet_model, exec_help);
523+
}
524+
467525
RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformStyle, QWidget *parent) :
468526
QWidget(parent),
469527
m_node(node),

0 commit comments

Comments
 (0)