use std::time::Duration;
use crate::error::GuixError;
use crate::operation::Operation;
#[derive(Debug, Clone, Default)]
pub struct RetryPolicy {
pub backoffs: Vec<Duration>,
}
impl RetryPolicy {
pub fn none() -> Self {
Self {
backoffs: Vec::new(),
}
}
pub fn installer_default() -> Self {
Self {
backoffs: vec![
Duration::ZERO,
Duration::from_secs(60),
Duration::from_secs(300),
],
}
}
pub fn from_secs(secs: &[u64]) -> Self {
Self {
backoffs: secs.iter().copied().map(Duration::from_secs).collect(),
}
}
}
pub async fn run_with_retry<F, Fut>(policy: &RetryPolicy, mut factory: F) -> Result<(), GuixError>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = Result<Operation, GuixError>>,
{
let mut attempt = 0usize;
loop {
let result = match factory().await {
Ok(op) => op.await_completion().await,
Err(e) => Err(e),
};
match result {
Ok(()) => return Ok(()),
Err(e) => {
if attempt < policy.backoffs.len() && e.is_transient() {
let delay = policy.backoffs[attempt];
if !delay.is_zero() {
tokio::time::sleep(delay).await;
}
attempt += 1;
continue;
}
return Err(e);
}
}
}
}