use std::num::NonZeroU32;
use std::ops::ControlFlow;
use std::time::Duration;
use grammers_mtsender::{InvocationError, RpcError};
pub trait RetryPolicy: Send + Sync {
fn should_retry(&self, ctx: &RetryContext) -> ControlFlow<(), Duration>;
}
pub struct RetryContext {
pub fail_count: NonZeroU32,
pub slept_so_far: Duration,
pub error: InvocationError,
}
pub struct NoRetries;
impl RetryPolicy for NoRetries {
fn should_retry(&self, _: &RetryContext) -> ControlFlow<(), Duration> {
ControlFlow::Break(())
}
}
pub struct AutoSleep {
pub threshold: Duration,
pub io_errors_as_flood_of: Option<Duration>,
}
impl RetryPolicy for AutoSleep {
fn should_retry(&self, ctx: &RetryContext) -> ControlFlow<(), Duration> {
match ctx.error {
InvocationError::Rpc(RpcError {
code: 420,
value: Some(seconds),
..
}) if ctx.fail_count.get() == 1 && seconds as u64 <= self.threshold.as_secs() => {
ControlFlow::Continue(Duration::from_secs(seconds as _))
}
InvocationError::Io(_) if ctx.fail_count.get() == 1 => {
if let Some(duration) = self.io_errors_as_flood_of {
ControlFlow::Continue(duration)
} else {
ControlFlow::Break(())
}
}
_ => ControlFlow::Break(()),
}
}
}
impl Default for AutoSleep {
fn default() -> Self {
Self {
threshold: Duration::from_secs(60),
io_errors_as_flood_of: Some(Duration::from_secs(1)),
}
}
}