Skip to content

Commit

Permalink
Merge pull request #9 from alloy-rs/prestwich/wasm-compat
Browse files Browse the repository at this point in the history
Wasm!
  • Loading branch information
prestwich authored Sep 5, 2023
2 parents e56e56e + b5eb3c1 commit f8bb1a9
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 91 deletions.
24 changes: 12 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ jobs:
- name: test
run: cargo test --workspace ${{ matrix.flags }}

# wasm:
# name: check WASM
# runs-on: ubuntu-latest
# timeout-minutes: 30
# steps:
# - uses: actions/checkout@v3
# - uses: dtolnay/rust-toolchain@stable
# with:
# targets: wasm32-unknown-unknown
# - uses: Swatinem/rust-cache@v2
# - name: check
# run: cargo check --workspace --target wasm32-unknown-unknown
wasm:
name: check WASM
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- name: check
run: cargo check --workspace --target wasm32-unknown-unknown

feature-checks:
name: feature checks
Expand Down
6 changes: 4 additions & 2 deletions crates/providers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ where
}
}

#[async_trait::async_trait]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
/// Provider is parameterized with a network and a transport. The default
/// transport is type-erased, but you can do `Provider<N, Http>`.
pub trait Provider<N: Network, T: Transport = BoxTransport>: Send + Sync {
Expand Down Expand Up @@ -105,7 +106,8 @@ pub trait Provider<N: Network, T: Transport = BoxTransport>: Send + Sync {
}
}

