| Crates.io | retryx |
| lib.rs | retryx |
| version | 1.0.0 |
| created_at | 2025-12-17 09:36:05.122584+00 |
| updated_at | 2025-12-17 09:36:05.122584+00 |
| description | Async-first retry and exponential backoff helper for Tokio-based Rust applications. |
| homepage | https://github.com/shahnoorgit/retryx |
| repository | https://github.com/shahnoorgit/retryx |
| max_upload_size | |
| id | 1989832 |
| size | 19,299 |
Small, async-first retry & backoff helper for Tokio-based Rust services.
retryx gives you a fluent builder API to retry any async operation with fixed or exponential backoff, optional per-attempt timeouts, custom retry filters, and an on_retry hook for logging or metrics.
async/await and tokio.Fn() -> Future<Output = Result<T, E>>.tokio::time::timeout.on_retry hook: Integrate logging and metrics.Add this to your Cargo.toml:
[dependencies]
retryx = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
use retryx::retry;
use std::time::Duration;
#[derive(Debug, Clone)]
struct ApiError;
impl std::fmt::Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "api error")
}
}
impl std::error::Error for ApiError {}
impl From<tokio::time::error::Elapsed> for ApiError {
fn from(_: tokio::time::error::Elapsed) -> Self {
ApiError
}
}
async fn call_api() -> Result<String, ApiError> {
// Your real API call goes here
Ok("ok".to_string())
}
#[tokio::main]
async fn main() -> Result<(), ApiError> {
let value = retry::<ApiError>()
.times(3)
.exponential()
.timeout(Duration::from_secs(2))
.run(|| async {
call_api().await
})
.await?;
println!("Got value: {value}");
Ok(())
}
on_retry hookuse retryx::retry;
use std::time::Duration;
#[derive(Debug, Clone)]
enum ApiError {
Timeout,
RateLimited,
Fatal,
}
impl std::fmt::Display for ApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ApiError::Timeout => write!(f, "timeout"),
ApiError::RateLimited => write!(f, "rate limited"),
ApiError::Fatal => write!(f, "fatal error"),
}
}
}
impl std::error::Error for ApiError {}
impl From<tokio::time::error::Elapsed> for ApiError {
fn from(_: tokio::time::error::Elapsed) -> Self {
ApiError::Timeout
}
}
async fn call_api() -> Result<String, ApiError> {
// Your real API call goes here
Err(ApiError::Timeout)
}
#[tokio::main]
async fn main() -> Result<(), ApiError> {
let result = retry::<ApiError>()
.times(5)
.fixed(Duration::from_millis(200))
.when(|err| matches!(err, ApiError::Timeout | ApiError::RateLimited))
.on_retry(|attempt, err, delay| {
println!("retry {attempt}: {err}, next in {delay:?}");
})
.run(|| async {
call_api().await
})
.await;
println!("Final result: {:?}", result);
Ok(())
}