1use std::borrow::Cow;
9use std::collections::HashMap;
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::sync::atomic;
13
14use serde_json::value::RawValue;
15use serde_json::Value;
16
17use crate::error::Error;
18use crate::{Request, Response};
19
20pub trait Transport: Send + Sync + 'static {
22 fn send_request(&self, _: Request) -> Result<Response, Error>;
24 fn send_batch(&self, _: &[Request]) -> Result<Vec<Response>, Error>;
26 fn fmt_target(&self, f: &mut fmt::Formatter) -> fmt::Result;
28}
29
30pub struct Client {
35 pub(crate) transport: Box<dyn Transport>,
36 nonce: atomic::AtomicUsize,
37}
38
39impl Client {
40 pub fn with_transport<T: Transport>(transport: T) -> Client {
42 Client {
43 transport: Box::new(transport),
44 nonce: atomic::AtomicUsize::new(1),
45 }
46 }
47
48 pub fn build_request<'a>(&self, method: &'a str, params: &'a [Box<RawValue>]) -> Request<'a> {
53 let nonce = self.nonce.fetch_add(1, atomic::Ordering::Relaxed);
54 Request {
55 method,
56 params,
57 id: serde_json::Value::from(nonce),
58 jsonrpc: Some("2.0"),
59 }
60 }
61
62 pub fn send_request(&self, request: Request) -> Result<Response, Error> {
64 self.transport.send_request(request)
65 }
66
67 pub fn send_batch(&self, requests: &[Request]) -> Result<Vec<Option<Response>>, Error> {
77 if requests.is_empty() {
78 return Err(Error::EmptyBatch);
79 }
80
81 let responses = self.transport.send_batch(requests)?;
84 if responses.len() > requests.len() {
85 return Err(Error::WrongBatchResponseSize);
86 }
87
88 let mut by_id = HashMap::with_capacity(requests.len());
92 for resp in responses.into_iter() {
93 let id = HashableValue(Cow::Owned(resp.id.clone()));
94 if let Some(dup) = by_id.insert(id, resp) {
95 return Err(Error::BatchDuplicateResponseId(dup.id));
96 }
97 }
98 let results =
100 requests.iter().map(|r| by_id.remove(&HashableValue(Cow::Borrowed(&r.id)))).collect();
101
102 if let Some(id) = by_id.keys().next() {
105 return Err(Error::WrongBatchResponseId((*id.0).clone()));
106 }
107
108 Ok(results)
109 }
110
111 pub fn call<R: for<'a> serde::de::Deserialize<'a>>(
116 &self,
117 method: &str,
118 args: &[Box<RawValue>],
119 ) -> Result<R, Error> {
120 let request = self.build_request(method, args);
121 let id = request.id.clone();
122
123 let response = self.send_request(request)?;
124 if response.jsonrpc.is_some() && response.jsonrpc != Some(From::from("2.0")) {
125 return Err(Error::VersionMismatch);
126 }
127 if response.id != id {
128 return Err(Error::NonceMismatch);
129 }
130
131 response.result()
132 }
133}
134
135impl fmt::Debug for crate::Client {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 write!(f, "jsonrpc::Client(")?;
138 self.transport.fmt_target(f)?;
139 write!(f, ")")
140 }
141}
142
143impl<T: Transport> From<T> for Client {
144 fn from(t: T) -> Client {
145 Client::with_transport(t)
146 }
147}
148
149#[derive(Clone, PartialEq, Debug)]
158struct HashableValue<'a>(pub Cow<'a, Value>);
159
160impl<'a> Eq for HashableValue<'a> {}
161
162impl<'a> Hash for HashableValue<'a> {
163 fn hash<H: Hasher>(&self, state: &mut H) {
164 match *self.0.as_ref() {
165 Value::Null => "null".hash(state),
166 Value::Bool(false) => "false".hash(state),
167 Value::Bool(true) => "true".hash(state),
168 Value::Number(ref n) => {
169 "number".hash(state);
170 if let Some(n) = n.as_i64() {
171 n.hash(state);
172 } else if let Some(n) = n.as_u64() {
173 n.hash(state);
174 } else {
175 n.to_string().hash(state);
176 }
177 }
178 Value::String(ref s) => {
179 "string".hash(state);
180 s.hash(state);
181 }
182 Value::Array(ref v) => {
183 "array".hash(state);
184 v.len().hash(state);
185 for obj in v {
186 HashableValue(Cow::Borrowed(obj)).hash(state);
187 }
188 }
189 Value::Object(ref m) => {
190 "object".hash(state);
191 m.len().hash(state);
192 for (key, val) in m {
193 key.hash(state);
194 HashableValue(Cow::Borrowed(val)).hash(state);
195 }
196 }
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 use std::borrow::Cow;
206 use std::collections::HashSet;
207 use std::str::FromStr;
208 use std::sync;
209
210 struct DummyTransport;
211 impl Transport for DummyTransport {
212 fn send_request(&self, _: Request) -> Result<Response, Error> {
213 Err(Error::NonceMismatch)
214 }
215 fn send_batch(&self, _: &[Request]) -> Result<Vec<Response>, Error> {
216 Ok(vec![])
217 }
218 fn fmt_target(&self, _: &mut fmt::Formatter) -> fmt::Result {
219 Ok(())
220 }
221 }
222
223 #[test]
224 fn sanity() {
225 let client = Client::with_transport(DummyTransport);
226 assert_eq!(client.nonce.load(sync::atomic::Ordering::Relaxed), 1);
227 let req1 = client.build_request("test", &[]);
228 assert_eq!(client.nonce.load(sync::atomic::Ordering::Relaxed), 2);
229 let req2 = client.build_request("test", &[]);
230 assert_eq!(client.nonce.load(sync::atomic::Ordering::Relaxed), 3);
231 assert!(req1.id != req2.id);
232 }
233
234 #[test]
235 fn hash_value() {
236 let val = HashableValue(Cow::Owned(Value::from_str("null").unwrap()));
237 let t = HashableValue(Cow::Owned(Value::from_str("true").unwrap()));
238 let f = HashableValue(Cow::Owned(Value::from_str("false").unwrap()));
239 let ns =
240 HashableValue(Cow::Owned(Value::from_str("[0, -0, 123.4567, -100000000]").unwrap()));
241 let m =
242 HashableValue(Cow::Owned(Value::from_str("{ \"field\": 0, \"field\": -0 }").unwrap()));
243
244 let mut coll = HashSet::new();
245
246 assert!(!coll.contains(&val));
247 coll.insert(val.clone());
248 assert!(coll.contains(&val));
249
250 assert!(!coll.contains(&t));
251 assert!(!coll.contains(&f));
252 coll.insert(t.clone());
253 assert!(coll.contains(&t));
254 assert!(!coll.contains(&f));
255 coll.insert(f.clone());
256 assert!(coll.contains(&t));
257 assert!(coll.contains(&f));
258
259 assert!(!coll.contains(&ns));
260 coll.insert(ns.clone());
261 assert!(coll.contains(&ns));
262
263 assert!(!coll.contains(&m));
264 coll.insert(m.clone());
265 assert!(coll.contains(&m));
266 }
267}