jsonrpc_async/
lib.rs

1// Rust JSON-RPC Library
2// Written in 2015 by
3//   Andrew Poelstra <apoelstra@wpsoftware.net>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! # Rust JSON-RPC Library
16//!
17//! Rust support for the JSON-RPC 2.0 protocol.
18//!
19
20// Coding conventions
21#![deny(non_upper_case_globals)]
22#![deny(non_camel_case_types)]
23#![deny(non_snake_case)]
24#![deny(unused_mut)]
25#![warn(missing_docs)]
26
27pub mod client;
28pub mod error;
29mod util;
30use serde::{Deserialize, Serialize};
31use serde_derive::*;
32
33#[cfg(feature = "simple_http")]
34pub mod simple_http;
35
36// Re-export error type
37pub use client::{Client, Transport};
38pub use error::Error;
39
40use serde_json::value::RawValue;
41
42/// Shorthand method to convert an argument into a [Box<serde_json::value::RawValue>].
43/// Since serializers rarely fail, it's probably easier to use [arg] instead.
44pub fn try_arg<T: serde::Serialize>(arg: T) -> Result<Box<RawValue>, serde_json::Error> {
45    RawValue::from_string(serde_json::to_string(&arg)?)
46}
47
48/// Shorthand method to convert an argument into a [Box<serde_json::value::RawValue>].
49///
50/// This conversion should not fail, so to avoid returning a [Result],
51/// in case of an error, the error is serialized as the return value.
52pub fn arg<T: serde::Serialize>(arg: T) -> Box<RawValue> {
53    match try_arg(arg) {
54        Ok(v) => v,
55        Err(e) => RawValue::from_string(format!("<<ERROR SERIALIZING ARGUMENT: {}>>", e))
56            .unwrap_or(RawValue::from_string("<<ERROR SERIALIZING ARGUMENT>>".to_owned()).unwrap()),
57    }
58}
59
60#[derive(Debug, Clone, Serialize)]
61/// A JSONRPC request object
62pub struct Request<'a> {
63    /// The name of the RPC call
64    pub method: &'a str,
65    /// Parameters to the RPC call
66    pub params: &'a [Box<RawValue>],
67    /// Identifier for this Request, which should appear in the response
68    pub id: serde_json::Value,
69    /// jsonrpc field, MUST be "2.0"
70    pub jsonrpc: Option<&'a str>,
71}
72
73#[derive(Debug, Clone, Deserialize, Serialize)]
74/// A JSONRPC response object
75pub struct Response {
76    /// A result if there is one, or null
77    pub result: Option<Box<RawValue>>,
78    /// An error if there is one, or null
79    pub error: Option<error::RpcError>,
80    /// Identifier for this Request, which should match that of the request
81    pub id: serde_json::Value,
82    /// jsonrpc field, MUST be "2.0"
83    pub jsonrpc: Option<String>,
84}
85
86impl Response {
87    /// Extract the result from a response
88    pub fn result<T: for<'a> serde::de::Deserialize<'a>>(&self) -> Result<T, Error> {
89        if let Some(ref e) = self.error {
90            return Err(Error::Rpc(e.clone()));
91        }
92
93        if let Some(ref res) = self.result {
94            serde_json::from_str(res.get()).map_err(Error::Json)
95        } else {
96            serde_json::from_value(serde_json::Value::Null).map_err(Error::Json)
97        }
98    }
99
100    /// Return the RPC error, if there was one, but do not check the result
101    pub fn check_error(self) -> Result<(), Error> {
102        if let Some(e) = self.error {
103            Err(Error::Rpc(e))
104        } else {
105            Ok(())
106        }
107    }
108
109    /// Returns whether or not the `result` field is empty
110    pub fn is_none(&self) -> bool {
111        self.result.is_none()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117
118    use super::Response;
119    use serde_derive::*;
120    use serde_json;
121    use serde_json::value::RawValue;
122
123    #[test]
124    fn response_is_none() {
125        let joanna = Response {
126            result: Some(RawValue::from_string(serde_json::to_string(&true).unwrap()).unwrap()),
127            error: None,
128            id: From::from(81),
129            jsonrpc: Some(String::from("2.0")),
130        };
131
132        let bill = Response {
133            result: None,
134            error: None,
135            id: From::from(66),
136            jsonrpc: Some(String::from("2.0")),
137        };
138
139        assert!(!joanna.is_none());
140        assert!(bill.is_none());
141    }
142
143    #[test]
144    fn response_extract() {
145        let obj = vec!["Mary", "had", "a", "little", "lamb"];
146        let response = Response {
147            result: Some(RawValue::from_string(serde_json::to_string(&obj).unwrap()).unwrap()),
148            error: None,
149            id: serde_json::Value::Null,
150            jsonrpc: Some(String::from("2.0")),
151        };
152        let recovered1: Vec<String> = response.result().unwrap();
153        assert!(response.clone().check_error().is_ok());
154        let recovered2: Vec<String> = response.result().unwrap();
155        assert_eq!(obj, recovered1);
156        assert_eq!(obj, recovered2);
157    }
158
159    #[test]
160    fn null_result() {
161        let s = r#"{"result":null,"error":null,"id":"test"}"#;
162        let response: Response = serde_json::from_str(&s).unwrap();
163        let recovered1: Result<(), _> = response.result();
164        let recovered2: Result<(), _> = response.clone().result();
165        assert!(recovered1.is_ok());
166        assert!(recovered2.is_ok());
167
168        let recovered1: Result<String, _> = response.result();
169        let recovered2: Result<String, _> = response.clone().result();
170        assert!(recovered1.is_err());
171        assert!(recovered2.is_err());
172    }
173
174    #[test]
175    fn batch_response() {
176        // from the jsonrpc.org spec example
177        let s = r#"[
178            {"jsonrpc": "2.0", "result": 7, "id": "1"},
179            {"jsonrpc": "2.0", "result": 19, "id": "2"},
180            {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
181            {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
182            {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
183        ]"#;
184        let batch_response: Vec<Response> = serde_json::from_str(&s).unwrap();
185        assert_eq!(batch_response.len(), 5);
186    }
187
188    #[test]
189    fn test_arg() {
190        macro_rules! test_arg {
191            ($val:expr, $t:ty) => {{
192                let val1: $t = $val;
193                let arg = super::arg(val1.clone());
194                let val2: $t = serde_json::from_str(arg.get()).expect(stringify!($val));
195                assert_eq!(val1, val2, "failed test for {}", stringify!($val));
196            }};
197        }
198
199        test_arg!(true, bool);
200        test_arg!(42, u8);
201        test_arg!(42, usize);
202        test_arg!(42, isize);
203        test_arg!(vec![42, 35], Vec<u8>);
204        test_arg!(String::from("test"), String);
205
206        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
207        struct Test {
208            v: String,
209        }
210        test_arg!(
211            Test {
212                v: String::from("test"),
213            },
214            Test
215        );
216    }
217}