im-core 0.1.0

Rust IM SDK for Awiki clients built on Agent Network Protocol (ANP)
Documentation
use std::time::Duration;

pub const SESSION_RECONNECT_BASE_DELAY: Duration = Duration::from_secs(1);
pub const SESSION_RECONNECT_MAX_DELAY: Duration = Duration::from_secs(30);

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContextSleep {
    Completed,
    Cancelled,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SessionLoopRetryPhase {
    ConnectFailure,
    ConsumeFailure,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SessionLoopRetryDecision {
    Retry {
        phase: SessionLoopRetryPhase,
        sleep_delay: Duration,
        next_delay: Duration,
    },
    ExitAfterCancelledSleep {
        phase: SessionLoopRetryPhase,
        sleep_delay: Duration,
        next_delay: Duration,
    },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConsumeFinishedDecision {
    ExitAfterSessionCancelled { next_delay: Duration },
    Sleep(SessionLoopRetryDecision),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SessionLoopBackoff {
    next_delay: Duration,
}

impl Default for SessionLoopBackoff {
    fn default() -> Self {
        Self::new()
    }
}

impl SessionLoopBackoff {
    pub fn new() -> Self {
        Self {
            next_delay: SESSION_RECONNECT_BASE_DELAY,
        }
    }

    pub fn next_delay(&self) -> Duration {
        self.next_delay
    }

    pub fn connected(&mut self) -> Duration {
        self.next_delay = SESSION_RECONNECT_BASE_DELAY;
        self.next_delay
    }

    pub fn connect_failed(&mut self, sleep: ContextSleep) -> SessionLoopRetryDecision {
        self.retry_after_failure(SessionLoopRetryPhase::ConnectFailure, sleep)
    }

    pub fn consume_finished(
        &mut self,
        session_cancelled: bool,
        sleep: ContextSleep,
    ) -> ConsumeFinishedDecision {
        if session_cancelled {
            return ConsumeFinishedDecision::ExitAfterSessionCancelled {
                next_delay: self.next_delay,
            };
        }
        ConsumeFinishedDecision::Sleep(
            self.retry_after_failure(SessionLoopRetryPhase::ConsumeFailure, sleep),
        )
    }

    fn retry_after_failure(
        &mut self,
        phase: SessionLoopRetryPhase,
        sleep: ContextSleep,
    ) -> SessionLoopRetryDecision {
        let sleep_delay = self.next_delay;
        match sleep {
            ContextSleep::Completed => {
                self.next_delay = capped_double_delay(self.next_delay);
                SessionLoopRetryDecision::Retry {
                    phase,
                    sleep_delay,
                    next_delay: self.next_delay,
                }
            }
            ContextSleep::Cancelled => SessionLoopRetryDecision::ExitAfterCancelledSleep {
                phase,
                sleep_delay,
                next_delay: self.next_delay,
            },
        }
    }
}

fn capped_double_delay(delay: Duration) -> Duration {
    let doubled = delay.saturating_mul(2);
    if doubled > SESSION_RECONNECT_MAX_DELAY {
        SESSION_RECONNECT_MAX_DELAY
    } else {
        doubled
    }
}