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: &'a [Box<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::value::RawValue;
127
128    #[test]
129    fn response_is_none() {
130        let joanna = Response {
131            result: Some(RawValue::from_string(serde_json::to_string(&true).unwrap()).unwrap()),
132            error: None,
133            id: From::from(81),
134            jsonrpc: Some(String::from("2.0")),
135        };
136
137        let bill = Response {
138            result: None,
139            error: None,
140            id: From::from(66),
141            jsonrpc: Some(String::from("2.0")),
142        };
143
144        assert!(!joanna.is_none());
145        assert!(bill.is_none());
146    }
147
148    #[test]
149    fn response_extract() {
150        let obj = vec!["Mary", "had", "a", "little", "lamb"];
151        let response = Response {
152            result: Some(RawValue::from_string(serde_json::to_string(&obj).unwrap()).unwrap()),
153            error: None,
154            id: serde_json::Value::Null,
155            jsonrpc: Some(String::from("2.0")),
156        };
157        let recovered1: Vec<String> = response.result().unwrap();
158        assert!(response.clone().check_error().is_ok());
159        let recovered2: Vec<String> = response.result().unwrap();
160        assert_eq!(obj, recovered1);
161        assert_eq!(obj, recovered2);
162    }
163
164    #[test]
165    fn null_result() {
166        let s = r#"{"result":null,"error":null,"id":"test"}"#;
167        let response: Response = serde_json::from_str(s).unwrap();
168        let recovered1: Result<(), _> = response.result();
169        let recovered2: Result<(), _> = response.result();
170        assert!(recovered1.is_ok());
171        assert!(recovered2.is_ok());
172
173        let recovered1: Result<String, _> = response.result();
174        let recovered2: Result<String, _> = response.result();
175        assert!(recovered1.is_err());
176        assert!(recovered2.is_err());
177    }
178
179    #[test]
180    fn batch_response() {
181        // from the jsonrpc.org spec example
182        let s = r#"[
183            {"jsonrpc": "2.0", "result": 7, "id": "1"},
184            {"jsonrpc": "2.0", "result": 19, "id": "2"},
185            {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
186            {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
187            {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
188        ]"#;
189        let batch_response: Vec<Response> = serde_json::from_str(s).unwrap();
190        assert_eq!(batch_response.len(), 5);
191    }
192
193    #[test]
194    fn test_arg() {
195        macro_rules! test_arg {
196            ($val:expr, $t:ty) => {{
197                let val1: $t = $val;
198                let arg = super::arg(val1.clone());
199                let val2: $t = serde_json::from_str(arg.get()).expect(stringify!($val));
200                assert_eq!(val1, val2, "failed test for {}", stringify!($val));
201            }};
202        }
203
204        test_arg!(true, bool);
205        test_arg!(42, u8);
206        test_arg!(42, usize);
207        test_arg!(42, isize);
208        test_arg!(vec![42, 35], Vec<u8>);
209        test_arg!(String::from("test"), String);
210
211        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
212        struct Test {
213            v: String,
214        }
215        test_arg!(
216            Test {
217                v: String::from("test"),
218            },
219            Test
220        );
221    }
222}