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
5pub struct Error<E> {
6    pub(crate) inner: Arc<ErrorRepr<E>>,
7}
8
9impl<E> Error<E> {
10    #[track_caller]
11    pub fn new<T>(error: T, service: &'static str) -> Self
12    where
13        E: From<T>,
14    {
15        Self {
16            inner: Arc::new(ErrorRepr::new3(
17                E::from(error),
18                Some(service),
19                Location::caller(),
20            )),
21        }
22    }
23
24    #[must_use]
25    /// Set responsible service
26    pub fn set_service(mut self, name: &'static str) -> Self
27    where
28        E: Clone,
29    {
30        if let Some(inner) = Arc::get_mut(&mut self.inner) {
31            inner.service = Some(name);
32            self
33        } else {
34            Error {
35                inner: Arc::new(ErrorRepr::new2(
36                    self.inner.error.clone(),
37                    Some(name),
38                    self.inner.backtrace.clone(),
39                )),
40            }
41        }
42    }
43
44    /// Map inner error to new error
45    ///
46    /// Keep same `service` and `location`
47    pub fn map<U, F>(self, f: F) -> Error<U>
48    where
49        E: Clone,
50        F: FnOnce(E) -> U,
51    {
52        let (err, svc, bt) = match Arc::try_unwrap(self.inner) {
53            Ok(inner) => (inner.error, inner.service, inner.backtrace),
54            Err(inner) => (inner.error.clone(), inner.service, inner.backtrace.clone()),
55        };
56
57        Error {
58            inner: Arc::new(ErrorRepr::new2(f(err), svc, bt)),
59        }
60    }
61
62    /// Map inner error to new error
63    ///
64    /// Keep same `service` and `location`
65    pub fn forward<U, F>(self, f: F) -> Error<U>
66    where
67        F: FnOnce(Error<E>) -> U,
68    {
69        let svc = self.inner.service;
70        let bt = self.inner.backtrace.clone();
71
72        Error {
73            inner: Arc::new(ErrorRepr::new2(f(self), svc, bt)),
74        }
75    }
76
77    /// Try to map inner error to new error
78    ///
79    /// Keep same `service` and `location`
80    pub fn try_map<T, U, F>(self, f: F) -> Result<T, Error<U>>
81    where
82        E: Clone,
83        F: FnOnce(E) -> Result<T, U>,
84    {
85        let (err, svc, bt) = match Arc::try_unwrap(self.inner) {
86            Ok(inner) => (inner.error, inner.service, inner.backtrace),
87            Err(inner) => (inner.error.clone(), inner.service, inner.backtrace.clone()),
88        };
89
90        f(err).map_err(move |err| Error {
91            inner: Arc::new(ErrorRepr::new2(err, svc, bt)),
92        })
93    }
94
95    /// Print error debug information
96    pub fn debug(&self) -> impl fmt::Debug
97    where
98        E: fmt::Debug,
99    {
100        ErrorDebug {
101            inner: self.inner.as_ref(),
102        }
103    }
104}
105
106impl<E: Clone> Error<E> {
107    /// Get inner error value
108    pub fn into_error(self) -> E {
109        Arc::try_unwrap(self.inner)
110            .map_or_else(|inner| inner.error.clone(), |inner| inner.error)
111    }
112}
113
114impl<E> Clone for Error<E> {
115    fn clone(&self) -> Error<E> {
116        Error {
117            inner: self.inner.clone(),
118        }
119    }
120}
121
122impl<E> From<E> for Error<E> {
123    #[track_caller]
124    fn from(error: E) -> Self {
125        Self {
126            inner: Arc::new(ErrorRepr::new3(error, None, Location::caller())),
127        }
128    }
129}
130
131impl<E> Eq for Error<E> where E: Eq {}
132
133impl<E> PartialEq for Error<E>
134where
135    E: PartialEq,
136{
137    fn eq(&self, other: &Self) -> bool {
138        self.inner.error.eq(&other.inner.error) && self.inner.service == other.inner.service
139    }
140}
141
142impl<E> PartialEq<E> for Error<E>
143where
144    E: PartialEq,
145{
146    fn eq(&self, other: &E) -> bool {
147        self.inner.error.eq(other)
148    }
149}
150
151impl<E> ops::Deref for Error<E> {
152    type Target = E;
153
154    fn deref(&self) -> &E {
155        &self.inner.error
156    }
157}
158
159impl<E: error::Error + 'static> error::Error for Error<E> {
160    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
161        Some(&self.inner.error)
162    }
163}
164
165impl<E: ErrorDiagnostic> ErrorDiagnostic for Error<E> {
166    type Kind = E::Kind;
167
168    fn kind(&self) -> Self::Kind {
169        self.inner.kind()
170    }
171
172    fn service(&self) -> Option<&'static str> {
173        self.inner.service()
174    }
175
176    fn backtrace(&self) -> Option<&Backtrace> {
177        self.inner.backtrace()
178    }
179}
180
181impl<T, E, U> ErrorMapping<T, E, U> for Result<T, E>
182where
183    U: From<E>,
184{
185    fn into_error(self) -> Result<T, Error<U>> {
186        match self {
187            Ok(val) => Ok(val),
188            Err(err) => Err(Error {
189                inner: Arc::new(ErrorRepr::new3(U::from(err), None, Location::caller())),
190            }),
191        }
192    }
193}
194
195impl<T, E, U> ErrorMapping<T, E, U> for Result<T, Error<E>>
196where
197    U: From<E>,
198    E: Clone,
199{
200    fn into_error(self) -> Result<T, Error<U>> {
201        match self {
202            Ok(val) => Ok(val),
203            Err(err) => Err(err.map(U::from)),
204        }
205    }
206}
207
208impl<E: fmt::Display> fmt::Display for Error<E> {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        fmt::Display::fmt(&self.inner.error, f)
211    }
212}
213
214impl<E: fmt::Debug> fmt::Debug for Error<E> {
215    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        fmt::Debug::fmt(&self.inner.error, f)
217    }
218}
219
220struct ErrorDebug<'a, E> {
221    inner: &'a ErrorRepr<E>,
222}
223
224impl<E: fmt::Debug> fmt::Debug for ErrorDebug<'_, E> {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        f.debug_struct("Error")
227            .field("error", &self.inner.error)
228            .field("service", &self.inner.service)
229            .field("backtrace", &self.inner.backtrace)
230            .finish()
231    }
232}