mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-19 14:53:43 +01:00
rpc: Allow named and positional arguments to be used together
It's nice to be able to use named options and positional arguments together.
Most shell tools accept both, and python functions combine options and
arguments allowing them to be passed with even more flexibility. This change
adds support for python's approach so as a motivating example:
bitcoin-cli -named createwallet wallet_name=mywallet load_on_startup=1
Can be shortened to:
bitcoin-cli -named createwallet mywallet load_on_startup=1
JSON-RPC standard doesn't have a convention for passing named and positional
parameters together, so this implementation makes one up and interprets any
unused "args" named parameter as a positional parameter array.
This commit is contained in:
@@ -401,8 +401,16 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||
for (size_t i=0; i<keys.size(); ++i) {
|
||||
argsIn[keys[i]] = &values[i];
|
||||
}
|
||||
// Process expected parameters.
|
||||
// Process expected parameters. If any parameters were left unspecified in
|
||||
// the request before a parameter that was specified, null values need to be
|
||||
// inserted at the unspecifed parameter positions, and the "hole" variable
|
||||
// below tracks the number of null values that need to be inserted.
|
||||
// The "initial_hole_size" variable stores the size of the initial hole,
|
||||
// i.e. how many initial positional arguments were left unspecified. This is
|
||||
// used after the for-loop to add initial positional arguments from the
|
||||
// "args" parameter, if present.
|
||||
int hole = 0;
|
||||
int initial_hole_size = 0;
|
||||
for (const std::string &argNamePattern: argNames) {
|
||||
std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
|
||||
auto fr = argsIn.end();
|
||||
@@ -424,6 +432,24 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||
argsIn.erase(fr);
|
||||
} else {
|
||||
hole += 1;
|
||||
if (out.params.empty()) initial_hole_size = hole;
|
||||
}
|
||||
}
|
||||
// If leftover "args" param was found, use it as a source of positional
|
||||
// arguments and add named arguments after. This is a convenience for
|
||||
// clients that want to pass a combination of named and positional
|
||||
// arguments as described in doc/JSON-RPC-interface.md#parameter-passing
|
||||
auto positional_args{argsIn.extract("args")};
|
||||
if (positional_args && positional_args.mapped()->isArray()) {
|
||||
const bool has_named_arguments{initial_hole_size < (int)argNames.size()};
|
||||
if (initial_hole_size < (int)positional_args.mapped()->size() && has_named_arguments) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + argNames[initial_hole_size] + " specified twice both as positional and named argument");
|
||||
}
|
||||
// Assign positional_args to out.params and append named_args after.
|
||||
UniValue named_args{std::move(out.params)};
|
||||
out.params = *positional_args.mapped();
|
||||
for (size_t i{out.params.size()}; i < named_args.size(); ++i) {
|
||||
out.params.push_back(named_args[i]);
|
||||
}
|
||||
}
|
||||
// If there are still arguments in the argsIn map, this is an error.
|
||||
|
||||
Reference in New Issue
Block a user