bitcoind_client/
client.rs1use alloc::string::ToString;
4use core::future::Future;
5use core::sync::atomic::{AtomicUsize, Ordering};
6
7use jsonrpc::serde_json;
8use jsonrpc::{Request, Response};
9use serde::Deserialize;
10use serde_json::{
11 json,
12 value::{RawValue, Value},
13};
14
15use crate::Error;
16use crate::Rpc;
17
18const JSONRPC: &str = "2.0";
20
21#[derive(Debug)]
23pub struct Client {
24 id: AtomicUsize,
26}
27
28impl Default for Client {
29 fn default() -> Self {
30 Self::new()
31 }
32}
33
34impl Client {
35 pub fn new() -> Self {
37 Self {
38 id: AtomicUsize::new(0),
39 }
40 }
41
42 pub fn call<T, E>(
44 &self,
45 rpc: Rpc,
46 params: &[Value],
47 send_fn: impl Fn(Request) -> Result<Response, E>,
48 ) -> Result<T, Error>
49 where
50 T: for<'de> Deserialize<'de>,
51 E: core::error::Error + Send + Sync + 'static,
52 {
53 let method = rpc.to_string();
54 let raw_value = if params.is_empty() {
55 None
56 } else {
57 Some(serde_json::value::to_raw_value(params)?)
58 };
59 let request = self.request(&method, raw_value.as_deref());
60 let request_id = request.id.clone();
61 let response = send_fn(request).map_err(Error::transport)?;
62 if response.id != request_id {
63 return Err(Error::IdMismatch);
64 }
65 Ok(response.result()?)
66 }
67
68 pub async fn call_async<T, E, F, Fut>(
70 &self,
71 rpc: Rpc,
72 params: &[Value],
73 send_fn: F,
74 ) -> Result<T, Error>
75 where
76 T: for<'de> Deserialize<'de>,
77 E: core::error::Error + Send + Sync + 'static,
78 F: Fn(Value) -> Fut,
79 Fut: Future<Output = Result<Response, E>>,
80 {
81 let method = rpc.to_string();
82 let raw_value = if params.is_empty() {
83 None
84 } else {
85 Some(serde_json::value::to_raw_value(params)?)
86 };
87 let request = self.request(&method, raw_value.as_deref());
88 let request_id = request.id.clone();
89 let value = serde_json::to_value(request)?;
90 let response = send_fn(value).await.map_err(Error::transport)?;
91 if response.id != request_id {
92 return Err(Error::IdMismatch);
93 }
94 Ok(response.result()?)
95 }
96
97 fn request<'a>(&self, method: &'a str, params: Option<&'a RawValue>) -> Request<'a> {
99 let id = self.id.fetch_add(1, Ordering::Relaxed);
100 Request {
101 method,
102 params,
103 id: json!(id),
104 jsonrpc: Some(JSONRPC),
105 }
106 }
107}