From 2246b06e9a426ca64627ef1bf05b41855c94e5e1 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 30 Aug 2024 01:34:20 +0800 Subject: [PATCH] refactor: Enable default features to avoid unexpected panic Signed-off-by: Xuanwo --- backon/Cargo.toml | 3 ++- backon/src/lib.rs | 11 +++++++---- backon/src/retry.rs | 5 +++++ backon/src/retry_with_context.rs | 5 +++++ backon/src/sleep.rs | 26 ++++++++++++++------------ 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/backon/Cargo.toml b/backon/Cargo.toml index b70cfd6..35ead2a 100644 --- a/backon/Cargo.toml +++ b/backon/Cargo.toml @@ -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 } diff --git a/backon/src/lib.rs b/backon/src/lib.rs index 926fe9d..517b9d6 100644 --- a/backon/src/lib.rs +++ b/backon/src/lib.rs @@ -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 //! @@ -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; diff --git a/backon/src/retry.rs b/backon/src/retry.rs index 25a9795..f81e533 100644 --- a/backon/src/retry.rs +++ b/backon/src/retry.rs @@ -263,6 +263,11 @@ where type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[cfg(debug_assertions)] + if std::any::TypeId::of::() == std::any::TypeId::of::() { + 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. // diff --git a/backon/src/retry_with_context.rs b/backon/src/retry_with_context.rs index 36a7375..acfd120 100644 --- a/backon/src/retry_with_context.rs +++ b/backon/src/retry_with_context.rs @@ -302,6 +302,11 @@ where type Output = (Ctx, Result); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + #[cfg(debug_assertions)] + if std::any::TypeId::of::() == std::any::TypeId::of::() { + 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. // diff --git a/backon/src/sleep.rs b/backon/src/sleep.rs index 1b4a926..2cfbf37 100644 --- a/backon/src/sleep.rs +++ b/backon/src/sleep.rs @@ -4,7 +4,7 @@ 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; @@ -12,32 +12,34 @@ pub trait Sleeper { 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 Fut, Fut: Future> Sleeper for F { +impl Fut + 'static, Fut: Future> Sleeper for F { type Sleep = Fut; fn sleep(&self, dur: Duration) -> Self::Sleep {