use std::{fmt, rc::Weak, time::Duration};
use derive_more::{Display, From};
use tracerr::Traced;
use crate::{
platform,
rpc::{BackoffDelayer, RpcSession, SessionError},
utils::Caused,
};
#[derive(Caused, Clone, Debug, From, Display)]
#[cause(error = platform::Error)]
pub enum ReconnectError {
Session(#[cause] SessionError),
#[display(fmt = "ReconnectHandle is in detached state")]
Detached,
}
#[derive(Clone)]
pub struct ReconnectHandle(Weak<dyn RpcSession>);
impl fmt::Debug for ReconnectHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("ReconnectHandle").finish()
}
}
impl ReconnectHandle {
#[must_use]
pub fn new(rpc: Weak<dyn RpcSession>) -> Self {
Self(rpc)
}
pub async fn reconnect_with_delay(
&self,
delay_ms: u32,
) -> Result<(), Traced<ReconnectError>> {
platform::delay_for(Duration::from_millis(u64::from(delay_ms))).await;
let rpc = self
.0
.upgrade()
.ok_or_else(|| tracerr::new!(ReconnectError::Detached))?;
rpc.reconnect().await.map_err(tracerr::map_from_and_wrap!())
}
pub async fn reconnect_with_backoff(
&self,
starting_delay_ms: u32,
multiplier: f64,
max_delay: u32,
max_elapsed_time_ms: Option<u32>,
) -> Result<(), Traced<ReconnectError>> {
BackoffDelayer::new(
Duration::from_millis(starting_delay_ms.into()),
multiplier,
Duration::from_millis(max_delay.into()),
max_elapsed_time_ms.map(|val| Duration::from_millis(val.into())),
)
.retry(|| async {
self.0
.upgrade()
.ok_or_else(|| {
backoff::Error::Permanent(tracerr::new!(
ReconnectError::Detached
))
})?
.reconnect()
.await
.map_err(tracerr::map_from_and_wrap!())
.map_err(backoff::Error::transient)
})
.await
}
}