easy_jsonrpc/
lib.rs

1/*!
2# Easy jsonrpc
3
4Generates rpc handlers based on a trait definition.
5
6## Defining an api
7
8```rust
9use easy_jsonrpc;
10
11#[easy_jsonrpc::rpc]
12pub trait Adder {
13    fn checked_add(&self, a: isize, b: isize) -> Option<isize>;
14    fn wrapping_add(&self, a: isize, b: isize) -> isize;
15    fn is_some(&self, a: Option<usize>) -> bool {
16        a.is_some()
17    }
18    fn takes_ref(&self, rf: &isize);
19}
20```
21
22The rpc macro generates
231. An implementaion of the Handler trait for &dyn Adder
242. A helper module for rpc clients
25
26## Server side usage
27
28```rust
29# use easy_jsonrpc;
30# #[easy_jsonrpc::rpc]
31# pub trait Adder {
32#     fn checked_add(&self, a: isize, b: isize) -> Option<isize>;
33#     fn wrapping_add(&self, a: isize, b: isize) -> isize;
34#     fn is_some(&self, a: Option<usize>) -> bool {
35#         a.is_some()
36#     }
37#     fn takes_ref(&self, rf: &isize);
38# }
39use easy_jsonrpc::{Handler, MaybeReply};
40use serde_json::json;
41
42struct AdderImpl;
43
44impl Adder for AdderImpl {
45    fn checked_add(&self, a: isize, b: isize) -> Option<isize> { a.checked_add(b) }
46    fn wrapping_add(&self, a: isize, b: isize) -> isize { a.wrapping_add(b) }
47    fn takes_ref(&self, rf: &isize) {}
48}
49
50let handler = (&AdderImpl {} as &dyn Adder);
51
52assert_eq!(
53    handler.handle_request(json!({
54        "jsonrpc": "2.0",
55        "method": "wrapping_add",
56        "params": [1, 2],
57        "id": 1
58    })),
59    MaybeReply::Reply(json!({
60        "jsonrpc": "2.0",
61        "result": 3,
62        "id": 1
63    }))
64);
65```
66
67## Client side usage
68
69```rust
70# use easy_jsonrpc;
71# #[easy_jsonrpc::rpc]
72# pub trait Adder {
73#     fn checked_add(&self, a: isize, b: isize) -> Option<isize>;
74#     fn wrapping_add(&self, a: isize, b: isize) -> isize;
75#     fn is_some(&self, a: Option<usize>) -> bool;
76#     fn takes_ref(&self, rf: &isize);
77# }
78# use easy_jsonrpc::{Handler, MaybeReply};
79# use serde_json::json;
80# struct AdderImpl;
81# impl Adder for AdderImpl {
82#     fn checked_add(&self, a: isize, b: isize) -> Option<isize> {
83#         a.checked_add(b)
84#     }
85#    fn wrapping_add(&self, a: isize, b: isize) -> isize {
86#        a.wrapping_add(b)
87#    }
88#    fn is_some(&self, a: Option<usize>) -> bool {
89#        a.is_some()
90#    }
91#    fn takes_ref(&self, rf: &isize) {}
92# }
93# let handler = (&AdderImpl {} as &dyn Adder);
94let bind = adder::checked_add(1, 2).unwrap();
95let (call, tracker) = bind.call();
96let json_response = match handler.handle_request(call.as_request()) {
97   MaybeReply::Reply(resp) => resp,
98   MaybeReply::DontReply => panic!(),
99};
100let mut response = easy_jsonrpc::Response::from_json_response(json_response).unwrap();
101assert_eq!(tracker.get_return(&mut response).unwrap(), Some(3));
102```
103
104## Bonus bits
105
106```rust
107# use easy_jsonrpc;
108# #[easy_jsonrpc::rpc]
109# pub trait Adder {
110#     fn checked_add(&self, a: isize, b: isize) -> Option<isize>;
111#     fn wrapping_add(&self, a: isize, b: isize) -> isize;
112#     fn is_some(&self, a: Option<usize>) -> bool;
113#     fn takes_ref(&self, rf: &isize);
114# }
115# use easy_jsonrpc::{Handler, MaybeReply};
116# use serde_json::json;
117# struct AdderImpl;
118# impl Adder for AdderImpl {
119#     fn checked_add(&self, a: isize, b: isize) -> Option<isize> {
120#         a.checked_add(b)
121#     }
122#    fn wrapping_add(&self, a: isize, b: isize) -> isize {
123#        a.wrapping_add(b)
124#    }
125#    fn is_some(&self, a: Option<usize>) -> bool {
126#        a.is_some()
127#    }
128#    fn takes_ref(&self, rf: &isize) {}
129# }
130# let handler = (&AdderImpl {} as &dyn Adder);
131// Named arguments are handled for free
132assert_eq!(
133    handler.handle_request(json!({
134        "jsonrpc": "2.0",
135        "method": "wrapping_add",
136        "params": {
137            "a": 1,
138            "b": 2
139        },
140        "id": 1
141    })),
142    MaybeReply::Reply(json!({
143        "jsonrpc": "2.0",
144        "result": 3,
145        "id": 1
146    }))
147);
148
149// Notifications (calls without an id) are handled sanely
150assert_eq!(
151    handler.handle_request(json!({
152        "jsonrpc": "2.0",
153        "method": "wrapping_add",
154        "params": [1, 1]
155    })),
156    MaybeReply::DontReply
157);
158
159// Notification are easy to generate
160let bind = adder::checked_add(0, 0).unwrap();
161let notification = bind.notification().as_request();
162assert_eq!(handler.handle_request(notification), MaybeReply::DontReply);
163
164// Batch calls are possible
165use easy_jsonrpc::Call;
166let bind0 = adder::checked_add(0, 0).unwrap();
167let (call0, tracker0) = bind0.call();
168let bind1 = adder::checked_add(1, 0).unwrap();
169let (call1, tracker1) = bind1.call();
170let bind2 = adder::wrapping_add(1, 1).unwrap();
171let (call2, tracker2) = bind2.call();
172let bind3 = adder::wrapping_add(1, 1).unwrap();
173let call3 = bind3.notification();
174let json_request = Call::batch_request(&[call0, call1, call2, call3]);
175let json_response = match handler.handle_request(json_request) {
176   MaybeReply::Reply(resp) => resp,
177   MaybeReply::DontReply => panic!(),
178};
179let mut response = easy_jsonrpc::Response::from_json_response(json_response).unwrap();
180assert_eq!(tracker1.get_return(&mut response).unwrap(), Some(1));
181assert_eq!(tracker0.get_return(&mut response).unwrap(), Some(0));
182assert_eq!(tracker2.get_return(&mut response).unwrap(), 2);
183```
184 */
185
186#![deny(missing_docs)]
187
188const SERIALZATION_ERROR: i64 = -32000;
189
190pub use easy_jsonrpc_proc_macro::rpc;
191
192// used from generated code
193#[doc(hidden)]
194pub use jsonrpc_core::types::{
195    self, Error, ErrorCode, Failure, Id, MethodCall, Notification, Output, Success, Version,
196};
197#[doc(hidden)]
198use serde::de::Deserialize;
199#[doc(hidden)]
200pub use serde_json::{self, Value};
201
202use rand;
203use serde::ser::Serialize;
204use serde_json::json;
205use std::{collections::BTreeMap, marker::PhantomData};
206
207/// Handles jsonrpc requests.
208pub trait Handler {
209    /// Type-check params and call method if method exists. This method is implemented automatically
210    /// by the [rpc](../easy_jsonrpc_proc_macro/attr.rpc.html) macro.
211    fn handle(&self, method: &str, params: Params) -> Result<Value, jsonrpc_core::Error>;
212
213    /// Parses raw_request as a jsonrpc request, handles request according to the jsonrpc spec.
214    fn handle_request(&self, raw_request: Value) -> MaybeReply {
215        let request: jsonrpc_core::Request = match serde_json::from_value(raw_request) {
216            Ok(request) => request,
217            Err(_) => {
218                return MaybeReply::Reply(serde_json::json!({
219                    "jsonrpc": "2.0",
220                    "error": {
221                        "code": -32700,
222                        "message": "Parse error"
223                    },
224                    "id": null
225                }));
226            }
227        };
228        let response = match handle_parsed_request(self, request) {
229            Some(ret) => ret,
230            None => return MaybeReply::DontReply,
231        };
232        MaybeReply::Reply(serde_json::to_value(response).unwrap_or_else(|e| {
233            serde_json::json!({
234                "jsonrpc": "2.0",
235                "error": {
236                    "code": SERIALZATION_ERROR,
237                    "message": "Serialization error",
238                    "data": format!("{}", e),
239                },
240                "id": null
241            })
242        }))
243    }
244}
245
246/// Returned by Handler::handle_request
247#[derive(Clone, PartialEq, Debug)]
248pub enum MaybeReply {
249    /// The value should be serialized and returned to the client.
250    Reply(Value),
251    /// The request consisted solely of notifications. No reply is necessary.
252    DontReply,
253}
254
255impl MaybeReply {
256    /// Convert to optional value.
257    pub fn as_option(self) -> Option<Value> {
258        match self {
259            MaybeReply::Reply(val) => Some(val),
260            MaybeReply::DontReply => None,
261        }
262    }
263}
264
265/// extract method name and parameters from call
266/// if call is a normal method call, call `handle` and return result
267/// if call is a notification, call `handle` and return None
268/// if call is invalid return a jsonrpc failure
269fn handle_call<S: ?Sized + Handler>(slef: &S, call: jsonrpc_core::Call) -> Option<Output> {
270    let (method, params, maybe_id, version): (
271        String,
272        jsonrpc_core::Params,
273        Option<Id>,
274        Option<Version>,
275    ) = match call {
276        jsonrpc_core::Call::Invalid { id } => {
277            return Some(Output::invalid_request(id, None));
278        }
279        jsonrpc_core::Call::MethodCall(MethodCall {
280            method,
281            params,
282            id,
283            jsonrpc,
284        }) => (method, params, Some(id), jsonrpc),
285        jsonrpc_core::Call::Notification(Notification {
286            method,
287            params,
288            jsonrpc,
289        }) => (method, params, None, jsonrpc),
290    };
291    let args = Params::from_rc_params(params);
292    let ret = slef.handle(&method, args);
293    let id = maybe_id?;
294    Some(match ret {
295        Ok(ok) => Output::Success(Success {
296            jsonrpc: version,
297            result: ok,
298            id,
299        }),
300        Err(err) => Output::Failure(Failure {
301            jsonrpc: version,
302            error: err,
303            id,
304        }),
305    })
306}
307
308// Handle a request after it has been successfuly deserialized, this function is private to avoid
309// exposing jsonrpc_core types to the user. Also, it's not needed externally.
310fn handle_parsed_request<S: ?Sized + Handler>(
311    slef: &S,
312    request: jsonrpc_core::Request,
313) -> Option<jsonrpc_core::Response> {
314    match request {
315        jsonrpc_core::Request::Single(call) => {
316            handle_call(slef, call).map(jsonrpc_core::Response::Single)
317        }
318        jsonrpc_core::Request::Batch(mut calls) => {
319            let outputs = calls
320                .drain(..)
321                .filter_map(|call| handle_call(slef, call))
322                .collect::<Vec<_>>();
323            if outputs.is_empty() {
324                None
325            } else {
326                Some(jsonrpc_core::Response::Batch(outputs))
327            }
328        }
329    }
330}
331
332#[doc(hidden)]
333#[derive(
334    Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, serde::Serialize, serde::Deserialize,
335)]
336pub enum InvalidArgs {
337    WrongNumberOfArgs { expected: usize, actual: usize },
338    ExtraNamedParameter { name: String },
339    MissingNamedParameter { name: &'static str },
340    InvalidArgStructure { name: &'static str, index: usize },
341}
342
343impl Into<Error> for InvalidArgs {
344    fn into(self) -> Error {
345        match self {
346            InvalidArgs::WrongNumberOfArgs { expected, actual } => Error::invalid_params(format!(
347                "WrongNumberOfArgs. Expected {}. Actual {}",
348                expected, actual
349            )),
350            InvalidArgs::ExtraNamedParameter { name } => {
351                Error::invalid_params(format!("ExtraNamedParameter {}", name))
352            }
353            InvalidArgs::MissingNamedParameter { name } => {
354                Error::invalid_params(format!("MissingNamedParameter {}", name))
355            }
356            InvalidArgs::InvalidArgStructure { name, index } => Error::invalid_params(format!(
357                "InvalidArgStructure {} at position {}.",
358                name, index
359            )),
360        }
361    }
362}
363
364/// Represetaion of jsonrpc arguments. Passing no arguments is assumed to be semantically equivalent
365/// to passing 0 positional args, or passing a map with zero entries.
366///
367/// Users of this library will rarely need to deal with this type.
368#[derive(Debug)]
369pub enum Params {
370    /// Arguments were either not present (expressed as a length 0 list), or arguments were provided as
371    /// a json list.
372    Positional(Vec<Value>),
373    /// Arguments were provided as a json dictionary.
374    Named(serde_json::Map<String, Value>),
375}
376
377impl Params {
378    fn from_rc_params(params: jsonrpc_core::Params) -> Self {
379        match params {
380            jsonrpc_core::Params::Array(arr) => Params::Positional(arr),
381            jsonrpc_core::Params::Map(map) => Params::Named(map),
382            jsonrpc_core::Params::None => Params::Positional(vec![]),
383        }
384    }
385
386    /// Verify and convert Params to an argument list. If arguments are provided as named
387    /// parameters, interpret them as positional arguments using the names argument as a key.
388    ///
389    /// Verifies:
390    ///    - Number of args in positional parameter list is correct
391    ///    - No missing args in named parameter object
392    ///    - No extra args in named parameter object
393    pub fn get_rpc_args(self, names: &[&'static str]) -> Result<Vec<Value>, InvalidArgs> {
394        debug_assert!(
395            {
396                fn contains_duplicates(list: &[&str]) -> bool {
397                    (1..list.len()).any(|i| list[i..].contains(&list[i - 1]))
398                }
399                !contains_duplicates(names)
400            },
401            "get_rpc_args recieved duplicate argument names"
402        );
403        let ar: Vec<Value> = match self {
404            Params::Positional(ar) => ar,
405            Params::Named(mut ma) => {
406                let mut ar: Vec<Value> = Vec::with_capacity(names.len());
407                for name in names.iter() {
408                    ar.push(
409                        ma.remove(*name)
410                            .ok_or(InvalidArgs::MissingNamedParameter { name })?,
411                    );
412                }
413                debug_assert_eq!(ar.len(), names.len());
414                match ma.keys().next() {
415                    Some(key) => {
416                        return Err(InvalidArgs::ExtraNamedParameter { name: key.clone() })
417                    }
418                    None => ar,
419                }
420            }
421        };
422        if ar.len() != names.len() {
423            Err(InvalidArgs::WrongNumberOfArgs {
424                expected: names.len(),
425                actual: ar.len(),
426            })
427        } else {
428            Ok(ar)
429        }
430    }
431}
432
433// Intentionally does not implement Serialize; we don't want users to accidentally send a call by
434// itself. Does not implement clone because Vec<Value> is potentially expensive to clone.
435/// Create a binding of arguments to a method name. Can be turned into either a jsonrpc call using
436/// [call](#method.call), or a jsonrpc notification using [notification](#method.notification).
437#[derive(Debug)]
438pub struct BoundMethod<'a, T>
439where
440    T: Deserialize<'static>,
441{
442    method: &'a str,
443    args: Vec<Value>,
444    _spook: PhantomData<*const T>,
445}
446
447impl<'a, T> BoundMethod<'a, T>
448where
449    T: Deserialize<'static>,
450{
451    /// Create a binding of arguments to a method name.
452    /// You probably don't want to use this method directly.
453    /// Try using the rpc macro instead.
454    pub fn new(method: &'a str, args: Vec<Value>) -> BoundMethod<T> {
455        BoundMethod {
456            method,
457            args,
458            _spook: PhantomData,
459        }
460    }
461
462    /// Create a jsonrpc method call with a random id and a tracker for retrieving the return value.
463    pub fn call(&'a self) -> (Call<'a>, Tracker<T>)
464    where
465        T: Deserialize<'static>,
466    {
467        let Self { method, args, .. } = self;
468        let id = rand::random::<u64>();
469        (
470            Call {
471                method,
472                args,
473                id: Some(id),
474            },
475            Tracker {
476                id,
477                _spook: PhantomData,
478            },
479        )
480    }
481
482    /// Create a jsonrpc method call with no id. Jsonrpc servers accept notifications silently.
483    /// That is to say, they handle the notification, but send to reasponse.
484    pub fn notification(&'a self) -> Call<'a> {
485        let Self { method, args, .. } = self;
486        Call {
487            method,
488            args,
489            id: None,
490        }
491    }
492}
493
494// Intentionally does not implement Serialize; we don't want users to accidentally send a call by
495// itself. Does not implement clone because Vec<Value> is potentially expensive to clone.
496/// A single rpc method call with arguments. May be sent to the server by itself using
497/// [as_request](#method.as_request), or as a batch, using
498/// [batch_request](#method.batch_request).
499pub struct Call<'a> {
500    method: &'a str,
501    args: &'a [Value],
502    id: Option<u64>,
503}
504
505impl<'a> Call<'a> {
506    /// Convert call to a json object which can be serialized and sent to a jsonrpc server.
507    pub fn as_request(&self) -> Value {
508        let Self { method, id, args } = self;
509        match id {
510            Some(id) => json!({
511                "jsonrpc": "2.0",
512                "method": method,
513                "params": args,
514                "id": id,
515            }),
516            None => json!({
517                "jsonrpc": "2.0",
518                "method": method,
519                "params": args,
520            }),
521        }
522    }
523
524    /// Convert list of calls to a json object which can be serialized and sent to a jsonrpc server.
525    pub fn batch_request(calls: &[Self]) -> Value {
526        debug_assert!({
527            fn contains_duplicates(list: &[u64]) -> bool {
528                (1..list.len()).any(|i| list[i..].contains(&list[i - 1]))
529            }
530            let ids = calls.iter().filter_map(|call| call.id).collect::<Vec<_>>();
531            !contains_duplicates(ids.as_slice())
532        });
533        Value::Array(calls.iter().map(Call::as_request).collect())
534    }
535}
536
537/// used from generated code
538#[doc(hidden)]
539pub fn try_serialize<T: Serialize>(t: &T) -> Result<Value, Error> {
540    // Serde serde_json::to_value does not perform io. It's still not safe to unwrap the result. For
541    // example, the implementation of Serialize for Mutex returns an error if the mutex is poisined.
542    // Another example, serialize(&std::Path) returns an error when it encounters invalid utf-8.
543    serde_json::to_value(t).map_err(|e| Error {
544        code: ErrorCode::ServerError(SERIALZATION_ERROR),
545        message: "Serialization error".to_owned(),
546        data: Some(Value::String(format!("{}", e))),
547    })
548}
549
550/// Error returned when a tracker fails to retrive its response.
551#[derive(Clone, PartialEq, Debug)]
552pub enum ResponseFail {
553    /// Server responded, but Server did not specify a result for the call in question.
554    ResultNotFound,
555    /// Server specified a result for the call in question, but it the result was malformed.
556    InvalidResponse,
557    /// Server specified a result for the call in question and the result was an rpc error.
558    RpcError(Error),
559}
560
561/// Thrown when arguments fail to be serialized. Possible causes include, but are not limited to:
562/// - A poisoned mutex
563/// - A cstring containing invalid utf-8
564#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
565pub struct ArgSerializeError;
566
567/// Returned by [from_json_response](struct.Response.html#method.from_json_response) on error.
568#[derive(Clone, PartialEq, Debug)]
569pub enum InvalidResponse {
570    /// Response is not a valid jsonrpc response.
571    DeserailizeFailure,
572    /// Response contains an id that is not number. The client helpers in easy_jsonrpc never send
573    /// non-number ids, so if the server responds with a non-number id, something is wrong.
574    ContainsNonNumericId,
575}
576
577/// Special purpose structure for holding a group of responses. Allows for response lookup by id.
578/// Does not support non-number ids.
579pub struct Response {
580    /// Mapping from id to output of rpc call.
581    pub outputs: BTreeMap<u64, Result<Value, Error>>,
582}
583
584impl Response {
585    /// Deserialize response from a jsonrpc server.
586    pub fn from_json_response(raw_jsonrpc_response: Value) -> Result<Self, InvalidResponse> {
587        let response: jsonrpc_core::Response = serde_json::from_value(raw_jsonrpc_response)
588            .map_err(|_| InvalidResponse::DeserailizeFailure)?;
589        let mut calls: Vec<Output> = match response {
590            jsonrpc_core::Response::Single(out) => vec![out],
591            jsonrpc_core::Response::Batch(outs) => outs,
592        };
593        debug_assert!({
594            fn contains_duplicates(list: &[u64]) -> bool {
595                (1..list.len()).any(|i| list[i..].contains(&list[i - 1]))
596            }
597            let ids = calls
598                .iter()
599                .filter_map(|out| match out {
600                    Output::Success(Success {
601                        id: Id::Num(id), ..
602                    })
603                    | Output::Failure(Failure {
604                        id: Id::Num(id), ..
605                    }) => Some(*id),
606                    _ => None,
607                })
608                .collect::<Vec<_>>();
609            !contains_duplicates(ids.as_slice())
610        });
611        let outputs = calls
612            .drain(..)
613            .map(
614                |out| -> Result<(u64, Result<Value, Error>), InvalidResponse> {
615                    match out {
616                        Output::Success(Success {
617                            result,
618                            id: Id::Num(id),
619                            ..
620                        }) => Ok((id, Ok(result))),
621                        Output::Failure(Failure {
622                            error,
623                            id: Id::Num(id),
624                            ..
625                        }) => Ok((id, Err(error))),
626                        _ => Err(InvalidResponse::ContainsNonNumericId),
627                    }
628                },
629            )
630            .collect::<Result<BTreeMap<u64, Result<Value, Error>>, InvalidResponse>>()?;
631        Ok(Self { outputs })
632    }
633
634    /// Retrieve the output with a matching id and return it, return None if no such output exists.
635    pub fn remove(&mut self, id: u64) -> Option<Result<Value, Error>> {
636        self.outputs.remove(&id)
637    }
638}
639
640/// Links a jsonrpc id to a return type.
641/// Trackers can be used to get a typed return value from a json response.
642pub struct Tracker<T>
643where
644    T: Deserialize<'static>,
645{
646    id: u64,
647    _spook: PhantomData<*const T>,
648}
649
650impl<T> Tracker<T>
651where
652    T: Deserialize<'static>,
653{
654    /// Get typed return value from server response.
655    /// If response contains the return value for this request, remove it from the
656    /// server response and attempt to interpret it as a value with type T.
657    pub fn get_return(&self, response: &mut Response) -> Result<T, ResponseFail> {
658        let result = response
659            .remove(self.id)
660            .ok_or(ResponseFail::ResultNotFound)?;
661        let raw_return = result.map_err(ResponseFail::RpcError)?;
662        <T>::deserialize(raw_return).map_err(|_| ResponseFail::InvalidResponse)
663    }
664}
665
666#[cfg(test)]
667mod test {
668    mod easy_jsonrpc {
669        pub use crate::*;
670    }
671    use super::{Handler, InvalidArgs, MaybeReply, Params};
672    use jsonrpc_core;
673    use serde_json::{json, Value};
674
675    #[easy_jsonrpc::rpc]
676    pub trait Adder {
677        fn checked_add(&self, a: isize, b: isize) -> Option<isize>;
678        fn wrapping_add(&self, a: isize, b: isize) -> isize;
679        fn greet(&self) -> String;
680        fn swallow(&self);
681        fn repeat_list(&self, lst: Vec<usize>) -> Vec<usize>;
682        fn fail(&self) -> Result<isize, String>;
683        fn succeed(&self) -> Result<isize, String>;
684        fn echo_ref(&self, a: &isize) -> isize;
685    }
686
687    struct AdderImpl;
688    impl Adder for AdderImpl {
689        fn checked_add(&self, a: isize, b: isize) -> Option<isize> {
690            a.checked_add(b)
691        }
692
693        fn wrapping_add(&self, a: isize, b: isize) -> isize {
694            a.wrapping_add(b)
695        }
696
697        fn greet(&self) -> String {
698            "hello".into()
699        }
700
701        fn swallow(&self) {}
702
703        fn repeat_list(&self, lst: Vec<usize>) -> Vec<usize> {
704            let mut ret = lst.clone();
705            ret.extend(lst);
706            ret
707        }
708
709        fn fail(&self) -> Result<isize, String> {
710            Err("tada!".into())
711        }
712
713        fn succeed(&self) -> Result<isize, String> {
714            Ok(1)
715        }
716
717        fn echo_ref(&self, a: &isize) -> isize {
718            *a
719        }
720    }
721
722    fn assert_adder_response(request: Value, response: Value) {
723        assert_eq!(
724            (&AdderImpl {} as &dyn Adder)
725                .handle_request(request)
726                .as_option()
727                .unwrap(),
728            response
729        );
730    }
731
732    fn error_code(request: Value) -> jsonrpc_core::ErrorCode {
733        let raw_response = (&AdderImpl {} as &dyn Adder)
734            .handle_request(request)
735            .as_option()
736            .unwrap();
737        let response: jsonrpc_core::Response = serde_json::from_value(raw_response).unwrap();
738        match response {
739            jsonrpc_core::Response::Single(jsonrpc_core::Output::Failure(
740                jsonrpc_core::Failure { error, .. },
741            )) => error.code,
742            _ => panic!(),
743        }
744    }
745
746    #[test]
747    fn batch() {
748        assert_adder_response(
749            json!([
750                {
751                    "jsonrpc": "2.0",
752                    "method": "wrapping_add",
753                    "params": [1, 1],
754                    "id": 1
755                },
756                {
757                    "jsonrpc": "2.0",
758                    "method": "wrapping_add",
759                    "params": [1, 2],
760                    "id": 2
761                },
762                {
763                    "jsonrpc": "2.0",
764                    "method": "wrapping_add",
765                    "params": [1, 3],
766                    "id": null
767                },
768                {
769                    "jsonrpc": "2.0",
770                    "method": "wrapping_add",
771                    "params": [1, 4],
772                },
773            ]),
774            json!([
775                {
776                    "jsonrpc": "2.0",
777                    "result": 2,
778                    "id": 1
779                },
780                {
781                    "jsonrpc": "2.0",
782                    "result": 3,
783                    "id": 2
784                },
785                {
786                    "jsonrpc": "2.0",
787                    "result": 4,
788                    "id": null
789                }
790            ]),
791        );
792    }
793
794    #[test]
795    fn positional_args() {
796        assert_adder_response(
797            json!({
798                "jsonrpc": "2.0",
799                "method": "wrapping_add",
800                "params": [1, 1],
801                "id": 1
802            }),
803            json!({
804                "jsonrpc": "2.0",
805                "result": 2,
806                "id": 1
807            }),
808        );
809    }
810
811    #[test]
812    fn string_id() {
813        assert_adder_response(
814            json!({
815                "jsonrpc": "2.0",
816                "method": "wrapping_add",
817                "params": [1, 1],
818                "id": "jfjfks sasdfk"
819            }),
820            json!({
821                "jsonrpc": "2.0",
822                "result": 2,
823                "id": "jfjfks sasdfk"
824            }),
825        );
826        assert_adder_response(
827            json!({
828                "jsonrpc": "2.0",
829                "method": "wrapping_add",
830                "params": [1, 1],
831                "id": ""
832            }),
833            json!({
834                "jsonrpc": "2.0",
835                "result": 2,
836                "id": ""
837            }),
838        );
839    }
840
841    #[test]
842    fn named_args() {
843        assert_adder_response(
844            json!({
845                "jsonrpc": "2.0",
846                "method": "wrapping_add",
847                "params": {
848                    "a": 1,
849                    "b": 1
850                },
851                "id": 1
852            }),
853            json!({
854                "jsonrpc": "2.0",
855                "result": 2,
856                "id": 1
857            }),
858        );
859    }
860
861    #[test]
862    fn null_args() {
863        let response = json!({
864            "jsonrpc": "2.0",
865            "result": "hello",
866            "id": 1
867        });
868        assert_adder_response(
869            json!({
870                "jsonrpc": "2.0",
871                "method": "greet",
872                "params": {},
873                "id": 1
874            }),
875            response.clone(),
876        );
877        assert_adder_response(
878            json!({
879                "jsonrpc": "2.0",
880                "method": "greet",
881                "params": [],
882                "id": 1
883            }),
884            response.clone(),
885        );
886        assert_adder_response(
887            json!({
888                "jsonrpc": "2.0",
889                "method": "greet",
890                "params": null,
891                "id": 1
892            }),
893            response.clone(),
894        );
895        assert_adder_response(
896            json!({
897                "jsonrpc": "2.0",
898                "method": "greet",
899                "id": 1
900            }),
901            response.clone(),
902        );
903    }
904
905    #[test]
906    fn null_return() {
907        assert_adder_response(
908            json!({
909                "jsonrpc": "2.0",
910                "method": "swallow",
911                "params": [],
912                "id": 1
913            }),
914            json!({
915                "jsonrpc": "2.0",
916                "result": null,
917                "id": 1
918            }),
919        );
920    }
921
922    #[test]
923    fn incorrect_method_name() {
924        assert_eq!(
925            error_code(json!({
926                "jsonrpc": "2.0",
927                "method": "nonexist",
928                "params": [],
929                "id": 1
930            })),
931            jsonrpc_core::ErrorCode::MethodNotFound,
932        );
933    }
934
935    #[test]
936    fn incorrect_args() {
937        assert_eq!(
938            error_code(json!({
939                "jsonrpc": "2.0",
940                "method": "wrapping_add",
941                "params": [],
942                "id": 1
943            })),
944            jsonrpc_core::ErrorCode::InvalidParams,
945        );
946        assert_eq!(
947            error_code(json!({
948                "jsonrpc": "2.0",
949                "method": "wrapping_add",
950                "params": {
951                    "notanarg": 1,
952                    "notarg": 1
953                },
954                "id": 1
955            })),
956            jsonrpc_core::ErrorCode::InvalidParams,
957        );
958        assert_eq!(
959            error_code(json!({
960                "jsonrpc": "2.0",
961                "method": "wrapping_add",
962                "params": [[], []],
963                "id": 1
964            })),
965            jsonrpc_core::ErrorCode::InvalidParams,
966        );
967    }
968
969    #[test]
970    fn complex_type() {
971        assert_adder_response(
972            json!({
973                "jsonrpc": "2.0",
974                "method": "repeat_list",
975                "params": [[1, 2, 3]],
976                "id": 1
977            }),
978            json!({
979                "jsonrpc": "2.0",
980                "result": [1, 2, 3, 1, 2, 3],
981                "id": 1
982            }),
983        );
984        assert_eq!(
985            error_code(json!({
986                "jsonrpc": "2.0",
987                "method": "repeat_list",
988                "params": [[1], [12]],
989                "id": 1
990            }),),
991            jsonrpc_core::ErrorCode::InvalidParams,
992        );
993        assert_adder_response(
994            json!({
995                "jsonrpc": "2.0",
996                "method": "fail",
997                "params": [],
998                "id": 1
999            }),
1000            json!({
1001                "jsonrpc": "2.0",
1002                "result": {
1003                    "Err": "tada!"
1004                },
1005                "id": 1
1006            }),
1007        );
1008        assert_adder_response(
1009            json!({
1010                "jsonrpc": "2.0",
1011                "method": "succeed",
1012                "params": [],
1013                "id": 1
1014            }),
1015            json!({
1016                "jsonrpc": "2.0",
1017                "result": {
1018                    "Ok": 1
1019                },
1020                "id": 1
1021            }),
1022        );
1023    }
1024
1025    #[test]
1026    fn notification() {
1027        let request = json!({
1028            "jsonrpc": "2.0",
1029            "method": "succeed",
1030            "params": []
1031        });
1032        assert_eq!(
1033            (&AdderImpl {} as &dyn Adder).handle_request(request),
1034            MaybeReply::DontReply
1035        );
1036    }
1037
1038    #[test]
1039    fn adder_client_non_macro() {
1040        #[easy_jsonrpc::rpc]
1041        trait Adder {
1042            fn checked_add(&self, a: usize, b: usize) -> Option<usize> {
1043                a.checked_add(b)
1044            }
1045        }
1046
1047        #[allow(non_camel_case_types)]
1048        pub enum adder_client {}
1049        impl adder_client {
1050            fn checked_add(
1051                arg0: usize,
1052                arg1: usize,
1053            ) -> Result<
1054                easy_jsonrpc::BoundMethod<'static, Option<usize>>,
1055                easy_jsonrpc::ArgSerializeError,
1056            > {
1057                Ok(easy_jsonrpc::BoundMethod::new(
1058                    "checked_add",
1059                    vec![
1060                        serde_json::to_value(arg0).map_err(|_| easy_jsonrpc::ArgSerializeError)?,
1061                        serde_json::to_value(arg1).map_err(|_| easy_jsonrpc::ArgSerializeError)?,
1062                    ],
1063                ))
1064            }
1065        }
1066
1067        impl Adder for () {}
1068        let handler = &() as &dyn Adder;
1069
1070        let bind = adder_client::checked_add(1, 2).unwrap();
1071        let (call, tracker) = bind.call();
1072        let raw_response = handler
1073            .handle_request(call.as_request())
1074            .as_option()
1075            .unwrap();
1076        let mut response = easy_jsonrpc::Response::from_json_response(raw_response).unwrap();
1077        let result: Option<usize> = tracker.get_return(&mut response).unwrap();
1078        assert_eq!(result, Some(3));
1079
1080        assert_eq!(
1081            handler.handle_request(
1082                adder_client::checked_add(1, 2)
1083                    .unwrap()
1084                    .notification()
1085                    .as_request()
1086            ),
1087            MaybeReply::DontReply
1088        );
1089    }
1090
1091    #[test]
1092    fn adder_client_with_macro() {
1093        #[easy_jsonrpc::rpc]
1094        trait Adder {
1095            fn checked_add(&self, a: usize, b: usize) -> Option<usize> {
1096                a.checked_add(b)
1097            }
1098        }
1099
1100        impl Adder for () {}
1101        let handler = &() as &dyn Adder;
1102
1103        let bind = adder::checked_add(1, 2).unwrap();
1104        let (call, tracker) = bind.call();
1105        let raw_response = handler
1106            .handle_request(call.as_request())
1107            .as_option()
1108            .unwrap();
1109        let mut response = easy_jsonrpc::Response::from_json_response(raw_response).unwrap();
1110        let result: Option<usize> = tracker.get_return(&mut response).unwrap();
1111        assert_eq!(result, Some(3));
1112
1113        let call = adder::checked_add(1, 2).unwrap();
1114        assert_eq!(
1115            handler.handle_request(call.notification().as_request()),
1116            MaybeReply::DontReply
1117        );
1118    }
1119
1120    #[test]
1121    fn client_with_reference_args() {
1122        let handler = &AdderImpl {} as &dyn Adder;
1123
1124        let bind = adder::echo_ref(&2).unwrap();
1125        let (call, tracker) = bind.call();
1126        let raw_response = handler
1127            .handle_request(call.as_request())
1128            .as_option()
1129            .unwrap();
1130        let mut response = easy_jsonrpc::Response::from_json_response(raw_response).unwrap();
1131        assert_eq!(tracker.get_return(&mut response).unwrap(), 2);
1132
1133        let call = adder::echo_ref(&2).unwrap();
1134        assert_eq!(
1135            handler.handle_request(call.notification().as_request()),
1136            MaybeReply::DontReply
1137        );
1138    }
1139
1140    #[test]
1141    fn response_double_get() {
1142        let handler = &AdderImpl as &dyn Adder;
1143        use easy_jsonrpc::Call;
1144        let bind0 = adder::checked_add(0, 0).unwrap();
1145        let (call0, tracker0) = bind0.call();
1146        let bind1 = adder::checked_add(1, 0).unwrap();
1147        let (call1, tracker1) = bind1.call();
1148        let bind2 = adder::wrapping_add(1, 1).unwrap();
1149        let (call2, tracker2) = bind2.call();
1150        let json_request = Call::batch_request(&[call0, call1, call2]);
1151        let json_response = handler.handle_request(json_request).as_option().unwrap();
1152        let mut response = easy_jsonrpc::Response::from_json_response(json_response).unwrap();
1153        assert_eq!(tracker0.get_return(&mut response).unwrap(), Some(0));
1154        assert_eq!(tracker2.get_return(&mut response).unwrap(), 2);
1155
1156        // get_return removes the returned return value
1157        assert_eq!(tracker1.get_return(&mut response), Ok(Some(1)));
1158        assert_eq!(
1159            tracker1.get_return(&mut response),
1160            Err(easy_jsonrpc::ResponseFail::ResultNotFound)
1161        );
1162    }
1163
1164    #[test]
1165    fn local_types() {
1166        #[derive(serde::Serialize, serde::Deserialize)]
1167        pub struct Foo;
1168
1169        #[easy_jsonrpc::rpc]
1170        trait Bar {
1171            fn frob(&self) -> Foo;
1172            fn borf(&self, foo: Foo);
1173        }
1174    }
1175
1176    // https://github.com/layer1capital/easy-jsonrpc/issues/8
1177    #[test]
1178    fn wrong_num_arg_err() {
1179        assert_adder_response(
1180            json!({
1181                "jsonrpc": "2.0",
1182                "method": "checked_add",
1183                "params": [1],
1184                "id": 1
1185            }),
1186            json!({
1187                "error": {
1188                    "code": -32602,
1189                    "message": "WrongNumberOfArgs. Expected 2. Actual 1"
1190                },
1191                "id": 1,
1192                "jsonrpc": "2.0"
1193            }),
1194        );
1195
1196        let res = Params::from_rc_params(jsonrpc_core::Params::Array(vec![
1197            json!(1),
1198            json!(2),
1199            json!(3),
1200        ]))
1201        .get_rpc_args(&["arg_one", "arg_two"]);
1202        assert_eq!(
1203            res,
1204            Err(InvalidArgs::WrongNumberOfArgs {
1205                expected: 2,
1206                actual: 3
1207            })
1208        );
1209    }
1210}