jsonrpc/
lib.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! # Rust JSON-RPC Library
4//!
5//! Rust support for the JSON-RPC 2.0 protocol.
6
7#![cfg_attr(docsrs, feature(doc_auto_cfg))]
8// Coding conventions
9#![warn(missing_docs)]
10
11/// Re-export `serde` crate.
12pub extern crate serde;
13/// Re-export `serde_json` crate.
14pub extern crate serde_json;
15
16/// Re-export `base64` crate.
17#[cfg(feature = "base64")]
18pub extern crate base64;
19
20/// Re-export `minreq` crate if the feature is set.
21#[cfg(feature = "minreq")]
22pub extern crate minreq;
23
24pub mod client;
25pub mod error;
26pub mod http;
27
28#[cfg(feature = "simple_http")]
29pub use http::simple_http;
30
31#[cfg(feature = "minreq_http")]
32pub use http::minreq_http;
33
34#[cfg(feature = "simple_tcp")]
35pub mod simple_tcp;
36
37#[cfg(all(feature = "simple_uds", not(windows)))]
38pub mod simple_uds;
39
40use serde::{Deserialize, Serialize};
41use serde_json::value::RawValue;
42
43pub use crate::client::{Client, Transport};
44pub use crate::error::Error;
45
46/// Shorthand method to convert an argument into a boxed [`serde_json::value::RawValue`].
47///
48/// Since serializers rarely fail, it's probably easier to use [`arg`] instead.
49pub fn try_arg<T: serde::Serialize>(arg: T) -> Result<Box<RawValue>, serde_json::Error> {
50    RawValue::from_string(serde_json::to_string(&arg)?)
51}
52
53/// Shorthand method to convert an argument into a boxed [`serde_json::value::RawValue`].
54///
55/// This conversion should not fail, so to avoid returning a [`Result`],
56/// in case of an error, the error is serialized as the return value.
57pub fn arg<T: serde::Serialize>(arg: T) -> Box<RawValue> {
58    match try_arg(arg) {
59        Ok(v) => v,
60        Err(e) => RawValue::from_string(format!("<<ERROR SERIALIZING ARGUMENT: {}>>", e))
61            .unwrap_or_else(|_| {
62                RawValue::from_string("<<ERROR SERIALIZING ARGUMENT>>".to_owned()).unwrap()
63            }),
64    }
65}
66
67/// A JSONRPC request object.
68#[derive(Debug, Clone, Serialize)]
69pub struct Request<'a> {
70    /// The name of the RPC call.
71    pub method: &'a str,
72    /// Parameters to the RPC call.
73    pub params: Option<&'a RawValue>,
74    /// Identifier for this request, which should appear in the response.
75    pub id: serde_json::Value,
76    /// jsonrpc field, MUST be "2.0".
77    pub jsonrpc: Option<&'a str>,
78}
79
80/// A JSONRPC response object.
81#[derive(Debug, Clone, Deserialize, Serialize)]
82pub struct Response {
83    /// A result if there is one, or [`None`].
84    pub result: Option<Box<RawValue>>,
85    /// An error if there is one, or [`None`].
86    pub error: Option<error::RpcError>,
87    /// Identifier for this response, which should match that of the request.
88    pub id: serde_json::Value,
89    /// jsonrpc field, MUST be "2.0".
90    pub jsonrpc: Option<String>,
91}
92
93impl Response {
94    /// Extracts the result from a response.
95    pub fn result<T: for<'a> serde::de::Deserialize<'a>>(&self) -> Result<T, Error> {
96        if let Some(ref e) = self.error {
97            return Err(Error::Rpc(e.clone()));
98        }
99
100        if let Some(ref res) = self.result {
101            serde_json::from_str(res.get()).map_err(Error::Json)
102        } else {
103            serde_json::from_value(serde_json::Value::Null).map_err(Error::Json)
104        }
105    }
106
107    /// Returns the RPC error, if there was one, but does not check the result.
108    pub fn check_error(self) -> Result<(), Error> {
109        if let Some(e) = self.error {
110            Err(Error::Rpc(e))
111        } else {
112            Ok(())
113        }
114    }
115
116    /// Returns whether or not the `result` field is empty.
117    pub fn is_none(&self) -> bool {
118        self.result.is_none()
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    use serde_json::{
127        json,
128        value::{to_raw_value, RawValue},
129    };
130
131    #[test]
132    fn response_is_none() {
133        let joanna = Response {
134            result: Some(RawValue::from_string(serde_json::to_string(&true).unwrap()).unwrap()),
135            error: None,
136            id: From::from(81),
137            jsonrpc: Some(String::from("2.0")),
138        };
139
140        let bill = Response {
141            result: None,
142            error: None,
143            id: From::from(66),
144            jsonrpc: Some(String::from("2.0")),
145        };
146
147        assert!(!joanna.is_none());
148        assert!(bill.is_none());
149    }
150
151    #[test]
152    fn response_extract() {
153        let obj = vec!["Mary", "had", "a", "little", "lamb"];
154        let response = Response {
155            result: Some(RawValue::from_string(serde_json::to_string(&obj).unwrap()).unwrap()),
156            error: None,
157            id: serde_json::Value::Null,
158            jsonrpc: Some(String::from("2.0")),
159        };
160        let recovered1: Vec<String> = response.result().unwrap();
161        assert!(response.clone().check_error().is_ok());
162        let recovered2: Vec<String> = response.result().unwrap();
163        assert_eq!(obj, recovered1);
164        assert_eq!(obj, recovered2);
165    }
166
167    #[test]
168    fn null_result() {
169        let s = r#"{"result":null,"error":null,"id":"test"}"#;
170        let response: Response = serde_json::from_str(s).unwrap();
171        let recovered1: Result<(), _> = response.result();
172        let recovered2: Result<(), _> = response.result();
173        assert!(recovered1.is_ok());
174        assert!(recovered2.is_ok());
175
176        let recovered1: Result<String, _> = response.result();
177        let recovered2: Result<String, _> = response.result();
178        assert!(recovered1.is_err());
179        assert!(recovered2.is_err());
180    }
181
182    #[test]
183    fn batch_response() {
184        // from the jsonrpc.org spec example
185        let s = r#"[
186            {"jsonrpc": "2.0", "result": 7, "id": "1"},
187            {"jsonrpc": "2.0", "result": 19, "id": "2"},
188            {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
189            {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
190            {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
191        ]"#;
192        let batch_response: Vec<Response> = serde_json::from_str(s).unwrap();
193        assert_eq!(batch_response.len(), 5);
194    }
195
196    #[test]
197    fn test_arg() {
198        macro_rules! test_arg {
199            ($val:expr, $t:ty) => {{
200                let val1: $t = $val;
201                let arg = super::arg(val1.clone());
202                let val2: $t = serde_json::from_str(arg.get()).expect(stringify!($val));
203                assert_eq!(val1, val2, "failed test for {}", stringify!($val));
204            }};
205        }
206
207        test_arg!(true, bool);
208        test_arg!(42, u8);
209        test_arg!(42, usize);
210        test_arg!(42, isize);
211        test_arg!(vec![42, 35], Vec<u8>);
212        test_arg!(String::from("test"), String);
213
214        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
215        struct Test {
216            v: String,
217        }
218        test_arg!(
219            Test {
220                v: String::from("test"),
221            },
222            Test
223        );
224    }
225
226    #[test]
227    fn test_request_list() {
228        let list = json!([0]);
229        let raw_value = Some(to_raw_value(&list).unwrap());
230
231        let request = Request {
232            method: "list",
233            params: raw_value.as_deref(),
234            id: serde_json::json!(2),
235            jsonrpc: Some("2.0"),
236        };
237        assert_eq!(
238            serde_json::to_string(&request).unwrap(),
239            r#"{"method":"list","params":[0],"id":2,"jsonrpc":"2.0"}"#
240        );
241    }
242
243    #[test]
244    fn test_request_object() {
245        let object = json!({ "height": 0 });
246        let raw_value = Some(to_raw_value(&object).unwrap());
247
248        let request = Request {
249            method: "object",
250            params: raw_value.as_deref(),
251            id: serde_json::json!(2),
252            jsonrpc: Some("2.0"),
253        };
254        assert_eq!(
255            serde_json::to_string(&request).unwrap(),
256            r#"{"method":"object","params":{"height":0},"id":2,"jsonrpc":"2.0"}"#
257        );
258    }
259}