rpc_lib/
client.rs

1use crate::error::{Error, ResponseError, Result as RpcResult};
2
3use std::fmt;
4
5use serde::{
6    de,
7    de::{DeserializeOwned, Deserializer, MapAccess, Visitor},
8    Deserialize, Serialize,
9};
10
11use reqwest::{
12    header,
13    header::{HeaderMap, HeaderValue},
14};
15
16pub mod prelude {
17    #[doc(no_inline)]
18    pub use crate::client::Client;
19}
20
21const RPC_VERSION: &'static str = "2.0";
22
23/// Client contains all information and authentication to hit an RPC server
24#[derive(Debug)]
25pub struct Client<'a> {
26    client: reqwest::Client,
27    url: &'a str,
28    headers: HeaderMap,
29}
30
31impl<'a> Client<'a> {
32    pub fn new(url: &'a str) -> Self {
33        let mut headers = HeaderMap::new();
34        headers.insert(
35            header::USER_AGENT,
36            HeaderValue::from_str(&format!("rpc-lib/{}", env!("CARGO_PKG_VERSION"))).unwrap(),
37        );
38        headers.insert(header::ACCEPT, HeaderValue::from_static("application/json"));
39        headers.insert(
40            header::CONTENT_TYPE,
41            HeaderValue::from_static("application/json"),
42        );
43
44        Client {
45            client: reqwest::Client::new(),
46            url,
47            headers,
48        }
49    }
50
51    pub fn with_basic_auth(mut self, username: &'a str, password: Option<&'a str>) -> Self {
52        let auth = match password {
53            Some(password) => format!("{}:{}", username, password),
54            None => format!("{}:", username),
55        };
56        let header_value = format!("Basic {}", base64::encode(&auth));
57        self.headers
58            .insert(header::AUTHORIZATION, header_value.parse().unwrap());
59        self
60    }
61
62    pub fn with_user_agent<S>(mut self, user_agent: S) -> Self
63    where
64        S: Into<String>,
65    {
66        self.headers
67            .insert(header::USER_AGENT, user_agent.into().parse().unwrap());
68        self
69    }
70
71    pub fn call<T>(&self, id: &'a str, method: &'a str, params: impl Serialize) -> RpcResult<T>
72    where
73        T: DeserializeOwned,
74    {
75        let req = Request::new(id, method, params);
76        let mut results: Response<T> = self
77            .client
78            .post(self.url)
79            .headers(self.headers.clone())
80            .json(&req)
81            .send()?
82            .json()?;
83
84        if results.error.is_some() {
85            return Err(Error::from(results.error.unwrap()));
86        }
87        results.result.take().ok_or_else(|| {
88            Error::from(ResponseError {
89                code: -2,
90                message: "invalid response".to_owned(),
91            })
92        })
93    }
94}
95
96#[derive(Debug, Deserialize)]
97struct Response<T> {
98    //    jsonrpc: String,
99    result: Option<T>,
100    #[serde(deserialize_with = "string_map_or_null")]
101    error: Option<ResponseError>,
102    id: String,
103}
104
105#[derive(Debug, Serialize)]
106struct Request<'a, T> {
107    jsonrpc: &'static str,
108    method: &'a str,
109    params: T,
110    id: &'a str,
111}
112
113impl<'a, T> Request<'a, T>
114where
115    T: Serialize,
116{
117    pub fn new(id: &'a str, method: &'a str, params: T) -> Self {
118        Request {
119            jsonrpc: RPC_VERSION,
120            method,
121            params,
122            id,
123        }
124    }
125}
126
127fn string_map_or_null<'de, D>(deserializer: D) -> Result<Option<ResponseError>, D::Error>
128where
129    D: Deserializer<'de>,
130{
131    struct StringMapOrNull;
132
133    impl<'de> Visitor<'de> for StringMapOrNull {
134        type Value = Option<ResponseError>;
135
136        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
137            formatter.write_str("string, map, or null")
138        }
139
140        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
141        where
142            E: de::Error,
143        {
144            Ok(Some(ResponseError {
145                code: -1,
146                message: value.to_owned(),
147            }))
148        }
149
150        fn visit_unit<E>(self) -> Result<Self::Value, E>
151        where
152            E: de::Error,
153        {
154            Ok(None)
155        }
156
157        fn visit_map<M>(self, visitor: M) -> Result<Self::Value, M::Error>
158        where
159            M: MapAccess<'de>,
160        {
161            let deserializer = de::value::MapAccessDeserializer::new(visitor);
162            let response_error = ResponseError::deserialize(deserializer)?;
163            Ok(Some(response_error))
164        }
165    }
166    deserializer.deserialize_any(StringMapOrNull)
167}