1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3
4use std::{
8 cell::RefCell,
9 error,
10 fmt::{self, Display},
11 future::Future,
12 ops,
13 pin::Pin,
14 sync::Arc,
15 task::{Context, Poll},
16};
17
18#[derive(Debug, Clone)]
22#[repr(transparent)]
23pub struct Error(Arc<dyn error::Error + Send + Sync>);
24
25impl Error {
26 pub fn into_inner(self) -> Arc<dyn error::Error + Send + Sync> {
28 Arc::clone(&self.0)
29 }
30}
31
32impl ops::Deref for Error {
33 type Target = Arc<dyn error::Error + Send + Sync>;
34
35 fn deref(&self) -> &Self::Target {
36 &self.0
37 }
38}
39
40impl fmt::Display for Error {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(f, "{}", self.0)
43 }
44}
45
46impl<T> From<T> for Error
47where
48 T: Into<Box<dyn error::Error + Send + Sync + 'static>>,
49{
50 fn from(value: T) -> Self {
51 Error(Arc::from(value.into()))
52 }
53}
54
55pub trait ErrorHook: Send + Sync {
62 fn throw(&self, error: Error) -> ErrorId;
64
65 fn clear(&self, id: &ErrorId);
67}
68
69#[derive(Debug, PartialEq, Eq, Hash, Clone, Default)]
72pub struct ErrorId(usize);
73
74impl Display for ErrorId {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 Display::fmt(&self.0, f)
77 }
78}
79
80impl From<usize> for ErrorId {
81 fn from(value: usize) -> Self {
82 Self(value)
83 }
84}
85
86thread_local! {
87 static ERROR_HOOK: RefCell<Option<Arc<dyn ErrorHook>>> = RefCell::new(None);
88}
89
90pub struct ResetErrorHookOnDrop(Option<Arc<dyn ErrorHook>>);
92
93impl Drop for ResetErrorHookOnDrop {
94 fn drop(&mut self) {
95 ERROR_HOOK.with_borrow_mut(|this| *this = self.0.take())
96 }
97}
98
99pub fn get_error_hook() -> Option<Arc<dyn ErrorHook>> {
101 ERROR_HOOK.with_borrow(Clone::clone)
102}
103
104pub fn set_error_hook(hook: Arc<dyn ErrorHook>) -> ResetErrorHookOnDrop {
106 ResetErrorHookOnDrop(
107 ERROR_HOOK.with_borrow_mut(|this| Option::replace(this, hook)),
108 )
109}
110
111pub fn throw(error: impl Into<Error>) -> ErrorId {
113 ERROR_HOOK
114 .with_borrow(|hook| hook.as_ref().map(|hook| hook.throw(error.into())))
115 .unwrap_or_default()
116}
117
118pub fn clear(id: &ErrorId) {
120 ERROR_HOOK
121 .with_borrow(|hook| hook.as_ref().map(|hook| hook.clear(id)))
122 .unwrap_or_default()
123}
124
125pin_project_lite::pin_project! {
126 pub struct ErrorHookFuture<Fut> {
129 hook: Option<Arc<dyn ErrorHook>>,
130 #[pin]
131 inner: Fut
132 }
133}
134
135impl<Fut> ErrorHookFuture<Fut> {
136 pub fn new(inner: Fut) -> Self {
139 Self {
140 hook: ERROR_HOOK.with_borrow(Clone::clone),
141 inner,
142 }
143 }
144}
145
146impl<Fut> Future for ErrorHookFuture<Fut>
147where
148 Fut: Future,
149{
150 type Output = Fut::Output;
151
152 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
153 let this = self.project();
154 let _hook = this
155 .hook
156 .as_ref()
157 .map(|hook| set_error_hook(Arc::clone(hook)));
158 this.inner.poll(cx)
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use std::error::Error as StdError;
166
167 #[derive(Debug)]
168 struct MyError;
169
170 impl Display for MyError {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 write!(f, "MyError")
173 }
174 }
175
176 impl StdError for MyError {}
177
178 #[test]
179 fn test_from() {
180 let e = MyError;
181 let _le = Error::from(e);
182
183 let e = "some error".to_string();
184 let _le = Error::from(e);
185
186 let e = anyhow::anyhow!("anyhow error");
187 let _le = Error::from(e);
188 }
189}