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();