Skip to main content

compio_pool/
error.rs

1//! Error types returned by pool operations.
2
3use core::fmt;
4
5/// Failure modes of [`Pool::acquire`](crate::Pool::acquire).
6///
7/// `E` is the manager's own error type ([`Manage::Error`](crate::Manage::Error)).
8#[derive(Debug)]
9pub enum Error<E> {
10    /// The acquire timeout elapsed before a connection became available.
11    Timeout,
12    /// The pool was closed via [`Pool::close`](crate::Pool::close).
13    Closed,
14    /// The manager failed to establish a new connection.
15    Backend(E),
16}
17
18impl<E> Error<E> {
19    /// Returns the underlying manager error, if this is a [`Error::Backend`].
20    pub fn into_backend(self) -> Option<E> {
21        match self {
22            Error::Backend(e) => Some(e),
23            _ => None,
24        }
25    }
26}
27
28impl<E: fmt::Display> fmt::Display for Error<E> {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Error::Timeout => f.write_str("timed out waiting for a pooled connection"),
32            Error::Closed => f.write_str("connection pool is closed"),
33            Error::Backend(e) => write!(f, "failed to establish connection: {e}"),
34        }
35    }
36}
37
38impl<E: std::error::Error + 'static> std::error::Error for Error<E> {
39    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
40        match self {
41            Error::Backend(e) => Some(e),
42            _ => None,
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use std::error::Error as _;
50
51    use super::*;
52
53    fn io() -> std::io::Error {
54        std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused")
55    }
56
57    #[test]
58    fn into_backend_yields_the_manager_error() {
59        assert_eq!(Error::Backend("boom").into_backend(), Some("boom"));
60    }
61
62    #[test]
63    fn into_backend_is_none_for_pool_side_failures() {
64        assert_eq!(Error::<&str>::Timeout.into_backend(), None);
65        assert_eq!(Error::<&str>::Closed.into_backend(), None);
66    }
67
68    #[test]
69    fn display_names_the_failure_mode() {
70        assert_eq!(
71            Error::<&str>::Timeout.to_string(),
72            "timed out waiting for a pooled connection"
73        );
74        assert_eq!(
75            Error::<&str>::Closed.to_string(),
76            "connection pool is closed"
77        );
78        assert_eq!(
79            Error::Backend("refused").to_string(),
80            "failed to establish connection: refused"
81        );
82    }
83
84    #[test]
85    fn a_backend_error_is_the_source() {
86        let e = Error::Backend(io());
87        let source = e.source().expect("Backend must expose its cause");
88        assert_eq!(source.to_string(), io().to_string());
89    }
90
91    #[test]
92    fn pool_side_failures_have_no_source() {
93        assert!(Error::<std::io::Error>::Timeout.source().is_none());
94        assert!(Error::<std::io::Error>::Closed.source().is_none());
95    }
96
97    #[test]
98    fn is_debug() {
99        assert!(format!("{:?}", Error::<&str>::Timeout).contains("Timeout"));
100        assert!(format!("{:?}", Error::<&str>::Closed).contains("Closed"));
101        assert!(format!("{:?}", Error::Backend("x")).contains("Backend"));
102    }
103
104    /// `Error<E>` is only an `std::error::Error` when `E` is; this is the
105    /// compile-time half of that contract.
106    #[test]
107    fn implements_std_error_for_std_error_payloads() {
108        fn assert_std_error<T: std::error::Error>(_: &T) {}
109        assert_std_error(&Error::Backend(io()));
110    }
111}