poison_guard/poison/
error.rs

1use std::{
2    any::Any,
3    borrow::Cow,
4    error::Error,
5    fmt,
6    mem,
7    panic::Location,
8    sync::Arc,
9};
10
11/**
12An error indicating that a value was poisoned.
13*/
14#[derive(Clone)]
15pub struct PoisonError(PoisonStateInner);
16
17impl fmt::Debug for PoisonError {
18    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19        fmt::Debug::fmt(&self.0, f)
20    }
21}
22
23impl fmt::Display for PoisonError {
24    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25        fmt::Display::fmt(&self.0, f)
26    }
27}
28
29impl Error for PoisonError {
30    fn source(&self) -> Option<&(dyn Error + 'static)> {
31        Error::source(&self.0)
32    }
33}
34
35#[derive(Clone)]
36pub(super) struct PoisonState(PoisonStateInner);
37
38#[derive(Clone)]
39enum PoisonStateInner {
40    CapturedPanic(Arc<CapturedPanic>),
41    UnknownPanic(Arc<UnknownPanic>),
42    CapturedErr(Arc<CapturedErr>),
43    UnknownErr(Arc<UnknownErr>),
44    Guarded(&'static Location<'static>),
45    Unpoisoned,
46}
47
48struct CapturedPanic {
49    location: &'static Location<'static>,
50    payload: Cow<'static, str>,
51}
52
53struct UnknownPanic {
54    location: &'static Location<'static>,
55}
56
57struct CapturedErr {
58    location: &'static Location<'static>,
59    source: Box<dyn Error + Send + Sync>,
60}
61
62struct UnknownErr {
63    location: &'static Location<'static>,
64}
65
66impl PoisonState {
67    pub(super) fn from_unpoisoned() -> Self {
68        PoisonState(PoisonStateInner::Unpoisoned)
69    }
70
71    pub(super) fn from_err(
72        location: &'static Location<'static>,
73        err: Option<Box<dyn Error + Send + Sync>>,
74    ) -> Self {
75        PoisonState(if let Some(err) = err {
76            PoisonStateInner::CapturedErr(Arc::new(CapturedErr {
77                location,
78                source: err,
79            }))
80        } else {
81            PoisonStateInner::UnknownErr(Arc::new(UnknownErr { location }))
82        })
83    }
84
85    pub(super) fn from_panic(
86        location: &'static Location<'static>,
87        panic: Option<Box<dyn Any + Send>>,
88    ) -> Self {
89        let panic = panic.and_then(|mut panic| {
90            if let Some(msg) = panic.downcast_ref::<&'static str>() {
91                return Some(Cow::Borrowed(*msg));
92            }
93
94            if let Some(msg) = panic.downcast_mut::<String>() {
95                return Some(Cow::Owned(mem::take(&mut *msg)));
96            }
97
98            None
99        });
100
101        PoisonState(if let Some(panic) = panic {
102            PoisonStateInner::CapturedPanic(Arc::new(CapturedPanic {
103                location,
104                payload: panic,
105            }))
106        } else {
107            PoisonStateInner::UnknownPanic(Arc::new(UnknownPanic { location }))
108        })
109    }
110
111    #[track_caller]
112    pub(super) fn guarded(&mut self) {
113        *self = PoisonState(PoisonStateInner::Guarded(Location::caller()))
114    }
115
116    #[track_caller]
117    pub(super) fn poison_with_error(&mut self, err: Option<Box<dyn Error + Send + Sync>>) {
118        let location = if let PoisonStateInner::Guarded(location) = self.0 {
119            location
120        } else {
121            Location::caller()
122        };
123
124        *self = PoisonState::from_err(location, err);
125    }
126
127    #[track_caller]
128    pub(super) fn poison_with_panic(&mut self, panic: Option<Box<dyn Any + Send>>) {
129        let location = if let PoisonStateInner::Guarded(location) = self.0 {
130            location
131        } else {
132            Location::caller()
133        };
134
135        *self = PoisonState::from_panic(location, panic);
136    }
137
138    #[track_caller]
139    pub(super) fn unpoison_if_guarded(&mut self) {
140        if let PoisonStateInner::Guarded(_) = self.0 {
141            *self = PoisonState::from_unpoisoned();
142        }
143    }
144
145    #[track_caller]
146    pub(super) fn unpoison(&mut self) {
147        *self = PoisonState::from_unpoisoned();
148    }
149
150    pub(super) fn is_unpoisoned(&self) -> bool {
151        matches!(self.0, PoisonStateInner::Unpoisoned)
152    }
153
154    pub(super) fn is_poisoned(&self) -> bool {
155        !self.is_unpoisoned()
156    }
157
158    pub(super) fn to_error(&self) -> PoisonError {
159        PoisonError(self.0.clone())
160    }
161
162    pub(super) fn as_dyn_error(&self) -> &(dyn Error + Send + Sync + 'static) {
163        &self.0
164    }
165
166    pub(super) fn to_dyn_error(&self) -> Box<dyn Error + Send + Sync> {
167        Box::new(self.0.clone())
168    }
169}
170
171impl fmt::Debug for PoisonStateInner {
172    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173        match self {
174            PoisonStateInner::CapturedPanic(panic) => f
175                .debug_struct("PoisonState")
176                .field(&"panic", &panic.payload)
177                .field(&"location", &panic.location)
178                .finish(),
179            PoisonStateInner::UnknownPanic(panic) => f
180                .debug_struct("PoisonState")
181                .field(&"panic", &"<unknown>")
182                .field(&"location", &panic.location)
183                .finish(),
184            PoisonStateInner::CapturedErr(err) => f
185                .debug_struct("PoisonState")
186                .field(&"err", &err.source)
187                .field(&"location", &err.location)
188                .finish(),
189            PoisonStateInner::UnknownErr(err) => f
190                .debug_struct("PoisonState")
191                .field(&"err", &"<unknown>")
192                .field(&"location", &err.location)
193                .finish(),
194            PoisonStateInner::Guarded(location) => f
195                .debug_struct("PoisonState")
196                .field(&"location", &location)
197                .finish(),
198            PoisonStateInner::Unpoisoned => f.debug_struct("PoisonState").finish(),
199        }
200    }
201}
202
203impl fmt::Display for PoisonStateInner {
204    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205        match self {
206            PoisonStateInner::CapturedPanic(panic) => {
207                write!(
208                    f,
209                    "poisoned by a panic '{}' (the poisoning guard was acquired at '{}')",
210                    panic.payload, panic.location
211                )
212            }
213            PoisonStateInner::UnknownPanic(panic) => write!(
214                f,
215                "poisoned by a panic (the poisoning guard was acquired at '{}')",
216                panic.location
217            ),
218            PoisonStateInner::CapturedErr(err) => write!(
219                f,
220                "poisoned by an error (the poisoning guard was acquired at '{}')",
221                err.location
222            ),
223            PoisonStateInner::UnknownErr(err) => write!(
224                f,
225                "poisoned by an error (the poisoning guard was acquired at '{}')",
226                err.location
227            ),
228            PoisonStateInner::Guarded(location) => write!(
229                f,
230                "poisoned (the poisoning guard was acquired at '{}')",
231                location
232            ),
233            PoisonStateInner::Unpoisoned => write!(f, "a guard was not poisoned"),
234        }
235    }
236}
237
238impl Error for PoisonStateInner {
239    fn source(&self) -> Option<&(dyn Error + 'static)> {
240        if let PoisonStateInner::CapturedErr(ref err) = self {
241            Some(&*err.source)
242        } else {
243            None
244        }
245    }
246}