Skip to content

Commit bbb3126

Browse files
committed
rpc: add named arg helper
Overload the Arg and MaybeArg helpers to allow accessing arguments by name as well. Also update the docs to document Arg and MaybeArg separately
1 parent 13525e0 commit bbb3126

File tree

3 files changed

+78
-10
lines changed

3 files changed

+78
-10
lines changed

src/rpc/util.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <util/string.h>
2525
#include <util/translation.h>
2626

27+
#include <algorithm>
28+
#include <iterator>
2729
#include <string_view>
2830
#include <tuple>
2931

@@ -728,6 +730,16 @@ std::vector<std::pair<std::string, bool>> RPCHelpMan::GetArgNames() const
728730
return ret;
729731
}
730732

733+
size_t RPCHelpMan::GetParamIndex(std::string_view key) const
734+
{
735+
auto it{std::find_if(
736+
m_args.begin(), m_args.end(), [&key](const auto& arg) { return arg.GetName() == key;}
737+
)};
738+
739+
CHECK_NONFATAL(it != m_args.end()); // TODO: ideally this is checked at compile time
740+
return std::distance(m_args.begin(), it);
741+
}
742+
731743
std::string RPCHelpMan::ToString() const
732744
{
733745
std::string ret;

src/rpc/util.h

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -402,18 +402,25 @@ class RPCHelpMan
402402

403403
UniValue HandleRequest(const JSONRPCRequest& request) const;
404404
/**
405-
* Helper to get a request argument.
406-
* This function only works during m_fun(), i.e. it should only be used in
407-
* RPC method implementations. The helper internally checks whether the
408-
* user-passed argument isNull() and parses (from JSON) and returns the
409-
* user-passed argument, or the default value derived from the RPCArg
410-
* documentation, or a falsy value if no default was given.
405+
* @brief Helper to get a required or default-valued request argument.
411406
*
412-
* Use Arg<Type>(i) to get the argument or its default value. Otherwise,
413-
* use MaybeArg<Type>(i) to get the optional argument or a falsy value.
407+
* Use this function when the argument is required or when it has a default value. If the
408+
* argument is optional and may not be provided, use MaybeArg instead.
414409
*
415-
* The Type passed to this helper must match the corresponding
416-
* RPCArg::Type.
410+
* This function only works during m_fun(), i.e., it should only be used in
411+
* RPC method implementations. It internally checks whether the user-passed
412+
* argument isNull() and parses (from JSON) and returns the user-passed argument,
413+
* or the default value derived from the RPCArg documentation.
414+
*
415+
* There are two overloads of this function:
416+
* - Use Arg<Type>(size_t i) to get the argument (or the default value) by index.
417+
* - Use Arg<Type>(const std::string& key) to get the argument (or the default value) by key.
418+
*
419+
* The Type passed to this helper must match the corresponding RPCArg::Type.
420+
*
421+
* @return The value of the RPC argument (or the default value) cast to type Type.
422+
*
423+
* @see MaybeArg for handling optional arguments without default values.
417424
*/
418425
template <typename R>
419426
auto Arg(size_t i) const
@@ -427,6 +434,34 @@ class RPCHelpMan
427434
return ArgValue<const R&>(i);
428435
}
429436
}
437+
template<typename R>
438+
auto Arg(std::string_view key) const
439+
{
440+
return Arg<R>(GetParamIndex(key));
441+
}
442+
/**
443+
* @brief Helper to get an optional request argument.
444+
*
445+
* Use this function when the argument is optional and does not have a default value. If the
446+
* argument is required or has a default value, use Arg instead.
447+
*
448+
* This function only works during m_fun(), i.e., it should only be used in
449+
* RPC method implementations. It internally checks whether the user-passed
450+
* argument isNull() and parses (from JSON) and returns the user-passed argument,
451+
* or a falsy value if no argument was passed.
452+
*
453+
* There are two overloads of this function:
454+
* - Use MaybeArg<Type>(size_t i) to get the optional argument by index.
455+
* - Use MaybeArg<Type>(const std::string& key) to get the optional argument by key.
456+
*
457+
* The Type passed to this helper must match the corresponding RPCArg::Type.
458+
*
459+
* @return For integral and floating-point types, a std::optional<Type> is returned.
460+
* For other types, a Type* pointer to the argument is returned. If the
461+
* argument is not provided, std::nullopt or a null pointer is returned.
462+
*
463+
* @see Arg for handling arguments that are required or have a default value.
464+
*/
430465
template <typename R>
431466
auto MaybeArg(size_t i) const
432467
{
@@ -439,6 +474,11 @@ class RPCHelpMan
439474
return ArgValue<const R*>(i);
440475
}
441476
}
477+
template<typename R>
478+
auto MaybeArg(std::string_view key) const
479+
{
480+
return MaybeArg<R>(GetParamIndex(key));
481+
}
442482
std::string ToString() const;
443483
/** Return the named args that need to be converted from string to another JSON type */
444484
UniValue GetArgMap() const;
@@ -458,6 +498,8 @@ class RPCHelpMan
458498
mutable const JSONRPCRequest* m_req{nullptr}; // A pointer to the request for the duration of m_fun()
459499
template <typename R>
460500
R ArgValue(size_t i) const;
501+
//! Return positional index of a parameter using its name as key.
502+
size_t GetParamIndex(std::string_view key) const;
461503
};
462504

463505
/**

src/test/rpc_tests.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,20 @@ BOOST_AUTO_TEST_CASE(rpc_arg_helper)
633633
};
634634
CheckRpc(params, UniValue{JSON(R"([5, "hello", null, null, null, null, null])")}, check_positional);
635635
CheckRpc(params, UniValue{JSON(R"([5, "hello", 4, "test", true, 1.23, "world"])")}, check_positional);
636+
637+
//! Check that `self.Arg` returns the same value when using index and key
638+
RPCHelpMan::RPCMethodImpl check_named = [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
639+
BOOST_CHECK_EQUAL(self.Arg<int>(0), self.Arg<int>("req_int"));
640+
BOOST_CHECK_EQUAL(self.Arg<std::string>(1), self.Arg<std::string>("req_str"));
641+
BOOST_CHECK_EQUAL(self.Arg<uint64_t>(2), self.Arg<uint64_t>("def_uint64_t"));
642+
BOOST_CHECK_EQUAL(self.Arg<std::string>(3), self.Arg<std::string>("def_string"));
643+
BOOST_CHECK_EQUAL(self.Arg<bool>(4), self.Arg<bool>("def_bool"));
644+
BOOST_CHECK(self.MaybeArg<double>(5) == self.MaybeArg<double>("opt_double"));
645+
BOOST_CHECK(self.MaybeArg<std::string>(6) == self.MaybeArg<std::string>("opt_string"));
646+
return UniValue{};
647+
};
648+
CheckRpc(params, UniValue{JSON(R"([5, "hello", null, null, null, null, null])")}, check_named);
649+
CheckRpc(params, UniValue{JSON(R"([5, "hello", 4, "test", true, 1.23, "world"])")}, check_named);
636650
}
637651

638652
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)