Skip to content

Commit

Permalink
Make sleep into a trait
Browse files Browse the repository at this point in the history
  • Loading branch information
praveenperera committed Oct 15, 2024
1 parent fdc7aed commit f74ff21
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 17 deletions.
18 changes: 13 additions & 5 deletions src/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//! Esplora by way of `reqwest` HTTP client.

use std::collections::HashMap;
use std::marker::PhantomData;
use std::str::FromStr;

use bitcoin::consensus::{deserialize, serialize, Decodable, Encodable};
Expand All @@ -26,24 +27,30 @@ use log::{debug, error, info, trace};

use reqwest::{header, Client, Response};

use crate::runtime::{DefaultRuntime, Runtime};
use crate::{
BlockStatus, BlockSummary, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus,
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
};

#[derive(Debug, Clone)]
pub struct AsyncClient {
pub struct AsyncClient<R: Runtime = DefaultRuntime> {
/// The URL of the Esplora Server.
url: String,
/// The inner [`reqwest::Client`] to make HTTP requests.
client: Client,
/// Number of times to retry a request
max_retries: usize,

runtime: PhantomData<R>,
}

impl AsyncClient {
impl<R> AsyncClient<R>
where
R: Runtime,
{
/// Build an async client from a builder
pub fn from_builder(builder: Builder) -> Result<Self, Error> {
pub fn from_builder(builder: Builder<R>) -> Result<Self, Error> {
let mut client_builder = Client::builder();

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -72,15 +79,16 @@ impl AsyncClient {
url: builder.base_url,
client: client_builder.build()?,
max_retries: builder.max_retries,
runtime: PhantomData,
})
}

/// Build an async client from the base url and [`Client`]
pub fn from_client(url: String, client: Client) -> Self {
AsyncClient {
url,
client,
max_retries: crate::DEFAULT_MAX_RETRIES,
runtime: PhantomData,
}
}

Expand Down Expand Up @@ -433,7 +441,7 @@ impl AsyncClient {
loop {
match self.client.get(url).send().await? {
resp if attempts < self.max_retries && is_status_retryable(resp.status()) => {
crate::runtime::sleep(delay).await;
R::sleep(delay).await;
attempts += 1;
delay *= 2;
}
Expand Down
38 changes: 30 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub use api::*;
pub use blocking::BlockingClient;
#[cfg(feature = "async")]
pub use r#async::AsyncClient;
use runtime::{DefaultRuntime, Runtime};

/// Response status codes for which the request may be retried.
const RETRYABLE_ERROR_CODES: [u16; 3] = [
Expand Down Expand Up @@ -113,7 +114,7 @@ pub fn convert_fee_rate(target: usize, estimates: HashMap<u16, f64>) -> Option<f
}

#[derive(Debug, Clone)]
pub struct Builder {
pub struct Builder<R: Runtime = DefaultRuntime> {
/// The URL of the Esplora server.
pub base_url: String,
/// Optional URL of the proxy to use to make requests to the Esplora server
Expand All @@ -135,6 +136,8 @@ pub struct Builder {
pub headers: HashMap<String, String>,
/// Max retries
pub max_retries: usize,
/// Async runtime, trait must implement `sleep` function, default is `tokio`
pub runtime: R,
}

impl Builder {
Expand All @@ -146,6 +149,31 @@ impl Builder {
timeout: None,
headers: HashMap::new(),
max_retries: DEFAULT_MAX_RETRIES,
runtime: DefaultRuntime,
}
}

/// Build a blocking client from builder
#[cfg(feature = "blocking")]
pub fn build_blocking(self) -> BlockingClient {
BlockingClient::from_builder(self)
}
}

impl<R: Runtime> Builder<R>
where
R: Runtime,
{
/// New with runtime
#[cfg(feature = "async")]
pub fn new_with_runtime(base_url: &str, runtime: R) -> Self {
Builder {
base_url: base_url.to_string(),
proxy: None,
timeout: None,
headers: HashMap::new(),
max_retries: DEFAULT_MAX_RETRIES,
runtime,
}
}

Expand Down Expand Up @@ -174,15 +202,9 @@ impl Builder {
self
}

/// Build a blocking client from builder
#[cfg(feature = "blocking")]
pub fn build_blocking(self) -> BlockingClient {
BlockingClient::from_builder(self)
}

// Build an asynchronous client from builder
#[cfg(feature = "async")]
pub fn build_async(self) -> Result<AsyncClient, Error> {
pub fn build_async(self) -> Result<AsyncClient<R>, Error> {
AsyncClient::from_builder(self)
}
}
Expand Down
25 changes: 21 additions & 4 deletions src/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
#[cfg(feature = "tokio")]
pub use tokio::time::sleep;
use std::time::Duration;

pub trait Runtime {
fn sleep(duration: Duration) -> impl std::future::Future<Output = ()> + Send;
}

pub struct DefaultRuntime;

#[cfg(feature = "tokio")]
impl Runtime for DefaultRuntime {
async fn sleep(duration: Duration) {
tokio::time::sleep(duration).await;
}
}

#[cfg(feature = "async-std")]
pub use async_std::task::sleep;
pub struct AsyncStd;

#[cfg(not(any(feature = "tokio", feature = "async-std")))]
compile_error!("Either 'tokio' or 'async-std' feature must be enabled");
#[cfg(feature = "async-std")]
impl Runtime for AsyncStd {
async fn sleep(duration: Duration) {
async_std::task::sleep(duration).await;
}
}

0 comments on commit f74ff21

Please sign in to comment.