babelforce-manager-sdk 0.42.3

Rust SDK for the babelforce manager APIs — auth, user & agent management, call reporting, metrics, and task automations.
Documentation
use std::time::Duration;

/// Automatic-retry tuning. Transient failures — network errors and 429/502/503/504 — are retried
/// with exponential backoff. Non-idempotent requests are only retried on 429.
#[derive(Clone, Debug)]
pub struct RetryPolicy {
    /// Retry attempts after the initial request.
    pub max_retries: u32,
    /// Base backoff; grows exponentially per attempt.
    pub base_delay: Duration,
    /// Upper bound on any single backoff.
    pub max_delay: Duration,
}

impl Default for RetryPolicy {
    fn default() -> Self {
        Self {
            max_retries: 2,
            base_delay: Duration::from_millis(250),
            max_delay: Duration::from_secs(10),
        }
    }
}

impl RetryPolicy {
    /// A policy that performs no retries.
    pub fn disabled() -> Self {
        Self {
            max_retries: 0,
            ..Self::default()
        }
    }
}

/// Implemented (per generated module, in `error.rs`) for each module's `Error<T>` so the retry loop
/// can decide whether a failure is worth retrying.
pub(crate) trait Transient {
    fn transient(&self, idempotent: bool) -> bool;
}

/// Invoke `f` with retries per `policy`. `f` is re-invoked (rebuilding the request) on transient
/// failure. `idempotent` gates whether network/5xx errors are retried (429 always is).
pub(crate) async fn with_retry<F, Fut, R, E>(
    policy: &RetryPolicy,
    idempotent: bool,
    mut f: F,
) -> Result<R, E>
where
    F: FnMut() -> Fut,
    Fut: std::future::Future<Output = Result<R, E>>,
    E: Transient,
{
    let mut attempt: u32 = 0;
    loop {
        match f().await {
            Ok(r) => return Ok(r),
            Err(e) => {
                if attempt >= policy.max_retries || !e.transient(idempotent) {
                    return Err(e);
                }
                let factor = 1u32.checked_shl(attempt.min(16)).unwrap_or(u32::MAX);
                let delay = policy
                    .base_delay
                    .saturating_mul(factor)
                    .min(policy.max_delay);
                tokio::time::sleep(delay).await;
                attempt += 1;
            }
        }
    }
}