1use alloy_json_rpc::{
2 transform_response, try_deserialize_ok, Request, RequestMeta, RequestPacket, ResponsePacket,
3 RpcRecv, RpcResult, RpcSend,
4};
5use alloy_transport::{
6 BoxTransport, IntoBoxTransport, RpcFut, TransportError, TransportErrorKind, TransportResult,
7};
8use futures::FutureExt;
9use serde_json::value::RawValue;
10use std::{
11 fmt,
12 future::Future,
13 marker::PhantomData,
14 pin::Pin,
15 task::{self, ready, Poll::Ready},
16};
17use tower::Service;
18
19#[must_use = "futures do nothing unless you `.await` or poll them"]
21#[pin_project::pin_project(project = CallStateProj)]
22enum CallState<Params>
23where
24 Params: RpcSend,
25{
26 Prepared {
27 request: Option<Request<Params>>,
28 connection: BoxTransport,
29 },
30 AwaitingResponse {
31 #[pin]
32 fut: <BoxTransport as Service<RequestPacket>>::Future,
33 },
34 Complete,
35}
36
37impl<Params> Clone for CallState<Params>
38where
39 Params: RpcSend,
40{
41 fn clone(&self) -> Self {
42 match self {
43 Self::Prepared { request, connection } => {
44 Self::Prepared { request: request.clone(), connection: connection.clone() }
45 }
46 _ => panic!("cloned after dispatch"),
47 }
48 }
49}
50
51impl<Params> fmt::Debug for CallState<Params>
52where
53 Params: RpcSend,
54{
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 f.write_str(match self {
57 Self::Prepared { .. } => "Prepared",
58 Self::AwaitingResponse { .. } => "AwaitingResponse",
59 Self::Complete => "Complete",
60 })
61 }
62}
63
64impl<Params> Future for CallState<Params>
65where
66 Params: RpcSend,
67{
68 type Output = TransportResult<Box<RawValue>>;
69
70 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
71 loop {
72 match self.as_mut().project() {
73 CallStateProj::Prepared { connection, request } => {
74 if let Err(e) =
75 task::ready!(Service::<RequestPacket>::poll_ready(connection, cx))
76 {
77 self.set(Self::Complete);
78 return Ready(RpcResult::Err(e));
79 }
80
81 let request = request.take().expect("no request");
82 if tracing::enabled!(tracing::Level::TRACE) {
83 trace!(?request, "sending request");
84 } else {
85 debug!(method=%request.meta.method, id=%request.meta.id, "sending request");
86 }
87 let request = request.serialize();
88 let fut = match request {
89 Ok(request) => {
90 trace!(request=%request.serialized(), "serialized request");
91 connection.call(request.into())
92 }
93 Err(err) => {
94 trace!(?err, "failed to serialize request");
95 self.set(Self::Complete);
96 return Ready(RpcResult::Err(TransportError::ser_err(err)));
97 }
98 };
99 self.set(Self::AwaitingResponse { fut });
100 }
101 CallStateProj::AwaitingResponse { fut } => {
102 let res = match task::ready!(fut.poll(cx)) {
103 Ok(ResponsePacket::Single(res)) => Ready(transform_response(res)),
104 Err(e) => Ready(RpcResult::Err(e)),
105 Ok(ResponsePacket::Batch(_)) => {
106 Ready(RpcResult::Err(TransportErrorKind::custom_str(
107 "received batch response from single request",
108 )))
109 }
110 };
111 self.set(Self::Complete);
112 return res;
113 }
114 CallStateProj::Complete => {
115 panic!("Polled after completion");
116 }
117 }
118 }
119 }
120}
121
122#[must_use = "futures do nothing unless you `.await` or poll them"]
141#[pin_project::pin_project]
142#[derive(Clone)]
143pub struct RpcCall<Params, Resp, Output = Resp, Map = fn(Resp) -> Output>
144where
145 Params: RpcSend,
146 Map: FnOnce(Resp) -> Output,
147{
148 #[pin]
149 state: CallState<Params>,
150 map: Option<Map>,
151 _pd: core::marker::PhantomData<fn() -> (Resp, Output)>,
152}
153
154impl<Params, Resp, Output, Map> core::fmt::Debug for RpcCall<Params, Resp, Output, Map>
155where
156 Params: RpcSend,
157 Map: FnOnce(Resp) -> Output,
158{
159 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
160 f.debug_struct("RpcCall").field("state", &self.state).finish()
161 }
162}
163
164impl<Params, Resp> RpcCall<Params, Resp>
165where
166 Params: RpcSend,
167{
168 #[doc(hidden)]
169 pub fn new(req: Request<Params>, connection: impl IntoBoxTransport) -> Self {
170 Self {
171 state: CallState::Prepared {
172 request: Some(req),
173 connection: connection.into_box_transport(),
174 },
175 map: Some(std::convert::identity),
176 _pd: PhantomData,
177 }
178 }
179}
180
181impl<Params, Resp, Output, Map> RpcCall<Params, Resp, Output, Map>
182where
183 Params: RpcSend,
184 Map: FnOnce(Resp) -> Output,
185{
186 pub fn map_resp<NewOutput, NewMap>(
198 self,
199 map: NewMap,
200 ) -> RpcCall<Params, Resp, NewOutput, NewMap>
201 where
202 NewMap: FnOnce(Resp) -> NewOutput,
203 {
204 RpcCall { state: self.state, map: Some(map), _pd: PhantomData }
205 }
206
207 pub fn is_subscription(&self) -> bool {
213 self.request().meta.is_subscription()
214 }
215
216 pub fn set_is_subscription(&mut self) {
223 self.request_mut().meta.set_is_subscription();
224 }
225
226 pub fn set_subscription_status(&mut self, status: bool) {
228 self.request_mut().meta.set_subscription_status(status);
229 }
230
231 pub fn params(&mut self) -> &mut Params {
240 &mut self.request_mut().params
241 }
242
243 pub fn request(&self) -> &Request<Params> {
249 let CallState::Prepared { request, .. } = &self.state else {
250 panic!("Cannot get request after request has been sent");
251 };
252 request.as_ref().expect("no request in prepared")
253 }
254
255 pub fn method(&self) -> &str {
257 &self.request().meta.method
258 }
259
260 pub fn request_mut(&mut self) -> &mut Request<Params> {
266 let CallState::Prepared { request, .. } = &mut self.state else {
267 panic!("Cannot get request after request has been sent");
268 };
269 request.as_mut().expect("no request in prepared")
270 }
271
272 pub fn map_params<NewParams: RpcSend>(
274 self,
275 map: impl Fn(Params) -> NewParams,
276 ) -> RpcCall<NewParams, Resp, Output, Map> {
277 let CallState::Prepared { request, connection } = self.state else {
278 panic!("Cannot get request after request has been sent");
279 };
280 let request = request.expect("no request in prepared").map_params(map);
281 RpcCall {
282 state: CallState::Prepared { request: Some(request), connection },
283 map: self.map,
284 _pd: PhantomData,
285 }
286 }
287
288 pub fn map_meta(self, f: impl FnOnce(RequestMeta) -> RequestMeta) -> Self {
290 let CallState::Prepared { request, connection } = self.state else {
291 panic!("Cannot get request after request has been sent");
292 };
293 let request = request.expect("no request in prepared").map_meta(f);
294 Self {
295 state: CallState::Prepared { request: Some(request), connection },
296 map: self.map,
297 _pd: PhantomData,
298 }
299 }
300}
301
302impl<Params, Resp, Output, Map> RpcCall<&Params, Resp, Output, Map>
303where
304 Params: RpcSend + ToOwned,
305 Params::Owned: RpcSend,
306 Map: FnOnce(Resp) -> Output,
307{
308 pub fn into_owned_params(self) -> RpcCall<Params::Owned, Resp, Output, Map> {
314 let CallState::Prepared { request, connection } = self.state else {
315 panic!("Cannot get params after request has been sent");
316 };
317 let request = request.expect("no request in prepared").into_owned_params();
318
319 RpcCall {
320 state: CallState::Prepared { request: Some(request), connection },
321 map: self.map,
322 _pd: PhantomData,
323 }
324 }
325}
326
327impl<'a, Params, Resp, Output, Map> RpcCall<Params, Resp, Output, Map>
328where
329 Params: RpcSend + 'a,
330 Resp: RpcRecv,
331 Output: 'static,
332 Map: FnOnce(Resp) -> Output + Send + 'a,
333{
334 pub fn boxed(self) -> RpcFut<'a, Output> {
336 Box::pin(self)
337 }
338}
339
340impl<Params, Resp, Output, Map> Future for RpcCall<Params, Resp, Output, Map>
341where
342 Params: RpcSend,
343 Resp: RpcRecv,
344 Output: 'static,
345 Map: FnOnce(Resp) -> Output,
346{
347 type Output = TransportResult<Output>;
348
349 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
350 let this = self.get_mut();
351 let resp = try_deserialize_ok(ready!(this.state.poll_unpin(cx)));
352 Ready(resp.map(this.map.take().expect("polled after completion")))
353 }
354}