poison_guard/poison/
error.rs1use std::{
2 any::Any,
3 borrow::Cow,
4 error::Error,
5 fmt,
6 mem,
7 panic::Location,
8 sync::Arc,
9};
10
11#[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}