tgin 0.1.0

Universal traffic glue: a Rust construction kit for gateways — HTTP, Telegram bots, RabbitMQ, Kafka, WebSocket tunnels
Documentation
use std::time::Duration;

use async_trait::async_trait;

use crate::base::{Egress, Meta, Runnable, SendError};

#[derive(Clone)]
pub struct Retry<E> {
    inner: E,
    attempts: usize,
    backoff: Duration,
}

impl<E> Retry<E> {
    pub fn new(inner: E) -> Self {
        Self {
            inner,
            attempts: 3,
            backoff: Duration::from_millis(100),
        }
    }

    pub fn attempts(mut self, attempts: usize) -> Self {
        self.attempts = attempts.max(1);
        self
    }

    pub fn backoff(mut self, backoff: Duration) -> Self {
        self.backoff = backoff;
        self
    }
}

#[async_trait]
impl<E, I> Egress<I> for Retry<E>
where
    E: Egress<I>,
    I: Clone + Send + Sync + 'static,
{
    type Output = E::Output;

    fn services(&self) -> Vec<Box<dyn Runnable>> {
        self.inner.services()
    }

    async fn setup(&mut self) {
        self.inner.setup().await;
    }

    async fn send(&self, input: I, meta: &Meta) -> Result<Self::Output, SendError> {
        let mut delay = self.backoff;

        for _ in 1..self.attempts {
            match self.inner.send(input.clone(), meta).await {
                Err(error) if error.is_retryable() => {
                    tokio::time::sleep(delay).await;
                    delay *= 2;
                }
                result => return result,
            }
        }

        self.inner.send(input, meta).await
    }

    async fn stop(&self) {
        self.inner.stop().await;
    }
}