Skip to content

Commit

Permalink
refactor: Enable default features to avoid unexpected panic (#132)
Browse files Browse the repository at this point in the history
Most backon users are accustomed to having a default sleep
implementation, making it easy for them to upgrade to new versions like
`0.5` or `1.0` without the feature enabled. For example:
#128

Let's add default features to simplify their experience. Users who want
custom implementations will not be affected.

Signed-off-by: Xuanwo <[email protected]>
  • Loading branch information
Xuanwo authored Aug 29, 2024
1 parent 64fdcfd commit 749740a
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 17 deletions.
3 changes: 2 additions & 1 deletion backon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ targets = [
]

[features]
default = ["tokio-sleep", "gloo-timers-sleep"]
gloo-timers-sleep = ["dep:gloo-timers", "gloo-timers?/futures"]
tokio-sleep = ["dep:tokio", "tokio?/time"]

[dependencies]
fastrand = "2.0.0"
fastrand = "2"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", optional = true }
Expand Down
11 changes: 7 additions & 4 deletions backon/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@
//!
//! Retry in BackON requires an implementation for sleeping. BackON will accept a [`Sleeper`] to pause for a specified duration.
//!
//! BackON offers the following features for users to choose from:
//! BackON employs the following default sleep implementations:
//!
//! - `tokio-sleep`: Use [`TokioSleeper`] within a Tokio context.
//! - `gloo-timers-sleep`: Use [`GlooTimersSleep`] to pause in a wasm32 environment.
//! - `tokio-sleep`: Utilizes [`TokioSleeper`] within a Tokio context in non-wasm32 environments.
//! - `gloo-timers-sleep`: Utilizes [`GlooTimersSleep`] to pause in wasm32 environments.
//!
//! Users MUST provide a custom implementation if they prefer not to use the default options.
//! Users CAN provide a custom implementation if they prefer not to use the default options.
//!
//! If neither feature is enabled nor a custom implementation is provided, BackON will fallback to an empty sleeper. This will cause a panic in the `debug` profile and do nothing in the `release` profile.
//!
//! # Retry
//!
Expand Down Expand Up @@ -123,6 +125,7 @@ mod sleep;
pub use sleep::DefaultSleeper;
#[cfg(all(target_arch = "wasm32", feature = "gloo-timers-sleep"))]
pub use sleep::GlooTimersSleep;
pub(crate) use sleep::NoopSleeper;
pub use sleep::Sleeper;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio-sleep"))]
pub use sleep::TokioSleeper;
Expand Down
5 changes: 5 additions & 0 deletions backon/src/retry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ where
type Output = Result<T, E>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
#[cfg(debug_assertions)]
if std::any::TypeId::of::<SF>() == std::any::TypeId::of::<crate::NoopSleeper>() {
panic!("BackON: No sleeper has been configured. Please enable the features or provide a custom implementation.")
}

// Safety: This is safe because we don't move the `Retry` struct itself,
// only its internal state.
//
Expand Down
5 changes: 5 additions & 0 deletions backon/src/retry_with_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ where
type Output = (Ctx, Result<T, E>);

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
#[cfg(debug_assertions)]
if std::any::TypeId::of::<SF>() == std::any::TypeId::of::<crate::NoopSleeper>() {
panic!("BackON: No sleeper has been configured. Please enable the features or provide a custom implementation.")
}

// Safety: This is safe because we don't move the `Retry` struct itself,
// only its internal state.
//
Expand Down
26 changes: 14 additions & 12 deletions backon/src/sleep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,42 @@ use std::{
};

/// A sleeper is used to generate a future that completes after a specified duration.
pub trait Sleeper {
pub trait Sleeper: 'static {
/// The future returned by the `sleep` method.
type Sleep: Future<Output = ()>;

/// Create a future that completes after a set period.
fn sleep(&self, dur: Duration) -> Self::Sleep;
}

/// The default implementation of `Sleeper`.
/// The default implementation of `Sleeper` is a no-op when no features are enabled.
///
/// - Under `tokio-sleep` feature, it uses `tokio::time::sleep`.
/// - Under `gloo-timers-sleep` feature, it uses `gloo_timers::sleep::sleep`.
/// It will panic on `debug` profile and do nothing on `release` profile.
#[cfg(all(not(feature = "tokio-sleep"), not(feature = "gloo-timers-sleep")))]
pub type DefaultSleeper = ();
/// The default implementation of `Sleeper` based on enabled feature flag.
pub type DefaultSleeper = NoopSleeper;
/// The default implementation of `Sleeper` while feature `tokio-sleep` enabled.
///
/// Under `tokio-sleep` feature, it uses `tokio::time::sleep`.
/// it uses `tokio::time::sleep`.
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio-sleep"))]
pub type DefaultSleeper = TokioSleeper;
/// The default implementation of `Sleeper` based on enabled feature flag.
/// The default implementation of `Sleeper` while feature `gloo-timers-sleep` enabled.
///
/// Under `gloo-timers-sleep` feature, it uses `gloo_timers::sleep::sleep`.
/// It uses `gloo_timers::sleep::sleep`.
#[cfg(all(target_arch = "wasm32", feature = "gloo-timers-sleep"))]
pub type DefaultSleeper = GlooTimersSleep;

impl Sleeper for () {
/// The no-op implementation of `Sleeper` that does nothing.
pub struct NoopSleeper;

impl Sleeper for NoopSleeper {
type Sleep = Ready<()>;

fn sleep(&self, _: Duration) -> Self::Sleep {
panic!("no sleeper has been configured, consider enabling features or provide a custom implementation")
std::future::ready(())
}
}

impl<F: Fn(Duration) -> Fut, Fut: Future<Output = ()>> Sleeper for F {
impl<F: Fn(Duration) -> Fut + 'static, Fut: Future<Output = ()>> Sleeper for F {
type Sleep = Fut;

fn sleep(&self, dur: Duration) -> Self::Sleep {
Expand Down

0 comments on commit 749740a

Please sign in to comment.