Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wasm! #9

Merged
merged 8 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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