1use serde::de::DeserializeOwned;
4use serde::Serialize;
5
6use crate::method::Method;
7use crate::prelude::reqwest::Client as HttpClient;
8use crate::protos::rings_node::*;
9
10pub struct Client {
12 client: HttpClient,
13 endpoint_url: String,
14}
15
16#[derive(Debug, thiserror::Error)]
18pub enum RpcError {
19 #[error("Server returned rpc error {0}")]
21 JsonClientError(jsonrpc_core::Error),
22 #[error("Failed to parse server response as {0}: {1}")]
24 ParseError(String, Box<dyn std::error::Error + Send>),
25 #[error("Request timed out")]
27 Timeout,
28 #[error("Client error: {0}")]
30 Client(String),
31 #[error("{0}")]
33 Other(Box<dyn std::error::Error + Send>),
34}
35
36type Result<T> = std::result::Result<T, RpcError>;
38
39impl Client {
40 pub fn new(endpoint_url: &str) -> Self {
42 Self {
43 client: HttpClient::default(),
44 endpoint_url: endpoint_url.to_string(),
45 }
46 }
47
48 pub async fn call_method<T>(&self, method: Method, req: &impl Serialize) -> Result<T>
50 where T: DeserializeOwned {
51 use jsonrpc_core::*;
52
53 let params = serde_json::to_value(req)
54 .map_err(|e| RpcError::Client(e.to_string()))?
55 .as_object()
56 .ok_or(RpcError::Client("params should be an object".to_string()))?
57 .clone();
58
59 let jsonrpc_request = Request::Single(Call::MethodCall(MethodCall {
60 jsonrpc: Some(Version::V2),
61 method: method.to_string(),
62 params: Params::Map(params),
63 id: Id::Num(1),
64 }));
65
66 let result = self.do_jsonrpc_request(&jsonrpc_request).await?;
67 serde_json::from_value(result).map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))
68 }
69
70 async fn do_jsonrpc_request(&self, req: &jsonrpc_core::Request) -> Result<serde_json::Value> {
71 let body = serde_json::to_string(req).map_err(|e| RpcError::Client(e.to_string()))?;
72
73 let req = self
74 .client
75 .post(self.endpoint_url.as_str())
76 .header("content-type", "application/json")
77 .header("accept", "application/json")
78 .body(body);
79
80 let resp = req
81 .send()
82 .await
83 .map_err(|e| RpcError::Client(e.to_string()))?
84 .error_for_status()
85 .map_err(|e| RpcError::Client(e.to_string()))?
86 .bytes()
87 .await
88 .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?;
89
90 let jsonrpc_resp = jsonrpc_core::Response::from_json(&String::from_utf8_lossy(&resp))
91 .map_err(|e| RpcError::ParseError(e.to_string(), Box::new(e)))?;
92
93 match jsonrpc_resp {
94 jsonrpc_core::Response::Single(resp) => match resp {
95 jsonrpc_core::Output::Success(success) => Ok(success.result),
96 jsonrpc_core::Output::Failure(failure) => {
97 Err(RpcError::JsonClientError(failure.error))
98 }
99 },
100 jsonrpc_core::Response::Batch(_) => Err(RpcError::Client(
101 "Batch response is not supported".to_string(),
102 )),
103 }
104 }
105
106 pub async fn connect_peer_via_http(
115 &self,
116 req: &ConnectPeerViaHttpRequest,
117 ) -> Result<ConnectPeerViaHttpResponse> {
118 self.call_method(Method::ConnectPeerViaHttp, req).await
119 }
120
121 pub async fn connect_with_did(
123 &self,
124 req: &ConnectWithDidRequest,
125 ) -> Result<ConnectWithSeedResponse> {
126 self.call_method(Method::ConnectWithDid, req).await
127 }
128
129 pub async fn connect_with_seed(
131 &self,
132 req: &ConnectWithSeedRequest,
133 ) -> Result<ConnectWithSeedResponse> {
134 self.call_method(Method::ConnectWithSeed, req).await
135 }
136
137 pub async fn list_peers(&self, req: &ListPeersRequest) -> Result<ListPeersResponse> {
141 self.call_method(Method::ListPeers, req).await
142 }
143
144 pub async fn create_offer(&self, req: &CreateOfferRequest) -> Result<CreateOfferResponse> {
146 self.call_method(Method::CreateOffer, req).await
147 }
148
149 pub async fn answer_offer(&self, req: &AnswerOfferRequest) -> Result<AnswerOfferResponse> {
151 self.call_method(Method::AnswerOffer, req).await
152 }
153
154 pub async fn accept_answer(&self, req: &AcceptAnswerRequest) -> Result<AcceptAnswerResponse> {
156 self.call_method(Method::AcceptAnswer, req).await
157 }
158
159 pub async fn disconnect(&self, req: &DisconnectRequest) -> Result<DisconnectResponse> {
161 self.call_method(Method::Disconnect, req).await
162 }
163
164 pub async fn send_backend_message(
166 &self,
167 req: &SendBackendMessageRequest,
168 ) -> Result<SendBackendMessageResponse> {
169 self.call_method(Method::SendBackendMessage, req).await
170 }
171
172 pub async fn send_e2e_handshake(
174 &self,
175 req: &SendE2eHandshakeRequest,
176 ) -> Result<SendE2eHandshakeResponse> {
177 self.call_method(Method::SendE2eHandshake, req).await
178 }
179
180 pub async fn send_e2e_message(
182 &self,
183 req: &SendE2eMessageRequest,
184 ) -> Result<SendE2eMessageResponse> {
185 self.call_method(Method::SendE2eMessage, req).await
186 }
187
188 pub async fn publish_message_to_topic(
190 &self,
191 req: &PublishMessageToTopicRequest,
192 ) -> Result<PublishMessageToTopicResponse> {
193 self.call_method(Method::PublishMessageToTopic, req).await
194 }
195
196 pub async fn fetch_topic_messages(
198 &self,
199 req: &FetchTopicMessagesRequest,
200 ) -> Result<FetchTopicMessagesResponse> {
201 self.call_method(Method::FetchTopicMessages, req).await
202 }
203
204 pub async fn register_service(
206 &self,
207 req: &RegisterServiceRequest,
208 ) -> Result<RegisterServiceResponse> {
209 self.call_method(Method::RegisterService, req).await
210 }
211
212 pub async fn lookup_service(
214 &self,
215 req: &LookupServiceRequest,
216 ) -> Result<LookupServiceResponse> {
217 self.call_method(Method::LookupService, req).await
218 }
219
220 pub async fn lookup_online_nodes(
222 &self,
223 req: &LookupOnlineNodesRequest,
224 ) -> Result<LookupOnlineNodesResponse> {
225 self.call_method(Method::LookupOnlineNodes, req).await
226 }
227
228 pub async fn lookup_onion_exits(
230 &self,
231 req: &LookupOnionExitsRequest,
232 ) -> Result<LookupOnionExitsResponse> {
233 self.call_method(Method::LookupOnionExits, req).await
234 }
235
236 pub async fn build_onion_route(
238 &self,
239 req: &BuildOnionRouteRequest,
240 ) -> Result<BuildOnionRouteResponse> {
241 self.call_method(Method::BuildOnionRoute, req).await
242 }
243
244 pub async fn node_info(&self, req: &NodeInfoRequest) -> Result<NodeInfoResponse> {
246 self.call_method(Method::NodeInfo, req).await
247 }
248
249 pub async fn peer_measurement(
251 &self,
252 req: &PeerMeasurementRequest,
253 ) -> Result<PeerMeasurementResponse> {
254 self.call_method(Method::PeerMeasurement, req).await
255 }
256
257 pub async fn list_peer_measurements(
259 &self,
260 req: &ListPeerMeasurementsRequest,
261 ) -> Result<ListPeerMeasurementsResponse> {
262 self.call_method(Method::ListPeerMeasurements, req).await
263 }
264
265 pub async fn node_did(&self, req: &NodeDidRequest) -> Result<NodeDidResponse> {
267 self.call_method(Method::NodeDid, req).await
268 }
269}