use super::classify::{backoff_delay, is_retryable_message, is_retryable_status};
pub async fn send_with_retry<F, Fut>(mut f: F) -> anyhow::Result<(String, reqwest::StatusCode)>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = anyhow::Result<(String, reqwest::StatusCode)>>,
{
let mut attempt = 0u32;
loop {
attempt += 1;
match f().await {
Ok((text, status)) if status.is_success() && !is_retryable_message(&text) => {
return Ok((text, status));
}
Ok((text, status)) if is_retryable_status(status) || is_retryable_message(&text) => {
let delay = backoff_delay(attempt);
tracing::warn!(
attempt, %status,
delay_secs = delay.as_secs(),
"Transient API error, retrying"
);
tokio::time::sleep(delay).await;
}
Ok((text, status)) => {
anyhow::bail!("API error: {status} {text}");
}
Err(e) if is_retryable_message(&e.to_string()) => {
let delay = backoff_delay(attempt);
tracing::warn!(
attempt, error = %e,
delay_secs = delay.as_secs(),
"Transient network error, retrying"
);
tokio::time::sleep(delay).await;
}
Err(e) => return Err(e),
}
}
}