alloy_provider/provider/
prov_call.rs

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"))]
15/// Boxed future type used in [`ProviderCall`] for non-wasm targets.
16pub type BoxedFut<Output> = Pin<Box<dyn Future<Output = TransportResult<Output>> + Send>>;
17
18#[cfg(target_arch = "wasm32")]
19/// Boxed future type used in [`ProviderCall`] for wasm targets.
20pub type BoxedFut<Output> = Pin<Box<dyn Future<Output = TransportResult<Output>>>>;
21/// The primary future type for the [`Provider`].
22///
23/// This future abstracts over several potential data sources. It allows
24/// providers to:
25/// - produce data via an [`RpcCall`]
26/// - produce data by waiting on a batched RPC [`Waiter`]
27/// - proudce data via an arbitrary boxed future
28/// - produce data in any synchronous way
29///
30/// [`Provider`]: crate::Provider
31#[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    /// An underlying call to an RPC server.
39    RpcCall(RpcCall<Params, Resp, Output, Map>),
40    /// A waiter for a batched call to a remote RPC server.
41    Waiter(Waiter<Resp, Output, Map>),
42    /// A boxed future.
43    BoxedFuture(BoxedFut<Output>),
44    /// The output, produces synchronously.
45    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    /// Instantiate a new [`ProviderCall`] from the output.
55    pub const fn ready(output: TransportResult<Output>) -> Self {
56        Self::Ready(Some(output))
57    }
58
59    /// True if this is an RPC call.
60    pub const fn is_rpc_call(&self) -> bool {
61        matches!(self, Self::RpcCall(_))
62    }
63
64    /// Fallible cast to [`RpcCall`]
65    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    /// Fallible cast to mutable [`RpcCall`]
73    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    /// True if this is a waiter.
81    pub const fn is_waiter(&self) -> bool {
82        matches!(self, Self::Waiter(_))
83    }
84
85    /// Fallible cast to [`Waiter`]
86    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    /// Fallible cast to mutable [`Waiter`]
94    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    /// True if this is a boxed future.
102    pub const fn is_boxed_future(&self) -> bool {
103        matches!(self, Self::BoxedFuture(_))
104    }
105
106    /// Fallible cast to a boxed future.
107    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    /// True if this is a ready value.
115    pub const fn is_ready(&self) -> bool {
116        matches!(self, Self::Ready(_))
117    }
118
119    /// Fallible cast to a ready value.
120    ///
121    /// # Panics
122    ///
123    /// Panics if the future is already complete
124    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    /// Set a function to map the response into a different type. This is
133    /// useful for transforming the response into a more usable type, e.g.
134    /// changing `U64` to `u64`.
135    ///
136    /// This function fails if the inner future is not an [`RpcCall`] or
137    /// [`Waiter`].
138    ///
139    /// ## Note
140    ///
141    /// Carefully review the rust documentation on [fn pointers] before passing
142    /// them to this function. Unless the pointer is specifically coerced to a
143    /// `fn(_) -> _`, the `NewMap` will be inferred as that function's unique
144    /// type. This can lead to confusing error messages.
145    ///
146    /// [fn pointers]: https://doc.rust-lang.org/std/primitive.fn.html#creating-function-pointers
147    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    /// Convert this call into one with owned params, by cloning the params.
170    ///
171    /// # Panics
172    ///
173    /// Panics if called after the request has been polled.
174    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}