use std::time::Duration; use backon::{ExponentialBuilder, Retryable}; use forge_config::RetryConfig; use forge_domain::Error; pub async fn retry_with_config( config: &RetryConfig, operation: F, notify: Option, ) -> anyhow::Result where F: Fn() -> Fut, Fut: std::future::Future>, C: Fn(&anyhow::Error, Duration) + Send + Sync + 'static, { let strategy = ExponentialBuilder::default() .with_min_delay(Duration::from_millis(config.min_delay_ms)) .with_factor(config.backoff_factor as f32) .with_max_times(config.max_attempts) .with_jitter(); let retryable = operation.retry(&strategy).when(should_retry); match notify { Some(callback) => retryable.notify(callback).await, None => retryable.await, } } /// Determines if an error should trigger a retry attempt. /// /// This function checks if the error is a retryable domain error. /// Currently, only `Error::Retryable` errors will trigger retries. fn should_retry(error: &anyhow::Error) -> bool { error .downcast_ref::() .is_some_and(|error| matches!(error, Error::Retryable(_))) }