Macro tokio_jsonrpc::jsonrpc_params
[−]
[src]
macro_rules! jsonrpc_params { ( $value:expr, ) => { ... }; ( $value:expr ) => { ... }; ( $value:expr, single $vartype:ty ) => { ... }; ( arity $head:ty ) => { ... }; ( arity $head:ty, $( $tail:ty ),* ) => { ... }; ( $spl:expr, accum ( $( $result:tt )* ), positional_decode $vtype:ty ) => { ... }; ( $spl:expr, accum ( $( $result:tt )* ), positional_decode $htype:ty, $( $ttype:ty ),+ ) => { ... }; ( $value:expr, positional $( $vartype:ty ),+ ) => { ... }; ( $value:expr, named $( $varname:expr => $vartype:ty ),+ ) => { ... }; ( $value:expr, decide $( $varname:expr => $vartype:ty ),+ ) => { ... }; ( $value:expr, $varname:expr => $vartype:ty ) => { ... }; ( $value:expr, $( $varname:expr => $vartype:ty ),+ ) => { ... }; ( $value:expr, wrap $( $varname:expr => $vartype:ty ),+ ) => { ... }; ( $value:expr, wrap named $( $varname:expr => $vartype:ty ),+ ) => { ... }; ( $value:expr, wrap positional $( $vartype:ty ),+ ) => { ... }; }
Parses the parameters of an RPC or a notification.
The Server
receives &Option<Value>
as the parameters when its
notification
or rpc
method is called and it needs to handle it. This means checking for
validity and converting it to appropriate types. This is tedious.
This macro helps with that. In it's basic usage, you pass the received parameters and parameter
definitions for what you want and receive a tuple of converted ones. In case the parameters are
invalid, it returns early with an Some(IntoFuture<_, Error = RpcError>)
directly from your
function (which is the return type of the callbacks).
Note that while this macro may be used directly, the macro
jsonrpc_server_impl
which builds the whole Server
trait
implementation uses it internally and it is the preferred way to use it.
By default, it accepts both parameters passed by a name (inside a JSON object) or by position
(inside an array). If you insist your clients must use named or positional parameters, prefix
the parameter definitions by named
or positional
respectively. The positional
omits the
parameter names in the definitions.
This also handles optional arguments in the named
case (and when auto-detecting and a JSON
object is provided).
If an empty parameter definition is provided, the macro checks that no parameters were sent
(accepts no parameters sent, null
sent as parameters and an empty object or array).
If a single parameter is passed, in addition to trying positional and named parameters, the
macro tries to convert the whole Value
into the given type. This allows you to ask for
a structure that holds all the named parameters.
If you want to force the single parameter conversion of the whole Value
, you can use the
macro as jsonrpc_params!(params, single Type)
. However, in this case it returns
Result<Type, RpcError>
‒ since it is expected you might want to try both named and
positional decoding yourself. Also, it expects &Value
, not &Option<Value>
.
You can also force the macro to return the Result<(Type, Type, ...), RpcError>
if you prefer,
by prefixing the parameter definitions with the wrap
token.
The macro has other variants than the mentioned here. They are mostly used internally by the macro itself and aren't meant to be used directly.
Examples
Basic usage, parse an integer and a boolean:
fn parse(params: &Option<Value>) -> Option<Result<(i32, bool), RpcError>> { Some(Ok(jsonrpc_params!(params, "num" => i32, "b" => bool))) } assert_eq!((42, true), parse(&Some(json!([42, true]))).unwrap().unwrap()); assert_eq!((42, true), parse(&Some(json!({"num": 42, "b": true}))).unwrap().unwrap()); parse(&None).unwrap().unwrap_err(); parse(&Some(json!({"num": "hello", "b": false}))).unwrap().unwrap_err(); // Return by the macro instead of exit assert_eq!((42, true), jsonrpc_params!(&Some(json!({"num": 42, "b": true})), wrap "num" => i32, "b" => bool).unwrap()); jsonrpc_params!(&None, wrap "num" => i32, "b" => bool).unwrap_err();
Usage with enforcing named parameters. Also, the integer is optional. Enforcing positional
works in a similar way, with the positional
token.
fn parse(params: &Option<Value>) -> Option<Result<(Option<i32>, bool), RpcError>> { Some(Ok(jsonrpc_params!(params, named "num" => Option<i32>, "b" => bool))) } parse(&Some(json!([42, true]))).unwrap().unwrap_err(); assert_eq!((Some(42), true), parse(&Some(json!({"num": 42, "b": true}))).unwrap().unwrap()); assert_eq!((None, false), parse(&Some(json!({"b": false, "extra": "ignored"}))).unwrap().unwrap()); parse(&None).unwrap().unwrap_err(); parse(&Some(json!({"num": "hello", "b": false}))).unwrap().unwrap_err();
Enforcing positional parameters:
fn parse(params: &Option<Value>) -> Option<Result<(i32, bool), RpcError>> { Some(Ok(jsonrpc_params!(params, positional i32, bool))) } assert_eq!((42, true), parse(&Some(json!([42, true]))).unwrap().unwrap());
Decoding into a structure works like this:
#[derive(PartialEq, Debug, Deserialize)] struct Params { num: Option<i32>, b: bool, } fn parse(params: &Option<Value>) -> Option<Result<Params, RpcError>> { let (params,) = jsonrpc_params!(params, "params" => Params); Some(Ok(params)) } let expected = Params { num: Some(42), b: true, }; let expected_optional = Params { num: None, b: false, }; assert_eq!(expected, parse(&Some(json!([42, true]))).unwrap().unwrap()); assert_eq!(expected, parse(&Some(json!({"num": 42, "b": true}))).unwrap().unwrap()); assert_eq!(expected_optional, parse(&Some(json!({"b": false}))).unwrap().unwrap()); // This is accepted mostly as a side effect. assert_eq!(expected, parse(&Some(json!([{"num": 42, "b": true}]))).unwrap().unwrap()); // As is this. assert_eq!(expected, parse(&Some(json!({"params": {"num": 42, "b": true}}))).unwrap().unwrap()); // If you mind the above limitations, you can ask directly for a single value decoding. // That returs a Result directly. assert_eq!(expected, jsonrpc_params!(&json!({"num": 42, "b": true}), single Params).unwrap()); jsonrpc_params!(&json!([{"num": 42, "b": true}]), single Params).unwrap_err();