Skip to main content

ntex_error/
error.rs

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