feat: add basic retry handling

This commit is contained in:
2026-02-03 19:42:55 +01:00
parent c5feb88d5c
commit b7973dc18d
4 changed files with 35 additions and 4 deletions

7
Cargo.lock generated
View File

@@ -32,6 +32,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.2.6" version = "0.2.6"
@@ -79,6 +85,7 @@ name = "noretry"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"either",
"thiserror", "thiserror",
"tokio", "tokio",
"tracing", "tracing",

View File

@@ -5,6 +5,7 @@ edition = "2024"
[dependencies] [dependencies]
anyhow.workspace = true anyhow.workspace = true
either = "1.15.0"
thiserror = "2.0.18" thiserror = "2.0.18"
tokio.workspace = true tokio.workspace = true
tracing.workspace = true tracing.workspace = true

View File

@@ -1,3 +1,7 @@
use std::fmt::Display;
use either::Either;
pub fn builder() -> RetryBuilder { pub fn builder() -> RetryBuilder {
RetryBuilder { RetryBuilder {
max_attempts: Some(3), max_attempts: Some(3),
@@ -48,16 +52,22 @@ impl RetryOptions {
pub async fn run<T, TFut, TOk, TError>( pub async fn run<T, TFut, TOk, TError>(
mut self, mut self,
f: T, f: T,
) -> Result<Result<TOk, TError>, RetryError> ) -> Result<TOk, Either<TError, RetryError>>
where where
T: Fn() -> TFut + Send + 'static, T: Fn() -> TFut + Send + 'static,
TFut: Future<Output = Result<TOk, TError>>, TFut: Future<Output = Result<TOk, TError>>,
{ {
let res = f().await; match f().await {
Ok(success) => return Ok(success),
Err(e) => return Err(either::Left(e)),
};
Ok(res) Err(either::Right(RetryError::ExhaustedAttempts))
} }
} }
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum RetryError {} pub enum RetryError {
#[error("retry stopped because attempts exceeded budget")]
ExhaustedAttempts,
}

View File

@@ -7,8 +7,21 @@ async fn test_can_call() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[tokio::test]
async fn test_can_fail() -> anyhow::Result<()> {
let res = noretry::builder().build().run(fail).await?;
assert_eq!("output", res.as_str());
Ok(())
}
async fn my_func() -> anyhow::Result<String> { async fn my_func() -> anyhow::Result<String> {
println!("my func was called"); println!("my func was called");
Ok("output".into()) Ok("output".into())
} }
async fn fail() -> anyhow::Result<String> {
anyhow::bail!("fails")
}