1use std::{
2 any::Any,
3 fmt,
4 panic::{AssertUnwindSafe, catch_unwind},
5 sync::Mutex,
6};
7
8#[derive(Debug)]
10pub enum PoolError<E> {
11 Factory(E),
13 Panic(PanicError),
15}
16
17impl<E: fmt::Display> fmt::Display for PoolError<E> {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 match self {
20 Self::Factory(error) => write!(f, "pool factory failed: {error}"),
21 Self::Panic(error) => write!(f, "pool operation panicked: {error}"),
22 }
23 }
24}
25
26impl<E: std::error::Error + 'static> std::error::Error for PoolError<E> {
27 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
28 match self {
29 Self::Factory(error) => Some(error),
30 Self::Panic(error) => Some(error),
31 }
32 }
33}
34
35impl<E> PoolError<E> {
36 pub fn catch<T>(operation: impl FnOnce() -> Result<T, E>) -> Result<T, Self> {
47 catch_operation(operation)
48 .map_err(Self::Panic)?
49 .map_err(Self::Factory)
50 }
51}
52
53pub struct PanicError {
57 payload: Mutex<Box<dyn Any + Send>>,
58}
59
60impl PanicError {
61 #[must_use]
63 pub fn into_payload(self) -> Box<dyn Any + Send> {
64 self.payload
65 .into_inner()
66 .unwrap_or_else(std::sync::PoisonError::into_inner)
67 }
68}
69
70impl fmt::Display for PanicError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 let payload = self
73 .payload
74 .lock()
75 .unwrap_or_else(std::sync::PoisonError::into_inner);
76 if let Some(message) = payload.downcast_ref::<String>() {
77 f.write_str(message)
78 } else if let Some(message) = payload.downcast_ref::<&str>() {
79 f.write_str(message)
80 } else {
81 f.write_str("non-string panic payload")
82 }
83 }
84}
85
86impl fmt::Debug for PanicError {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 f.debug_struct("PanicError")
89 .field("message", &format_args!("{self}"))
90 .finish_non_exhaustive()
91 }
92}
93
94impl std::error::Error for PanicError {}
95
96#[allow(clippy::redundant_pub_crate)]
98pub(super) fn catch_operation<T>(operation: impl FnOnce() -> T) -> Result<T, PanicError> {
99 catch_unwind(AssertUnwindSafe(operation)).map_err(|payload| PanicError {
103 payload: Mutex::new(payload),
104 })
105}
106
107#[cfg(test)]
108mod tests;