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#[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 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}