#[async_trait::async_trait]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
impl<N: Network, T: Transport + Clone> Provider<N, T> for NetworkRpcClient<N, T> {
fn client(&self) -> &NetworkRpcClient<N, T> {
self
Expand Down
6 changes: 5 additions & 1 deletion crates/transports/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ pin-project.workspace = true

# feature deps
reqwest = { version = "0.11.18", features = ["serde_json", "json"], optional = true }
hyper = { version = "0.14.27", optional = true, features = ["full"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies.hyper]
version = "0.14.27"
optional = true
features = ["full"]

[features]
default = ["reqwest", "hyper"]
Expand Down
2 changes: 1 addition & 1 deletion crates/transports/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ let balance = balance_fut.await.unwrap();
### Features

- `reqwest`: Enables the `reqwest` transport implementation.
- `hyper`: Enables the `hyper` transport implementation.
- `hyper`: Enables the `hyper` transport implementation (not available in WASM).
8 changes: 5 additions & 3 deletions crates/transports/src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@ type ChannelMap = HashMap<Id, Channel>;
/// call.
#[derive(Debug)]
pub struct BatchRequest<'a, T> {
/// The transport via which the batch will be sent.
transport: &'a RpcClient<T>,

/// The requests to be sent.
requests: Vec<Box<RawValue>>,

/// The channels to send the responses through.
channels: ChannelMap,
}

/// Awaits a single response for a request that has been included in a batch.
#[must_use = "A Waiter does nothing unless the corresponding BatchRequest is sent via `send_batch` and `.await`, AND the Waiter is awaited."]
pub struct Waiter<Resp> {
rx: oneshot::Receiver<RpcResult<Box<RawValue>, TransportError>>,
_resp: PhantomData<Resp>,
Expand Down Expand Up @@ -62,8 +66,7 @@ where
#[pin_project::pin_project(project = CallStateProj)]
pub enum BatchFuture<Conn>
where
Conn: Transport + Clone,
Conn::Future: Send,
Conn: Transport,
{
Prepared {
transport: Conn,
Expand Down Expand Up @@ -250,7 +253,6 @@ where
impl<T> Future for BatchFuture<T>
where
T: Transport + Clone,
T::Future: Send,
{
type Output = Result<(), TransportError>;

Expand Down
12 changes: 2 additions & 10 deletions crates/transports/src/call.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
error::TransportError,
transports::{JsonRpcLayer, JsonRpcService, Transport},
RpcFut,
};

use alloy_json_rpc::{Request, RpcParam, RpcResult, RpcReturn};
Expand All @@ -15,7 +16,6 @@ use tower::{Layer, Service};
enum CallState<Params, Conn>
where
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
Prepared {
Expand All @@ -32,7 +32,6 @@ where
impl<Params, Conn> CallState<Params, Conn>
where
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
fn poll_prepared(
Expand Down Expand Up @@ -79,7 +78,6 @@ where
impl<Params, Conn> Future for CallState<Params, Conn>
where
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
type Output = RpcResult<Box<RawValue>, TransportError>;
Expand Down Expand Up @@ -120,7 +118,6 @@ where
pub struct RpcCall<Conn, Params, Resp>
where
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
#[pin]
Expand All @@ -131,7 +128,6 @@ where
impl<Conn, Params, Resp> RpcCall<Conn, Params, Resp>
where
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
{
#[doc(hidden)]
Expand Down Expand Up @@ -164,22 +160,18 @@ where
impl<'a, Conn, Params, Resp> RpcCall<Conn, Params, Resp>
where
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam + 'a,
Resp: RpcReturn,
{
/// Convert this future into a boxed, pinned future, erasing its type.
pub fn boxed(
self,
) -> Pin<Box<dyn Future<Output = RpcResult<Resp, TransportError>> + Send + 'a>> {
pub fn boxed(self) -> RpcFut<'a, Resp> {
Box::pin(self)
}
}

impl<Conn, Params, Resp> Future for RpcCall<Conn, Params, Resp>
where
Conn: Transport + Clone,
Conn::Future: Send,
Params: RpcParam,
Resp: RpcReturn,
{
Expand Down
68 changes: 41 additions & 27 deletions crates/transports/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use alloy_json_rpc::{Id, Request, RpcParam, RpcReturn};
use serde_json::value::RawValue;
use tower::{layer::util::Stack, Layer, ServiceBuilder};
use tower::{
layer::util::{Identity, Stack},
Layer, ServiceBuilder,
};

use std::{
borrow::Cow,
Expand Down Expand Up @@ -31,6 +33,14 @@ pub struct RpcClient<T> {
pub(crate) id: AtomicU64,
}

impl RpcClient<Identity> {
pub fn builder() -> ClientBuilder<Identity> {
ClientBuilder {
builder: ServiceBuilder::new(),
}
}
}

impl<T> RpcClient<T> {
/// Create a new [`RpcClient`] with the given transport.
pub fn new(t: T, is_local: bool) -> Self {
Expand All @@ -41,6 +51,23 @@ impl<T> RpcClient<T> {
}
}

/// Build a `JsonRpcRequest` with the given method and params.
///
/// This function reserves an ID for the request, however the request
/// is not sent. To send a request, use [`RpcClient::prepare`] and await
/// the returned [`RpcCall`].
pub fn make_request<'a, Params: RpcParam>(
&self,
method: &'static str,
params: Cow<'a, Params>,
) -> Request<Cow<'a, Params>> {
Request {
method,
params,
id: self.next_id(),
}
}

/// `true` if the client believes the transport is local.
///
/// This can be used to optimize remote API usage, or to change program
Expand Down Expand Up @@ -73,31 +100,13 @@ impl<T> RpcClient<T> {
impl<T> RpcClient<T>
where
T: Transport + Clone,
T::Future: Send,
{
/// Create a new [`BatchRequest`] builder.
#[inline]
pub fn new_batch(&self) -> BatchRequest<T> {
BatchRequest::new(self)
}

/// Build a `JsonRpcRequest` with the given method and params.
///
/// This function reserves an ID for the request, however the request
/// is not sent. To send a request, use [`RpcClient::prepare`] and await
/// the returned [`RpcCall`].
pub fn make_request<'a, Params: RpcParam>(
&self,
method: &'static str,
params: Cow<'a, Params>,
) -> Request<Cow<'a, Params>> {
Request {
method,
params,
id: self.next_id(),
}
}

/// Prepare an [`RpcCall`].
///
/// This function reserves an ID for the request, however the request
Expand Down Expand Up @@ -144,6 +153,14 @@ pub struct ClientBuilder<L> {
builder: ServiceBuilder<L>,
}

impl Default for ClientBuilder<Identity> {
fn default() -> Self {
Self {
builder: ServiceBuilder::new(),
}
}
}

impl<L> ClientBuilder<L> {
/// Add a middleware layer to the stack.
///
Expand All @@ -162,37 +179,34 @@ impl<L> ClientBuilder<L> {
L: Layer<T>,
T: Transport,
L::Service: Transport,
<L::Service as tower::Service<Box<RawValue>>>::Future: Send,
{
RpcClient::new(self.builder.service(transport), is_local)
}

#[cfg(feature = "reqwest")]
/// Create a new [`RpcClient`] with a [`reqwest`] HTTP transport connecting
/// to the given URL and the configured layers.
#[cfg(feature = "reqwest")]
pub fn reqwest_http(self, url: reqwest::Url) -> RpcClient<L::Service>
where
L: Layer<Http<reqwest::Client>>,
L::Service: Transport,
<L::Service as tower::Service<Box<RawValue>>>::Future: Send,
{
let transport = Http::new(url);
let is_local = transport.is_local();
let is_local = transport.guess_local();

self.transport(transport, is_local)
}

#[cfg(feature = "hyper")]
/// Create a new [`RpcClient`] with a [`hyper`] HTTP transport connecting
/// to the given URL and the configured layers.
#[cfg(all(not(target_arch = "wasm32"), feature = "hyper"))]
pub fn hyper_http(self, url: url::Url) -> RpcClient<L::Service>
where
L: Layer<Http<hyper::client::Client<hyper::client::HttpConnector>>>,
L::Service: Transport,
<L::Service as tower::Service<Box<RawValue>>>::Future: Send,
{
let transport = Http::new(url);
let is_local = transport.is_local();
let is_local = transport.guess_local();

self.transport(transport, is_local)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/transports/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub enum TransportError {

/// Hyper http transport
#[error(transparent)]
#[cfg(feature = "hyper")]
#[cfg(all(not(target_arch = "wasm32"), feature = "hyper"))]
Hyper(#[from] hyper::Error),
}

Expand Down
32 changes: 32 additions & 0 deletions crates/transports/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,35 @@ pub use transports::{BoxTransport, Http, Transport};
pub use alloy_json_rpc::RpcResult;

pub(crate) mod utils;

pub use type_aliases::*;

#[cfg(not(target_arch = "wasm32"))]
mod type_aliases {
use alloy_json_rpc::RpcResult;

use crate::TransportError;

/// Future for Transport-level requests.
pub type TransportFut<'a, T = Box<serde_json::value::RawValue>, E = TransportError> =
std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, E>> + Send + 'a>>;

/// Future for RPC-level requests.
pub type RpcFut<'a, T, E = TransportError> =
std::pin::Pin<Box<dyn std::future::Future<Output = RpcResult<T, E>> + Send + 'a>>;
}

#[cfg(target_arch = "wasm32")]
mod type_aliases {
use alloy_json_rpc::RpcResult;

use crate::TransportError;

/// Future for Transport-level requests.
pub type TransportFut<'a, T = Box<serde_json::value::RawValue>, E = TransportError> =
std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, E>> + 'a>>;

/// Future for RPC-level requests.
pub type RpcFut<'a, T, E = TransportError> =
std::pin::Pin<Box<dyn std::future::Future<Output = RpcResult<T, E>> + 'a>>;
}
Loading

0 comments on commit f8bb1a9

Please sign in to comment.