Skip to main content

ntex_error/
error.rs

1use std::{error, fmt, ops, panic::Location, sync::Arc};
2
3use crate::{Backtrace, ErrorDiagnostic, ErrorMapping, repr::ErrorRepr};
4
5/// An error container.
6///
7/// `Error<E>` is a lightweight handle to an error that can be cheaply cloned
8/// and safely shared across threads. It preserves the original error along with
9/// associated context such as where it occurred.
10pub struct Error<E> {
11    pub(crate) inner: Arc<ErrorRepr<E>>,
12}
13
14impl<E> Error<E> {
15    /// Creates a new error container.
16    ///
17    /// Captures the caller location and associates the error with a service.
18    #[track_caller]
19    pub fn new<T>(error: T, service: &'static str) -> Self
20    where
21        E: From<T>,
22    {
23        Self {
24            inner: Arc::new(ErrorRepr::new3(
25                E::from(error),
26                Some(service),
27                Location::caller(),
28            )),
29        }
30    }
31
32    /// Transforms the inner error into another error type.
33    ///
34    /// Preserves `service`, backtrace, and extension data.
35    pub fn forward<U, F>(self, f: F) -> Error<U>
36    where
37        F: FnOnce(Error<E>) -> U,
38    {
39        let svc = self.inner.service;
40        let bt = self.inner.backtrace.clone();
41        let ext = self.inner.ext.clone();
42
43        Error {
44            inner: Arc::new(ErrorRepr::new2(f(self), svc, bt, ext)),
45        }
46    }
47
48    /// Returns a debug view of the error.
49    ///
50    /// Intended for debugging purposes.
51    pub fn debug(&self) -> impl fmt::Debug
52    where
53        E: fmt::Debug,
54    {
55        ErrorDebug {
56            inner: self.inner.as_ref(),
57        }
58    }
59
60    /// Returns a reference to a previously stored value of type `T` from this error.
61    ///
62    /// This can be used to access additional contextual data attached to the error.
63    pub fn get_item<T: 'static>(&self) -> Option<&T> {
64        self.inner.ext.get::<T>()
65    }
66}
67
68impl<E: Clone> Error<E> {
69    /// Sets the service responsible for this error.
70    ///
71    /// Returns the updated error.
72    #[must_use]
73    pub fn set_service(mut self, name: &'static str) -> Self {
74        if let Some(inner) = Arc::get_mut(&mut self.inner) {
75            inner.service = Some(name);
76            self
77        } else {
78            Error {
79                inner: Arc::new(ErrorRepr::new2(
80                    self.inner.error.clone(),
81                    Some(name),
82                    self.inner.backtrace.clone(),
83                    self.inner.ext.clone(),
84                )),
85            }
86        }
87    }
88
89    /// Maps the inner error into a new error type.
90    ///
91    /// Preserves `service`, backtrace, and extension data.
92    pub fn map<U, F>(self, f: F) -> Error<U>
93    where
94        F: FnOnce(E) -> U,
95    {
96        let (err, svc, bt, ext) = match Arc::try_unwrap(self.inner) {
97            Ok(inner) => (inner.error, inner.service, inner.backtrace, inner.ext),
98            Err(inner) => (
99                inner.error.clone(),
100                inner.service,
101                inner.backtrace.clone(),
102                inner.ext.clone(),
103            ),
104        };
105
106        Error {
107            inner: Arc::new(ErrorRepr::new2(f(err), svc, bt, ext)),
108        }
109    }
110
111    /// Try to map inner error to new error.
112    ///
113    /// Preserves `service`, backtrace, and extension data.
114    pub fn try_map<T, U, F>(self, f: F) -> Result<T, Error<U>>
115    where
116        F: FnOnce(E) -> Result<T, U>,
117    {
118        let (err, svc, bt, ext) = match Arc::try_unwrap(self.inner) {
119            Ok(inner) => (inner.error, inner.service, inner.backtrace, inner.ext),
120            Err(inner) => (
121                inner.error.clone(),
122                inner.service,
123                inner.backtrace.clone(),
124                inner.ext.clone(),
125            ),
126        };
127
128        f(err).map_err(move |err| Error {
129            inner: Arc::new(ErrorRepr::new2(err, svc, bt, ext)),
130        })
131    }
132
133    /// Attaches a typed value to this `Error`.
134    ///
135    /// This value can be retrieved later using `get_item::<T>()`.
136    #[must_use]
137    pub fn insert_item<T: Sync + Send + 'static>(mut self, val: T) -> Self {
138        if let Some(inner) = Arc::get_mut(&mut self.inner) {
139            inner.ext.insert(val);
140            self
141        } else {
142            let mut ext = self.inner.ext.clone();
143            ext.insert(val);
144            Error {
145                inner: Arc::new(ErrorRepr::new2(
146                    self.inner.error.clone(),
147                    self.inner.service,
148                    self.inner.backtrace.clone(),
149                    ext,
150                )),
151            }
152        }
153    }
154}
155
156impl<E: Clone> Error<E> {
157    /// Consumes this error and returns the inner error value.
158    pub fn into_error(self) -> E {
159        Arc::try_unwrap(self.inner)
160            .map_or_else(|inner| inner.error.clone(), |inner| inner.error)
161    }
162}
163
164impl<E> Clone for Error<E> {
165    fn clone(&self) -> Error<E> {
166        Error {
167            inner: self.inner.clone(),
168        }
169    }
170}
171
172impl<E> From<E> for Error<E> {
173    #[track_caller]
174    fn from(error: E) -> Self {
175        Self {
176            inner: Arc::new(ErrorRepr::new3(error, None, Location::caller())),
177        }
178    }
179}
180
181impl<E> Eq for Error<E> where E: Eq {}
182
183impl<E> PartialEq for Error<E>
184where
185    E: PartialEq,
186{
187    fn eq(&self, other: &Self) -> bool {
188        self.inner.error.eq(&other.inner.error) && self.inner.service == other.inner.service
189    }
190}
191
192impl<E> PartialEq<E> for Error<E>
193where
194    E: PartialEq,
195{
196    fn eq(&self, other: &E) -> bool {
197        self.inner.error.eq(other)
198    }
199}
200
201impl<E> ops::Deref for Error<E> {
202    type Target = E;
203
204    fn deref(&self) -> &E {
205        &self.inner.error
206    }
207}
208
209impl<E: error::Error + 'static> error::Error for Error<E> {
210    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
211        self.inner.error.source()
212    }
213}
214
215impl<E: ErrorDiagnostic> ErrorDiagnostic for Error<E> {
216    type Kind = E::Kind;
217
218    fn kind(&self) -> Self::Kind {
219        self.inner.kind()
220    }
221
222    fn service(&self) -> Option<&'static str> {
223        self.inner.service()
224    }
225
226    fn backtrace(&self) -> Option<&Backtrace> {
227        self.inner.backtrace()
228    }
229}
230
231impl<T, E, U> ErrorMapping<T, E, U> for Result<T, E>
232where
233    U: From<E>,
234{
235    fn into_error(self) -> Result<T, Error<U>> {
236        match self {
237            Ok(val) => Ok(val),
238            Err(err) => Err(Error {
239                inner: Arc::new(ErrorRepr::new3(U::from(err), None, Location::caller())),
240            }),
241        }
242    }
243}
244
245impl<T, E, U> ErrorMapping<T, E, U> for Result<T, Error<E>>
246where
247    U: From<E>,
248    E: Clone,
249{
250    fn into_error(self) -> Result<T, Error<U>> {
251        match self {
252            Ok(val) => Ok(val),
253            Err(err) => Err(err.map(U::from)),
254        }
255    }
256}
257
258impl<E: fmt::Display> fmt::Display for Error<E> {
259    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260        fmt::Display::fmt(&self.inner.error, f)
261    }
262}
263
264impl<E: fmt::Debug> fmt::Debug for Error<E> {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        fmt::Debug::fmt(&self.inner.error, f)
267    }
268}
269
270struct ErrorDebug<'a, E> {
271    inner: &'a ErrorRepr<E>,
272}
273
274impl<E: fmt::Debug> fmt::Debug for ErrorDebug<'_, E> {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        f.debug_struct("Error")
277            .field("error", &self.inner.error)
278            .field("service", &self.inner.service)
279            .field("backtrace", &self.inner.backtrace)
280            .finish()
281    }
282}