Repository: bitcoin/bitcoin
File: src/rpc/client.cpp (C++)

Code before:

static CRPCConvertTable rpcCvtTable;

UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
{
    UniValue params(UniValue::VARR);

    for (unsigned int idx = 0; idx < strParams.size(); idx++) {
        std::string_view value{strParams[idx]};
        params.push_back(rpcCvtTable.ArgToUniValue(value, strMethod, idx));
    }

    return params;
}

UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<std::string> &strParams)
{
    UniValue params(UniValue::VOBJ);
    UniValue positional_args{UniValue::VARR};

    for (std::string_view s: strParams) {
        size_t pos = s.find('=');
        if (pos == std::string::npos) {
            positional_args.push_back(rpcCvtTable.ArgToUniValue(s, strMethod, positional_args.size()));
            continue;
        }

        std::string name{s.substr(0, pos)};
        std::string_view value{s.substr(pos+1)};

        // Intentionally overwrite earlier named values with later ones as a
        // convenience for scripts and command line users that want to merge
        // options.
        params.pushKV(name, rpcCvtTable.ArgToUniValue(value, strMethod, name));
    }

    if (!positional_args.empty()) {
        // Use pushKVEnd instead of pushKV to avoid overwriting an explicit
        // "args" value with an implicit one. Let the RPC server handle the
        // request as given.
        params.pushKVEnd("args", std::move(positional_args));
    }

    return params;
}


Review comment (suggestion):
In commit "rpc: Handle -named argument parsing where '=' character is used" (03068e67cd9a62aa9105ca6a4c971b6261a783c5)

Every place this function is called, it called with a `size_t` value cast to an `int`. It would seem nice to make it take an a `size_t` value directly, since it is a new function. (Could also change the `CRPCConvertParam` struct to use `size_t` instead of `int` but would suggest not doing that to keep the changes minimal.)

Code after:
    { "addnode", 2, "v2transport" },
    { "addconnection", 2, "v2transport" },
    { "decodepsbt", 0, "psbt", ParamFormat::STRING },
    { "analyzepsbt", 0, "psbt", ParamFormat::STRING},
    { "verifymessage", 1, "signature", ParamFormat::STRING },
    { "verifymessage", 2, "message", ParamFormat::STRING },
    { "getnewaddress", 0, "label", ParamFormat::STRING },
    { "getnewaddress", 1, "address_type", ParamFormat::STRING },
    { "backupwallet", 0, "destination", ParamFormat::STRING },
    { "echoipc", 0, "arg", ParamFormat::STRING },
    { "encryptwallet", 0, "passphrase", ParamFormat::STRING },
    { "getaddressesbylabel", 0, "label", ParamFormat::STRING },
    { "loadtxoutset", 0, "path", ParamFormat::STRING },
    { "migratewallet", 0, "wallet_name", ParamFormat::STRING },
    { "migratewallet", 1, "passphrase", ParamFormat::STRING },
    { "setlabel", 1, "label", ParamFormat::STRING },
    { "signmessage", 1, "message", ParamFormat::STRING },
    { "signmessagewithprivkey", 1, "message", ParamFormat::STRING },
    { "walletpassphrasechange", 0, "oldpassphrase", ParamFormat::STRING },
    { "walletpassphrasechange", 1, "newpassphrase", ParamFormat::STRING },
};
// clang-format on

/** Parse string to UniValue or throw runtime_error if string contains invalid JSON */
static UniValue Parse(std::string_view raw, ParamFormat format = ParamFormat::JSON)
{
    UniValue parsed;
    if (!parsed.read(raw)) {
        if (format != ParamFormat::JSON_OR_STRING) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw));
        return UniValue(std::string(raw));
    }
    return parsed;
}

namespace rpc_convert
{
const CRPCConvertParam* FromPosition(std::string_view method, size_t pos)
{
    auto it = std::ranges::find_if(vRPCConvertParams, [&](const auto& p) {
        return p.methodName == method && p.paramIdx == static_cast<int>(pos);
    });

    return it == std::end(vRPCConvertParams) ? nullptr : &*it;
}

const CRPCConvertParam* FromName(std::string_view method, std::string_view name)
{
    auto it = std::ranges::find_if(vRPCConvertParams, [&](const auto& p) {
        return p.methodName == method && p.paramName == name;
    });