use crate::backend::Backend;
use std::{io, marker::PhantomData, time::Duration};
pub trait Port<B>: private::Sealed {
#[doc(hidden)]
fn backend_mut(&mut self) -> &mut B;
#[doc(hidden)]
fn poison(&mut self, e: io::Error);
}
mod private {
pub trait Sealed {}
#[cfg(feature = "ascii")]
impl<B, H, Tag> Sealed for crate::ascii::Port<'_, B, H, Tag> {}
#[cfg(feature = "binary")]
impl<B, H> Sealed for crate::binary::Port<'_, B, H> {}
}
#[derive(Debug)]
pub struct TimeoutGuard<'a, B: Backend, P: Port<B>> {
port: &'a mut P,
original_timeout: Option<Duration>,
backend_marker: PhantomData<B>,
}
impl<'a, B: Backend, P: Port<B>> TimeoutGuard<'a, B, P> {
pub(crate) fn new(port: &'a mut P, timeout: Option<Duration>) -> Result<Self, io::Error> {
let backend = port.backend_mut();
let original_timeout = backend.read_timeout()?;
backend.set_read_timeout(timeout)?;
Ok(TimeoutGuard {
port,
original_timeout,
backend_marker: PhantomData,
})
}
}
impl<B: Backend, P: Port<B>> std::ops::Deref for TimeoutGuard<'_, B, P> {
type Target = P;
fn deref(&self) -> &Self::Target {
self.port
}
}
impl<B: Backend, P: Port<B>> std::ops::DerefMut for TimeoutGuard<'_, B, P> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.port
}
}
impl<B: Backend, P: Port<B>> std::ops::Drop for TimeoutGuard<'_, B, P> {
fn drop(&mut self) {
if let Err(err) = self
.port
.backend_mut()
.set_read_timeout(self.original_timeout)
{
self.port.poison(io::Error::new(
io::ErrorKind::Other,
if let Some(timeout) = self.original_timeout {
format!(
"failed to reset timeout to {} seconds: {}",
timeout.as_secs(),
err
)
} else {
format!("failed to reset to an infinite timeout: {err}")
},
));
}
}
}