Skip to content

Commit aee1d9b

Browse files
committed
qt: refactor console-only command parsing
1 parent 9d3b216 commit aee1d9b

File tree

1 file changed

+86
-28
lines changed

1 file changed

+86
-28
lines changed

src/qt/rpcconsole.cpp

+86-28
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,33 @@ public Q_SLOTS:
9595

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

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

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

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

0 commit comments

Comments
 (0)