mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-30 02:15:54 +02:00
RPC: Add add OBJ_NAMED_PARAMS type
OBJ_NAMED_PARAMS type works the same as OBJ type except it registers the object keys to be accepted as top-level named-only RPC parameters. Generated documentation also lists the object keys seperately in a new "Named arguments" section of help text. Named-only RPC parameters have the same semantics as python keyword-only arguments (https://peps.python.org/pep-3102/). They are always required to be passed by name, so they don't affect interpretation of positional arguments, and aren't affected when positional arguments are added or removed. The new OBJ_NAMED_PARAMS type is used in the next commit to make it easier to pass options values to various RPC methods. Co-authored-by: Andrew Chow <github@achow101.com>
This commit is contained in:
@@ -389,7 +389,8 @@ struct Sections {
|
||||
case RPCArg::Type::NUM:
|
||||
case RPCArg::Type::AMOUNT:
|
||||
case RPCArg::Type::RANGE:
|
||||
case RPCArg::Type::BOOL: {
|
||||
case RPCArg::Type::BOOL:
|
||||
case RPCArg::Type::OBJ_NAMED_PARAMS: {
|
||||
if (is_top_level_arg) return; // Nothing more to do for non-recursive types on first recursion
|
||||
auto left = indent;
|
||||
if (arg.m_opts.type_str.size() != 0 && push_name) {
|
||||
@@ -605,12 +606,17 @@ bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
|
||||
return num_required_args <= num_args && num_args <= m_args.size();
|
||||
}
|
||||
|
||||
std::vector<std::string> RPCHelpMan::GetArgNames() const
|
||||
std::vector<std::pair<std::string, bool>> RPCHelpMan::GetArgNames() const
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
std::vector<std::pair<std::string, bool>> ret;
|
||||
ret.reserve(m_args.size());
|
||||
for (const auto& arg : m_args) {
|
||||
ret.emplace_back(arg.m_names);
|
||||
if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {
|
||||
for (const auto& inner : arg.m_inner) {
|
||||
ret.emplace_back(inner.m_names, /*named_only=*/true);
|
||||
}
|
||||
}
|
||||
ret.emplace_back(arg.m_names, /*named_only=*/false);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -642,20 +648,31 @@ std::string RPCHelpMan::ToString() const
|
||||
|
||||
// Arguments
|
||||
Sections sections;
|
||||
Sections named_only_sections;
|
||||
for (size_t i{0}; i < m_args.size(); ++i) {
|
||||
const auto& arg = m_args.at(i);
|
||||
if (arg.m_opts.hidden) break; // Any arg that follows is also hidden
|
||||
|
||||
if (i == 0) ret += "\nArguments:\n";
|
||||
|
||||
// Push named argument name and description
|
||||
sections.m_sections.emplace_back(::ToString(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString(/*is_named_arg=*/true));
|
||||
sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size());
|
||||
|
||||
// Recursively push nested args
|
||||
sections.Push(arg);
|
||||
|
||||
// Push named-only argument sections
|
||||
if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {
|
||||
for (const auto& arg_inner : arg.m_inner) {
|
||||
named_only_sections.PushSection({arg_inner.GetFirstName(), arg_inner.ToDescriptionString(/*is_named_arg=*/true)});
|
||||
named_only_sections.Push(arg_inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sections.m_sections.empty()) ret += "\nArguments:\n";
|
||||
ret += sections.ToString();
|
||||
if (!named_only_sections.m_sections.empty()) ret += "\nNamed Arguments:\n";
|
||||
ret += named_only_sections.ToString();
|
||||
|
||||
// Result
|
||||
ret += m_results.ToDescriptionString();
|
||||
@@ -669,17 +686,30 @@ std::string RPCHelpMan::ToString() const
|
||||
UniValue RPCHelpMan::GetArgMap() const
|
||||
{
|
||||
UniValue arr{UniValue::VARR};
|
||||
|
||||
auto push_back_arg_info = [&arr](const std::string& rpc_name, int pos, const std::string& arg_name, const RPCArg::Type& type) {
|
||||
UniValue map{UniValue::VARR};
|
||||
map.push_back(rpc_name);
|
||||
map.push_back(pos);
|
||||
map.push_back(arg_name);
|
||||
map.push_back(type == RPCArg::Type::STR ||
|
||||
type == RPCArg::Type::STR_HEX);
|
||||
arr.push_back(map);
|
||||
};
|
||||
|
||||
for (int i{0}; i < int(m_args.size()); ++i) {
|
||||
const auto& arg = m_args.at(i);
|
||||
std::vector<std::string> arg_names = SplitString(arg.m_names, '|');
|
||||
for (const auto& arg_name : arg_names) {
|
||||
UniValue map{UniValue::VARR};
|
||||
map.push_back(m_name);
|
||||
map.push_back(i);
|
||||
map.push_back(arg_name);
|
||||
map.push_back(arg.m_type == RPCArg::Type::STR ||
|
||||
arg.m_type == RPCArg::Type::STR_HEX);
|
||||
arr.push_back(map);
|
||||
push_back_arg_info(m_name, i, arg_name, arg.m_type);
|
||||
if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {
|
||||
for (const auto& inner : arg.m_inner) {
|
||||
std::vector<std::string> inner_names = SplitString(inner.m_names, '|');
|
||||
for (const std::string& inner_name : inner_names) {
|
||||
push_back_arg_info(m_name, i, inner_name, inner.m_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
@@ -708,6 +738,7 @@ static std::optional<UniValue::VType> ExpectedType(RPCArg::Type type)
|
||||
return UniValue::VBOOL;
|
||||
}
|
||||
case Type::OBJ:
|
||||
case Type::OBJ_NAMED_PARAMS:
|
||||
case Type::OBJ_USER_KEYS: {
|
||||
return UniValue::VOBJ;
|
||||
}
|
||||
@@ -781,6 +812,7 @@ std::string RPCArg::ToDescriptionString(bool is_named_arg) const
|
||||
break;
|
||||
}
|
||||
case Type::OBJ:
|
||||
case Type::OBJ_NAMED_PARAMS:
|
||||
case Type::OBJ_USER_KEYS: {
|
||||
ret += "json object";
|
||||
break;
|
||||
@@ -809,6 +841,7 @@ std::string RPCArg::ToDescriptionString(bool is_named_arg) const
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
ret += ")";
|
||||
if (m_type == Type::OBJ_NAMED_PARAMS) ret += " Options object that can be used to pass named arguments, listed below.";
|
||||
ret += m_description.empty() ? "" : " " + m_description;
|
||||
return ret;
|
||||
}
|
||||
@@ -1054,6 +1087,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
|
||||
}
|
||||
return res + "...]";
|
||||
case Type::OBJ:
|
||||
case Type::OBJ_NAMED_PARAMS:
|
||||
case Type::OBJ_USER_KEYS:
|
||||
// Currently unused, so avoid writing dead code
|
||||
NONFATAL_UNREACHABLE();
|
||||
@@ -1077,6 +1111,7 @@ std::string RPCArg::ToString(const bool oneline) const
|
||||
return GetFirstName();
|
||||
}
|
||||
case Type::OBJ:
|
||||
case Type::OBJ_NAMED_PARAMS:
|
||||
case Type::OBJ_USER_KEYS: {
|
||||
const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
|
||||
if (m_type == Type::OBJ) {
|
||||
|
||||
Reference in New Issue
Block a user