From b83c0d2a6926fcac9ec273d0d60318a79d7db319 Mon Sep 17 00:00:00 2001 From: Ulrik Mikaelsson Date: Mon, 13 Oct 2025 14:17:05 +0200 Subject: [PATCH] error:Translate Error::Timeout into io::ErrorKind::Timedout This enables better categorization of errors. In particular, TcpTransport translates `ErrorKind` to `ureq::Error::Timeout` errors on timeout conditions. These should then be re-raised as `ErrorKind::Timedout` errors behind the `io::Error` abstraction. Omitting this error-structure forces downstream error-handlers to resort to string-matching to distinguish timeout-errors from other errors. --- src/error.rs | 7 ++++-- src/unversioned/transport/io.rs | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 0721cab3..4c7a41bc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -185,11 +185,14 @@ impl std::error::Error for Error {} impl Error { /// Convert the error into a [`std::io::Error`]. /// - /// If the error is [`Error::Io`], we unpack the error. In othe cases we make - /// an `std::io::ErrorKind::Other`. + /// If the error is [`Error::Io`], we unpack the error. If the error is + /// [`Error::Timeout`] we translate to `io::ErrorKind::TimedOut`. + /// In other cases we make it an `std::io::ErrorKind::Other`. pub fn into_io(self) -> io::Error { if let Self::Io(e) = self { e + } else if let Self::Timeout(_) = self { + io::Error::new(io::ErrorKind::TimedOut, self) } else { io::Error::new(io::ErrorKind::Other, self) } diff --git a/src/unversioned/transport/io.rs b/src/unversioned/transport/io.rs index 644496a2..09f7bab8 100644 --- a/src/unversioned/transport/io.rs +++ b/src/unversioned/transport/io.rs @@ -85,3 +85,45 @@ impl io::Write for TransportAdapter { Ok(()) } } + +#[cfg(test)] +mod tests { + use std::io::{self, Read}; + + use super::super::*; + use super::*; + + #[test] + fn timeout_errors_translate_transparently() { + #[derive(Debug)] + struct FailingTransport(LazyBuffers); + impl Transport for FailingTransport { + fn buffers(&mut self) -> &mut dyn crate::unversioned::transport::Buffers { + &mut self.0 + } + + fn transmit_output( + &mut self, + _amount: usize, + _timeout: NextTimeout, + ) -> Result<(), crate::Error> { + unimplemented!() + } + + fn await_input(&mut self, _timeout: NextTimeout) -> Result { + Err(crate::Error::Timeout(Timeout::Global)) + } + + fn is_open(&mut self) -> bool { + unimplemented!() + } + } + + let mut adapter = TransportAdapter::new(FailingTransport(LazyBuffers::new(1024, 1024))); + let Err(expected_error) = adapter.read(&mut [0u8; 10]) else { + panic!("Expected error, but got success"); + }; + + assert_eq!(expected_error.kind(), io::ErrorKind::TimedOut); + } +}