1use alloy_json_rpc::{RpcRecv, RpcSend};
2use alloy_rpc_client::{RpcCall, Waiter};
3use alloy_transport::TransportResult;
4use futures::FutureExt;
5use pin_project::pin_project;
6use serde_json::value::RawValue;
7use std::{
8 future::Future,
9 pin::Pin,
10 task::{self, Poll},
11};
12use tokio::sync::oneshot;
13
14#[cfg(not(target_arch = "wasm32"))]
15pub type BoxedFut<Output> = Pin<Box<dyn Future<Output = TransportResult<Output>> + Send>>;
17
18#[cfg(target_arch = "wasm32")]
19pub type BoxedFut<Output> = Pin<Box<dyn Future<Output = TransportResult<Output>>>>;
21#[pin_project(project = ProviderCallProj)]
32pub enum ProviderCall<Params, Resp, Output = Resp, Map = fn(Resp) -> Output>
33where
34 Params: RpcSend,
35 Resp: RpcRecv,
36 Map: Fn(Resp) -> Output,
37{
38 RpcCall(RpcCall<Params, Resp, Output, Map>),
40 Waiter(Waiter<Resp, Output, Map>),
42 BoxedFuture(BoxedFut<Output>),
44 Ready(Option<TransportResult<Output>>),
46}
47
48impl<Params, Resp, Output, Map> ProviderCall<Params, Resp, Output, Map>
49where
50 Params: RpcSend,
51 Resp: RpcRecv,
52 Map: Fn(Resp) -> Output,
53{
54 pub const fn ready(output: TransportResult<Output>) -> Self {
56 Self::Ready(Some(output))
57 }
58
59 pub const fn is_rpc_call(&self) -> bool {
61 matches!(self, Self::RpcCall(_))
62 }
63
64 pub const fn as_rpc_call(&self) -> Option<&RpcCall<Params, Resp, Output, Map>> {
66 match self {
67 Self::RpcCall(call) => Some(call),
68 _ => None,
69 }
70 }
71
72 pub fn as_mut_rpc_call(&mut self) -> Option<&mut RpcCall<Params, Resp, Output, Map>> {
74 match self {
75 Self::RpcCall(call) => Some(call),
76 _ => None,
77 }
78 }
79
80 pub const fn is_waiter(&self) -> bool {
82 matches!(self, Self::Waiter(_))
83 }
84
85 pub const fn as_waiter(&self) -> Option<&Waiter<Resp, Output, Map>> {
87 match self {
88 Self::Waiter(waiter) => Some(waiter),
89 _ => None,
90 }
91 }
92
93 pub fn as_mut_waiter(&mut self) -> Option<&mut Waiter<Resp, Output, Map>> {
95 match self {
96 Self::Waiter(waiter) => Some(waiter),
97 _ => None,
98 }
99 }
100
101 pub const fn is_boxed_future(&self) -> bool {
103 matches!(self, Self::BoxedFuture(_))
104 }
105
106 pub const fn as_boxed_future(&self) -> Option<&BoxedFut<Output>> {
108 match self {
109 Self::BoxedFuture(fut) => Some(fut),
110 _ => None,
111 }
112 }
113
114 pub const fn is_ready(&self) -> bool {
116 matches!(self, Self::Ready(_))
117 }
118
119 pub const fn as_ready(&self) -> Option<&TransportResult<Output>> {
125 match self {
126 Self::Ready(Some(output)) => Some(output),
127 Self::Ready(None) => panic!("tried to access ready value after taking"),
128 _ => None,
129 }
130 }
131
132 pub fn map_resp<NewOutput, NewMap>(
148 self,
149 map: NewMap,
150 ) -> Result<ProviderCall<Params, Resp, NewOutput, NewMap>, Self>
151 where
152 NewMap: Fn(Resp) -> NewOutput + Clone,
153 {
154 match self {
155 Self::RpcCall(call) => Ok(ProviderCall::RpcCall(call.map_resp(map))),
156 Self::Waiter(waiter) => Ok(ProviderCall::Waiter(waiter.map_resp(map))),
157 _ => Err(self),
158 }
159 }
160}
161
162impl<Params, Resp, Output, Map> ProviderCall<&Params, Resp, Output, Map>
163where
164 Params: RpcSend + ToOwned,
165 Params::Owned: RpcSend,
166 Resp: RpcRecv,
167 Map: Fn(Resp) -> Output,
168{
169 pub fn into_owned_params(self) -> ProviderCall<Params::Owned, Resp, Output, Map> {
175 match self {
176 Self::RpcCall(call) => ProviderCall::RpcCall(call.into_owned_params()),
177 _ => panic!(),
178 }
179 }
180}
181
182impl<Params, Resp> std::fmt::Debug for ProviderCall<Params, Resp>
183where
184 Params: RpcSend,
185 Resp: RpcRecv,
186{
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 match self {
189 Self::RpcCall(call) => f.debug_tuple("RpcCall").field(call).finish(),
190 Self::Waiter { .. } => f.debug_struct("Waiter").finish_non_exhaustive(),
191 Self::BoxedFuture(_) => f.debug_struct("BoxedFuture").finish_non_exhaustive(),
192 Self::Ready(_) => f.debug_struct("Ready").finish_non_exhaustive(),
193 }
194 }
195}
196
197impl<Params, Resp, Output, Map> From<RpcCall<Params, Resp, Output, Map>>
198 for ProviderCall<Params, Resp, Output, Map>
199where
200 Params: RpcSend,
201 Resp: RpcRecv,
202 Map: Fn(Resp) -> Output,
203{
204 fn from(call: RpcCall<Params, Resp, Output, Map>) -> Self {
205 Self::RpcCall(call)
206 }
207}
208
209impl<Params, Resp> From<Waiter<Resp>> for ProviderCall<Params, Resp, Resp, fn(Resp) -> Resp>
210where
211 Params: RpcSend,
212 Resp: RpcRecv,
213{
214 fn from(waiter: Waiter<Resp>) -> Self {
215 Self::Waiter(waiter)
216 }
217}
218
219impl<Params, Resp, Output, Map> From<Pin<Box<dyn Future<Output = TransportResult<Output>> + Send>>>
220 for ProviderCall<Params, Resp, Output, Map>
221where
222 Params: RpcSend,
223 Resp: RpcRecv,
224 Map: Fn(Resp) -> Output,
225{
226 fn from(fut: Pin<Box<dyn Future<Output = TransportResult<Output>> + Send>>) -> Self {
227 Self::BoxedFuture(fut)
228 }
229}
230
231impl<Params, Resp> From<oneshot::Receiver<TransportResult<Box<RawValue>>>>
232 for ProviderCall<Params, Resp>
233where
234 Params: RpcSend,
235 Resp: RpcRecv,
236{
237 fn from(rx: oneshot::Receiver<TransportResult<Box<RawValue>>>) -> Self {
238 Waiter::from(rx).into()
239 }
240}
241
242impl<Params, Resp, Output, Map> Future for ProviderCall<Params, Resp, Output, Map>
243where
244 Params: RpcSend,
245 Resp: RpcRecv,
246 Output: 'static,
247 Map: Fn(Resp) -> Output,
248{
249 type Output = TransportResult<Output>;
250
251 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
252 match self.as_mut().project() {
253 ProviderCallProj::RpcCall(call) => call.poll_unpin(cx),
254 ProviderCallProj::Waiter(waiter) => waiter.poll_unpin(cx),
255 ProviderCallProj::BoxedFuture(fut) => fut.poll_unpin(cx),
256 ProviderCallProj::Ready(output) => {
257 Poll::Ready(output.take().expect("output taken twice"))
258 }
259 }
260 }
261}