jsonrpc_client_transports/transports/
mod.rs

1//! Client transport implementations
2
3use jsonrpc_core::{Call, Error, Id, MethodCall, Notification, Params, Version};
4use jsonrpc_pubsub::SubscriptionId;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8use crate::{CallMessage, NotifyMessage, RpcError};
9
10pub mod duplex;
11#[cfg(feature = "http")]
12pub mod http;
13#[cfg(feature = "ipc")]
14pub mod ipc;
15pub mod local;
16#[cfg(feature = "ws")]
17pub mod ws;
18
19pub use duplex::duplex;
20
21/// Creates JSON-RPC requests
22pub struct RequestBuilder {
23	id: u64,
24}
25
26impl RequestBuilder {
27	/// Create a new RequestBuilder
28	pub fn new() -> Self {
29		RequestBuilder { id: 0 }
30	}
31
32	fn next_id(&mut self) -> Id {
33		let id = self.id;
34		self.id = id + 1;
35		Id::Num(id)
36	}
37
38	/// Build a single request with the next available id
39	fn single_request(&mut self, method: String, params: Params) -> (Id, String) {
40		let id = self.next_id();
41		let request = jsonrpc_core::Request::Single(Call::MethodCall(MethodCall {
42			jsonrpc: Some(Version::V2),
43			method,
44			params,
45			id: id.clone(),
46		}));
47		(
48			id,
49			serde_json::to_string(&request).expect("Request serialization is infallible; qed"),
50		)
51	}
52
53	fn call_request(&mut self, msg: &CallMessage) -> (Id, String) {
54		self.single_request(msg.method.clone(), msg.params.clone())
55	}
56
57	fn subscribe_request(&mut self, subscribe: String, subscribe_params: Params) -> (Id, String) {
58		self.single_request(subscribe, subscribe_params)
59	}
60
61	fn unsubscribe_request(&mut self, unsubscribe: String, sid: SubscriptionId) -> (Id, String) {
62		self.single_request(unsubscribe, Params::Array(vec![Value::from(sid)]))
63	}
64
65	fn notification(&mut self, msg: &NotifyMessage) -> String {
66		let request = jsonrpc_core::Request::Single(Call::Notification(Notification {
67			jsonrpc: Some(Version::V2),
68			method: msg.method.clone(),
69			params: msg.params.clone(),
70		}));
71		serde_json::to_string(&request).expect("Request serialization is infallible; qed")
72	}
73}
74
75/// Parse raw string into a single JSON value, together with the request Id.
76///
77/// This method will attempt to parse a JSON-RPC response object (either `Failure` or `Success`)
78/// and a `Notification` (for Subscriptions).
79/// Note that if you have more specific expectations about the returned type and don't want
80/// to handle all of them it might be best to deserialize on your own.
81pub fn parse_response(
82	response: &str,
83) -> Result<(Id, Result<Value, RpcError>, Option<String>, Option<SubscriptionId>), RpcError> {
84	jsonrpc_core::serde_from_str::<ClientResponse>(response)
85		.map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))
86		.map(|response| {
87			let id = response.id().unwrap_or(Id::Null);
88			let sid = response.subscription_id();
89			let method = response.method();
90			let value: Result<Value, Error> = response.into();
91			let result = value.map_err(RpcError::JsonRpcError);
92			(id, result, method, sid)
93		})
94}
95
96/// A type representing all possible values sent from the server to the client.
97#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
98#[serde(deny_unknown_fields)]
99#[serde(untagged)]
100pub enum ClientResponse {
101	/// A regular JSON-RPC request output (single response).
102	Output(jsonrpc_core::Output),
103	/// A notification.
104	Notification(jsonrpc_core::Notification),
105}
106
107impl ClientResponse {
108	/// Get the id of the response (if any).
109	pub fn id(&self) -> Option<Id> {
110		match *self {
111			ClientResponse::Output(ref output) => Some(output.id().clone()),
112			ClientResponse::Notification(_) => None,
113		}
114	}
115
116	/// Get the method name if the output is a notification.
117	pub fn method(&self) -> Option<String> {
118		match *self {
119			ClientResponse::Notification(ref n) => Some(n.method.to_owned()),
120			ClientResponse::Output(_) => None,
121		}
122	}
123
124	/// Parses the response into a subscription id.
125	pub fn subscription_id(&self) -> Option<SubscriptionId> {
126		match *self {
127			ClientResponse::Notification(ref n) => match &n.params {
128				jsonrpc_core::Params::Map(map) => match map.get("subscription") {
129					Some(value) => SubscriptionId::parse_value(value),
130					None => None,
131				},
132				_ => None,
133			},
134			_ => None,
135		}
136	}
137}
138
139impl From<ClientResponse> for Result<Value, Error> {
140	fn from(res: ClientResponse) -> Self {
141		match res {
142			ClientResponse::Output(output) => output.into(),
143			ClientResponse::Notification(n) => match &n.params {
144				Params::Map(map) => {
145					let subscription = map.get("subscription");
146					let result = map.get("result");
147					let error = map.get("error");
148
149					match (subscription, result, error) {
150						(Some(_), Some(result), _) => Ok(result.to_owned()),
151						(Some(_), _, Some(error)) => {
152							let error = serde_json::from_value::<Error>(error.to_owned())
153								.ok()
154								.unwrap_or_else(Error::parse_error);
155							Err(error)
156						}
157						_ => Ok(n.params.into()),
158					}
159				}
160				_ => Ok(n.params.into()),
161			},
162		}
163	}
164}
165
166#[cfg(test)]
167mod tests {
168	use super::*;
169	use jsonrpc_core::{Failure, Notification, Output, Params, Success, Value, Version};
170
171	#[test]
172	fn notification_deserialize() {
173		let dsr = r#"{"jsonrpc":"2.0","method":"hello","params":[10]}"#;
174
175		let deserialized: ClientResponse = jsonrpc_core::serde_from_str(dsr).unwrap();
176		assert_eq!(
177			deserialized,
178			ClientResponse::Notification(Notification {
179				jsonrpc: Some(Version::V2),
180				method: "hello".into(),
181				params: Params::Array(vec![Value::from(10)]),
182			})
183		);
184	}
185
186	#[test]
187	fn success_deserialize() {
188		let dsr = r#"{"jsonrpc":"2.0","result":1,"id":1}"#;
189
190		let deserialized: ClientResponse = jsonrpc_core::serde_from_str(dsr).unwrap();
191		assert_eq!(
192			deserialized,
193			ClientResponse::Output(Output::Success(Success {
194				jsonrpc: Some(Version::V2),
195				id: Id::Num(1),
196				result: 1.into(),
197			}))
198		);
199	}
200
201	#[test]
202	fn failure_output_deserialize() {
203		let dfo = r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":1}"#;
204
205		let deserialized: ClientResponse = jsonrpc_core::serde_from_str(dfo).unwrap();
206		assert_eq!(
207			deserialized,
208			ClientResponse::Output(Output::Failure(Failure {
209				jsonrpc: Some(Version::V2),
210				error: Error::parse_error(),
211				id: Id::Num(1)
212			}))
213		);
214	}
215}