easy_jsonrpc_mwc/
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_mwc;
10
11#[easy_jsonrpc_mwc::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_mwc;
30# #[easy_jsonrpc_mwc::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_mwc::{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_mwc;
71# #[easy_jsonrpc_mwc::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_mwc::{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_mwc::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_mwc;
108# #[easy_jsonrpc_mwc::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_mwc::{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_mwc::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_mwc::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_mwc::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(ma.remove(*name).unwrap_or( Value::Null ));
409                }
410                debug_assert_eq!(ar.len(), names.len());
411                match ma.keys().next() {
412                    Some(key) => {
413                        return Err(InvalidArgs::ExtraNamedParameter { name: key.clone() })
414                    }
415                    None => ar,
416                }
417            }
418        };
419        if ar.len() != names.len() {
420            Err(InvalidArgs::WrongNumberOfArgs {
421                expected: names.len(),
422                actual: ar.len(),
423            })
424        } else {
425            Ok(ar)
426        }
427    }
428}
429
430// Intentionally does not implement Serialize; we don't want users to accidentally send a call by
431// itself. Does not implement clone because Vec<Value> is potentially expensive to clone.
432/// Create a binding of arguments to a method name. Can be turned into either a jsonrpc call using
433/// [call](#method.call), or a jsonrpc notification using [notification](#method.notification).
434#[derive(Debug)]
435pub struct BoundMethod<'a, T>
436where
437    T: Deserialize<'static>,
438{
439    method: &'a str,
440    args: Vec<Value>,
441    _spook: PhantomData<*const T>,
442}
443
444impl<'a, T> BoundMethod<'a, T>
445where
446    T: Deserialize<'static>,
447{
448    /// Create a binding of arguments to a method name.
449    /// You probably don't want to use this method directly.
450    /// Try using the rpc macro instead.
451    pub fn new(method: &'a str, args: Vec<Value>) -> BoundMethod<'a, T> {
452        BoundMethod {
453            method,
454            args,
455            _spook: PhantomData,
456        }
457    }
458
459    /// Create a jsonrpc method call with a random id and a tracker for retrieving the return value.
460    pub fn call(&'a self) -> (Call<'a>, Tracker<T>)
461    where
462        T: Deserialize<'static>,
463    {
464        let Self { method, args, .. } = self;
465        let id = rand::random::<u64>();
466        (
467            Call {
468                method,
469                args,
470                id: Some(id),
471            },
472            Tracker {
473                id,
474                _spook: PhantomData,
475            },
476        )
477    }
478
479    /// Create a jsonrpc method call with no id. Jsonrpc servers accept notifications silently.
480    /// That is to say, they handle the notification, but send to reasponse.
481    pub fn notification(&'a self) -> Call<'a> {
482        let Self { method, args, .. } = self;
483        Call {
484            method,
485            args,
486            id: None,
487        }
488    }
489}
490
491// Intentionally does not implement Serialize; we don't want users to accidentally send a call by
492// itself. Does not implement clone because Vec<Value> is potentially expensive to clone.
493/// A single rpc method call with arguments. May be sent to the server by itself using
494/// [as_request](#method.as_request), or as a batch, using
495/// [batch_request](#method.batch_request).
496pub struct Call<'a> {
497    method: &'a str,
498    args: &'a [Value],
499    id: Option<u64>,
500}
501
502impl<'a> Call<'a> {
503    /// Convert call to a json object which can be serialized and sent to a jsonrpc server.
504    pub fn as_request(&self) -> Value {
505        let Self { method, id, args } = self;
506        match id {
507            Some(id) => json!({
508                "jsonrpc": "2.0",
509                "method": method,
510                "params": args,
511                "id": id,
512            }),
513            None => json!({
514                "jsonrpc": "2.0",
515                "method": method,
516                "params": args,
517            }),
518        }
519    }
520
521    /// Convert list of calls to a json object which can be serialized and sent to a jsonrpc server.
522    pub fn batch_request(calls: &[Self]) -> Value {
523        debug_assert!({
524            fn contains_duplicates(list: &[u64]) -> bool {
525                (1..list.len()).any(|i| list[i..].contains(&list[i - 1]))
526            }
527            let ids = calls.iter().filter_map(|call| call.id).collect::<Vec<_>>();
528            !contains_duplicates(ids.as_slice())
529        });
530        Value::Array(calls.iter().map(Call::as_request).collect())
531    }
532}
533
534/// used from generated code
535#[doc(hidden)]
536pub fn try_serialize<T: Serialize>(t: &T) -> Result<Value, Error> {
537    // Serde serde_json::to_value does not perform io. It's still not safe to unwrap the result. For
538    // example, the implementation of Serialize for Mutex returns an error if the mutex is poisined.
539    // Another example, serialize(&std::Path) returns an error when it encounters invalid utf-8.
540    serde_json::to_value(t).map_err(|e| Error {
541        code: ErrorCode::ServerError(SERIALZATION_ERROR),
542        message: "Serialization error".to_owned(),
543        data: Some(Value::String(format!("{}", e))),
544    })
545}
546
547/// Error returned when a tracker fails to retrive its response.
548#[derive(Clone, PartialEq, Debug)]
549pub enum ResponseFail {
550    /// Server responded, but Server did not specify a result for the call in question.
551    ResultNotFound,
552    /// Server specified a result for the call in question, but it the result was malformed.
553    InvalidResponse,
554    /// Server specified a result for the call in question and the result was an rpc error.
555    RpcError(Error),
556}
557
558/// Thrown when arguments fail to be serialized. Possible causes include, but are not limited to:
559/// - A poisoned mutex
560/// - A cstring containing invalid utf-8
561#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
562pub struct ArgSerializeError;
563
564/// Returned by [from_json_response](struct.Response.html#method.from_json_response) on error.
565#[derive(Clone, PartialEq, Debug)]
566pub enum InvalidResponse {
567    /// Response is not a valid jsonrpc response.
568    DeserailizeFailure,
569    /// Response contains an id that is not number. The client helpers in easy_jsonrpc never send
570    /// non-number ids, so if the server responds with a non-number id, something is wrong.
571    ContainsNonNumericId,
572}
573
574/// Special purpose structure for holding a group of responses. Allows for response lookup by id.
575/// Does not support non-number ids.
576pub struct Response {
577    /// Mapping from id to output of rpc call.
578    pub outputs: BTreeMap<u64, Result<Value, Error>>,
579}
580
581impl Response {
582    /// Deserialize response from a jsonrpc server.
583    pub fn from_json_response(raw_jsonrpc_response: Value) -> Result<Self, InvalidResponse> {
584        let response: jsonrpc_core::Response = serde_json::from_value(raw_jsonrpc_response)
585            .map_err(|_| InvalidResponse::DeserailizeFailure)?;
586        let mut calls: Vec<Output> = match response {
587            jsonrpc_core::Response::Single(out) => vec![out],
588            jsonrpc_core::Response::Batch(outs) => outs,
589        };
590        debug_assert!({
591            fn contains_duplicates(list: &[u64]) -> bool {
592                (1..list.len()).any(|i| list[i..].contains(&list[i - 1]))
593            }
594            let ids = calls
595                .iter()
596                .filter_map(|out| match out {
597                    Output::Success(Success {
598                        id: Id::Num(id), ..
599                    })
600                    | Output::Failure(Failure {
601                        id: Id::Num(id), ..
602                    }) => Some(*id),
603                    _ => None,
604                })
605                .collect::<Vec<_>>();
606            !contains_duplicates(ids.as_slice())
607        });
608        let outputs = calls
609            .drain(..)
610            .map(
611                |out| -> Result<(u64, Result<Value, Error>), InvalidResponse> {
612                    match out {
613                        Output::Success(Success {
614                            result,
615                            id: Id::Num(id),
616                            ..
617                        }) => Ok((id, Ok(result))),
618                        Output::Failure(Failure {
619                            error,
620                            id: Id::Num(id),
621                            ..
622                        }) => Ok((id, Err(error))),
623                        _ => Err(InvalidResponse::ContainsNonNumericId),
624                    }
625                },
626            )
627            .collect::<Result<BTreeMap<u64, Result<Value, Error>>, InvalidResponse>>()?;
628        Ok(Self { outputs })
629    }
630
631    /// Retrieve the output with a matching id and return it, return None if no such output exists.
632    pub fn remove(&mut self, id: u64) -> Option<Result<Value, Error>> {
633        self.outputs.remove(&id)
634    }
635}
636
637/// Links a jsonrpc id to a return type.
638/// Trackers can be used to get a typed return value from a json response.
639pub struct Tracker<T>
640where
641    T: Deserialize<'static>,
642{
643    id: u64,
644    _spook: PhantomData<*const T>,
645}
646
647impl<T> Tracker<T>
648where
649    T: Deserialize<'static>,
650{
651    /// Get typed return value from server response.
652    /// If response contains the return value for this request, remove it from the
653    /// server response and attempt to interpret it as a value with type T.
654    pub fn get_return(&self, response: &mut Response) -> Result<T, ResponseFail> {
655        let result = response
656            .remove(self.id)
657            .ok_or(ResponseFail::ResultNotFound)?;
658        let raw_return = result.map_err(ResponseFail::RpcError)?;
659        <T>::deserialize(raw_return).map_err(|_| ResponseFail::InvalidResponse)
660    }
661}
662
663#[cfg(test)]
664mod test {
665    mod easy_jsonrpc_mwc {
666        pub use crate::*;
667    }
668    use super::{Handler, InvalidArgs, MaybeReply, Params};
669    use jsonrpc_core;
670    use serde_json::{json, Value};
671
672    #[easy_jsonrpc_mwc::rpc]
673    pub trait Adder {
674        fn checked_add(&self, a: isize, b: isize) -> Option<isize>;
675        fn wrapping_add(&self, a: isize, b: isize) -> isize;
676        fn greet(&self) -> String;
677        fn swallow(&self);
678        fn repeat_list(&self, lst: Vec<usize>) -> Vec<usize>;
679        fn fail(&self) -> Result<isize, String>;
680        fn succeed(&self) -> Result<isize, String>;
681        fn echo_ref(&self, a: &isize) -> isize;
682    }
683
684    struct AdderImpl;
685    impl Adder for AdderImpl {
686        fn checked_add(&self, a: isize, b: isize) -> Option<isize> {
687            a.checked_add(b)
688        }
689
690        fn wrapping_add(&self, a: isize, b: isize) -> isize {
691            a.wrapping_add(b)
692        }
693
694        fn greet(&self) -> String {
695            "hello".into()
696        }
697
698        fn swallow(&self) {}
699
700        fn repeat_list(&self, lst: Vec<usize>) -> Vec<usize> {
701            let mut ret = lst.clone();
702            ret.extend(lst);
703            ret
704        }
705
706        fn fail(&self) -> Result<isize, String> {
707            Err("tada!".into())
708        }
709
710        fn succeed(&self) -> Result<isize, String> {
711            Ok(1)
712        }
713
714        fn echo_ref(&self, a: &isize) -> isize {
715            *a
716        }
717    }
718
719    fn assert_adder_response(request: Value, response: Value) {
720        assert_eq!(
721            (&AdderImpl {} as &dyn Adder)
722                .handle_request(request)
723                .as_option()
724                .unwrap(),
725            response
726        );
727    }
728
729    fn error_code(request: Value) -> jsonrpc_core::ErrorCode {
730        let raw_response = (&AdderImpl {} as &dyn Adder)
731            .handle_request(request)
732            .as_option()
733            .unwrap();
734        let response: jsonrpc_core::Response = serde_json::from_value(raw_response).unwrap();
735        match response {
736            jsonrpc_core::Response::Single(jsonrpc_core::Output::Failure(
737                jsonrpc_core::Failure { error, .. },
738            )) => error.code,
739            _ => panic!(),
740        }
741    }
742
743    #[test]
744    fn batch() {
745        assert_adder_response(
746            json!([
747                {
748                    "jsonrpc": "2.0",
749                    "method": "wrapping_add",
750                    "params": [1, 1],
751                    "id": 1
752                },
753                {
754                    "jsonrpc": "2.0",
755                    "method": "wrapping_add",
756                    "params": [1, 2],
757                    "id": 2
758                },
759                {
760                    "jsonrpc": "2.0",
761                    "method": "wrapping_add",
762                    "params": [1, 3],
763                    "id": null
764                },
765                {
766                    "jsonrpc": "2.0",
767                    "method": "wrapping_add",
768                    "params": [1, 4],
769                },
770            ]),
771            json!([
772                {
773                    "jsonrpc": "2.0",
774                    "result": 2,
775                    "id": 1
776                },
777                {
778                    "jsonrpc": "2.0",
779                    "result": 3,
780                    "id": 2
781                },
782                {
783                    "jsonrpc": "2.0",
784                    "result": 4,
785                    "id": null
786                }
787            ]),
788        );
789    }
790
791    #[test]
792    fn positional_args() {
793        assert_adder_response(
794            json!({
795                "jsonrpc": "2.0",
796                "method": "wrapping_add",
797                "params": [1, 1],
798                "id": 1
799            }),
800            json!({
801                "jsonrpc": "2.0",
802                "result": 2,
803                "id": 1
804            }),
805        );
806    }
807
808    #[test]
809    fn string_id() {
810        assert_adder_response(
811            json!({
812                "jsonrpc": "2.0",
813                "method": "wrapping_add",
814                "params": [1, 1],
815                "id": "jfjfks sasdfk"
816            }),
817            json!({
818                "jsonrpc": "2.0",
819                "result": 2,
820                "id": "jfjfks sasdfk"
821            }),
822        );
823        assert_adder_response(
824            json!({
825                "jsonrpc": "2.0",
826                "method": "wrapping_add",
827                "params": [1, 1],
828                "id": ""
829            }),
830            json!({
831                "jsonrpc": "2.0",
832                "result": 2,
833                "id": ""
834            }),
835        );
836    }
837
838    #[test]
839    fn named_args() {
840        assert_adder_response(
841            json!({
842                "jsonrpc": "2.0",
843                "method": "wrapping_add",
844                "params": {
845                    "a": 1,
846                    "b": 1
847                },
848                "id": 1
849            }),
850            json!({
851                "jsonrpc": "2.0",
852                "result": 2,
853                "id": 1
854            }),
855        );
856    }
857
858    #[test]
859    fn null_args() {
860        let response = json!({
861            "jsonrpc": "2.0",
862            "result": "hello",
863            "id": 1
864        });
865        assert_adder_response(
866            json!({
867                "jsonrpc": "2.0",
868                "method": "greet",
869                "params": {},
870                "id": 1
871            }),
872            response.clone(),
873        );
874        assert_adder_response(
875            json!({
876                "jsonrpc": "2.0",
877                "method": "greet",
878                "params": [],
879                "id": 1
880            }),
881            response.clone(),
882        );
883        assert_adder_response(
884            json!({
885                "jsonrpc": "2.0",
886                "method": "greet",
887                "params": null,
888                "id": 1
889            }),
890            response.clone(),
891        );
892        assert_adder_response(
893            json!({
894                "jsonrpc": "2.0",
895                "method": "greet",
896                "id": 1
897            }),
898            response.clone(),
899        );
900    }
901
902    #[test]
903    fn null_return() {
904        assert_adder_response(
905            json!({
906                "jsonrpc": "2.0",
907                "method": "swallow",
908                "params": [],
909                "id": 1
910            }),
911            json!({
912                "jsonrpc": "2.0",
913                "result": null,
914                "id": 1
915            }),
916        );
917    }
918
919    #[test]
920    fn incorrect_method_name() {
921        assert_eq!(
922            error_code(json!({
923                "jsonrpc": "2.0",
924                "method": "nonexist",
925                "params": [],
926                "id": 1
927            })),
928            jsonrpc_core::ErrorCode::MethodNotFound,
929        );
930    }
931
932    #[test]
933    fn incorrect_args() {
934        assert_eq!(
935            error_code(json!({
936                "jsonrpc": "2.0",
937                "method": "wrapping_add",
938                "params": [],
939                "id": 1
940            })),
941            jsonrpc_core::ErrorCode::InvalidParams,
942        );
943        assert_eq!(
944            error_code(json!({
945                "jsonrpc": "2.0",
946                "method": "wrapping_add",
947                "params": {
948                    "notanarg": 1,
949                    "notarg": 1
950                },
951                "id": 1
952            })),
953            jsonrpc_core::ErrorCode::InvalidParams,
954        );
955        assert_eq!(
956            error_code(json!({
957                "jsonrpc": "2.0",
958                "method": "wrapping_add",
959                "params": [[], []],
960                "id": 1
961            })),
962            jsonrpc_core::ErrorCode::InvalidParams,
963        );
964    }
965
966    #[test]
967    fn complex_type() {
968        assert_adder_response(
969            json!({
970                "jsonrpc": "2.0",
971                "method": "repeat_list",
972                "params": [[1, 2, 3]],
973                "id": 1
974            }),
975            json!({
976                "jsonrpc": "2.0",
977                "result": [1, 2, 3, 1, 2, 3],
978                "id": 1
979            }),
980        );
981        assert_eq!(
982            error_code(json!({
983                "jsonrpc": "2.0",
984                "method": "repeat_list",
985                "params": [[1], [12]],
986                "id": 1
987            }),),
988            jsonrpc_core::ErrorCode::InvalidParams,
989        );
990        assert_adder_response(
991            json!({
992                "jsonrpc": "2.0",
993                "method": "fail",
994                "params": [],
995                "id": 1
996            }),
997            json!({
998                "jsonrpc": "2.0",
999                "result": {
1000                    "Err": "tada!"
1001                },
1002                "id": 1
1003            }),
1004        );
1005        assert_adder_response(
1006            json!({
1007                "jsonrpc": "2.0",
1008                "method": "succeed",
1009                "params": [],
1010                "id": 1
1011            }),
1012            json!({
1013                "jsonrpc": "2.0",
1014                "result": {
1015                    "Ok": 1
1016                },
1017                "id": 1
1018            }),
1019        );
1020    }
1021
1022    #[test]
1023    fn notification() {
1024        let request = json!({
1025            "jsonrpc": "2.0",
1026            "method": "succeed",
1027            "params": []
1028        });
1029        assert_eq!(
1030            (&AdderImpl {} as &dyn Adder).handle_request(request),
1031            MaybeReply::DontReply
1032        );
1033    }
1034
1035    #[test]
1036    fn adder_client_non_macro() {
1037        #[easy_jsonrpc_mwc::rpc]
1038        trait Adder {
1039            fn checked_add(&self, a: usize, b: usize) -> Option<usize> {
1040                a.checked_add(b)
1041            }
1042        }
1043
1044        #[allow(non_camel_case_types)]
1045        pub enum adder_client {}
1046        impl adder_client {
1047            fn checked_add(
1048                arg0: usize,
1049                arg1: usize,
1050            ) -> Result<
1051                easy_jsonrpc_mwc::BoundMethod<'static, Option<usize>>,
1052                easy_jsonrpc_mwc::ArgSerializeError,
1053            > {
1054                Ok(easy_jsonrpc_mwc::BoundMethod::new(
1055                    "checked_add",
1056                    vec![
1057                        serde_json::to_value(arg0).map_err(|_| easy_jsonrpc_mwc::ArgSerializeError)?,
1058                        serde_json::to_value(arg1).map_err(|_| easy_jsonrpc_mwc::ArgSerializeError)?,
1059                    ],
1060                ))
1061            }
1062        }
1063
1064        impl Adder for () {}
1065        let handler = &() as &dyn Adder;
1066
1067        let bind = adder_client::checked_add(1, 2).unwrap();
1068        let (call, tracker) = bind.call();
1069        let raw_response = handler
1070            .handle_request(call.as_request())
1071            .as_option()
1072            .unwrap();
1073        let mut response = easy_jsonrpc_mwc::Response::from_json_response(raw_response).unwrap();
1074        let result: Option<usize> = tracker.get_return(&mut response).unwrap();
1075        assert_eq!(result, Some(3));
1076
1077        assert_eq!(
1078            handler.handle_request(
1079                adder_client::checked_add(1, 2)
1080                    .unwrap()
1081                    .notification()
1082                    .as_request()
1083            ),
1084            MaybeReply::DontReply
1085        );
1086    }
1087
1088    #[test]
1089    fn adder_client_with_macro() {
1090        #[easy_jsonrpc_mwc::rpc]
1091        trait Adder {
1092            fn checked_add(&self, a: usize, b: usize) -> Option<usize> {
1093                a.checked_add(b)
1094            }
1095        }
1096
1097        impl Adder for () {}
1098        let handler = &() as &dyn Adder;
1099
1100        let bind = adder::checked_add(1, 2).unwrap();
1101        let (call, tracker) = bind.call();
1102        let raw_response = handler
1103            .handle_request(call.as_request())
1104            .as_option()
1105            .unwrap();
1106        let mut response = easy_jsonrpc_mwc::Response::from_json_response(raw_response).unwrap();
1107        let result: Option<usize> = tracker.get_return(&mut response).unwrap();
1108        assert_eq!(result, Some(3));
1109
1110        let call = adder::checked_add(1, 2).unwrap();
1111        assert_eq!(
1112            handler.handle_request(call.notification().as_request()),
1113            MaybeReply::DontReply
1114        );
1115    }
1116
1117    #[test]
1118    fn client_with_reference_args() {
1119        let handler = &AdderImpl {} as &dyn Adder;
1120
1121        let bind = adder::echo_ref(&2).unwrap();
1122        let (call, tracker) = bind.call();
1123        let raw_response = handler
1124            .handle_request(call.as_request())
1125            .as_option()
1126            .unwrap();
1127        let mut response = easy_jsonrpc_mwc::Response::from_json_response(raw_response).unwrap();
1128        assert_eq!(tracker.get_return(&mut response).unwrap(), 2);
1129
1130        let call = adder::echo_ref(&2).unwrap();
1131        assert_eq!(
1132            handler.handle_request(call.notification().as_request()),
1133            MaybeReply::DontReply
1134        );
1135    }
1136
1137    #[test]
1138    fn response_double_get() {
1139        let handler = &AdderImpl as &dyn Adder;
1140        use easy_jsonrpc_mwc::Call;
1141        let bind0 = adder::checked_add(0, 0).unwrap();
1142        let (call0, tracker0) = bind0.call();
1143        let bind1 = adder::checked_add(1, 0).unwrap();
1144        let (call1, tracker1) = bind1.call();
1145        let bind2 = adder::wrapping_add(1, 1).unwrap();
1146        let (call2, tracker2) = bind2.call();
1147        let json_request = Call::batch_request(&[call0, call1, call2]);
1148        let json_response = handler.handle_request(json_request).as_option().unwrap();
1149        let mut response = easy_jsonrpc_mwc::Response::from_json_response(json_response).unwrap();
1150        assert_eq!(tracker0.get_return(&mut response).unwrap(), Some(0));
1151        assert_eq!(tracker2.get_return(&mut response).unwrap(), 2);
1152
1153        // get_return removes the returned return value
1154        assert_eq!(tracker1.get_return(&mut response), Ok(Some(1)));
1155        assert_eq!(
1156            tracker1.get_return(&mut response),
1157            Err(easy_jsonrpc_mwc::ResponseFail::ResultNotFound)
1158        );
1159    }
1160
1161    #[test]
1162    fn local_types() {
1163        #[derive(serde::Serialize, serde::Deserialize)]
1164        pub struct Foo;
1165
1166        #[easy_jsonrpc_mwc::rpc]
1167        trait Bar {
1168            fn frob(&self) -> Foo;
1169            fn borf(&self, foo: Foo);
1170        }
1171    }
1172
1173    // https://github.com/layer1capital/easy-jsonrpc/issues/8
1174    #[test]
1175    fn wrong_num_arg_err() {
1176        assert_adder_response(
1177            json!({
1178                "jsonrpc": "2.0",
1179                "method": "checked_add",
1180                "params": [1],
1181                "id": 1
1182            }),
1183            json!({
1184                "error": {
1185                    "code": -32602,
1186                    "message": "WrongNumberOfArgs. Expected 2. Actual 1"
1187                },
1188                "id": 1,
1189                "jsonrpc": "2.0"
1190            }),
1191        );
1192
1193        let res = Params::from_rc_params(jsonrpc_core::Params::Array(vec![
1194            json!(1),
1195            json!(2),
1196            json!(3),
1197        ]))
1198        .get_rpc_args(&["arg_one", "arg_two"]);
1199        assert_eq!(
1200            res,
1201            Err(InvalidArgs::WrongNumberOfArgs {
1202                expected: 2,
1203                actual: 3
1204            })
1205        );
1206    }
1207}