Skip to main content

ntex_error/
info.rs

1use ntex_bytes::Bytes;
2use std::{any::Any, any::TypeId, error, fmt, sync::Arc};
3
4use crate::{AsError, Backtrace, Error, ErrorDiagnostic, IntoFailure, repr::ErrorRepr};
5
6trait ErrorInfo: fmt::Display + fmt::Debug + 'static {
7    fn tag(&self) -> Option<&crate::Bytes>;
8
9    fn service(&self) -> Option<&'static str>;
10
11    fn signature(&self) -> &'static str;
12
13    fn backtrace(&self) -> Option<&Backtrace>;
14
15    fn source(&self) -> Option<&(dyn error::Error + 'static)>;
16
17    fn get_item(&self, id: &TypeId) -> Option<&(dyn Any + Send + Sync)>;
18}
19
20impl<E> ErrorInfo for ErrorRepr<E>
21where
22    E: ErrorDiagnostic,
23{
24    fn tag(&self) -> Option<&crate::Bytes> {
25        ErrorDiagnostic::tag(self)
26    }
27
28    fn service(&self) -> Option<&'static str> {
29        ErrorDiagnostic::service(self)
30    }
31
32    fn signature(&self) -> &'static str {
33        self.error.signature()
34    }
35
36    fn backtrace(&self) -> Option<&Backtrace> {
37        ErrorDiagnostic::backtrace(self)
38    }
39
40    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
41        self.error.source()
42    }
43
44    fn get_item(&self, id: &TypeId) -> Option<&(dyn Any + Send + Sync)> {
45        self.ext.map.get(id).map(AsRef::as_ref)
46    }
47}
48
49/// Type-erased container holding error information.
50///
51/// This allows storing and passing error metadata without exposing the concrete type.
52pub struct Failure {
53    inner: FailureDiagnostic,
54}
55
56/// Type-erased diagnostic view of a [`Failure`].
57///
58/// Returned by [`AsError::as_diag()`](crate::AsError::as_diag) for a [`Failure`].
59pub struct FailureDiagnostic(Arc<dyn ErrorInfo>);
60
61impl Failure {
62    /// Returns an optional tag associated with this error.
63    pub fn tag(&self) -> Option<&crate::Bytes> {
64        self.inner.0.tag()
65    }
66
67    /// Returns the name of the responsible service, if applicable.
68    pub fn service(&self) -> Option<&'static str> {
69        self.inner.0.service()
70    }
71
72    /// Returns a stable identifier for the specific error classification.
73    pub fn signature(&self) -> &'static str {
74        self.inner.0.signature()
75    }
76
77    /// Returns a backtrace for debugging purposes, if available.
78    pub fn backtrace(&self) -> Option<&Backtrace> {
79        self.inner.0.backtrace()
80    }
81
82    /// Returns a reference to a previously stored value of type `T` from this error.
83    pub fn get_item<T: 'static>(&self) -> Option<&T> {
84        self.inner
85            .0
86            .get_item(&TypeId::of::<T>())
87            .and_then(|boxed| boxed.downcast_ref())
88    }
89}
90
91impl<E> From<Error<E>> for Failure
92where
93    E: ErrorDiagnostic,
94{
95    fn from(err: Error<E>) -> Self {
96        Self {
97            inner: FailureDiagnostic(err.inner),
98        }
99    }
100}
101
102impl<E> From<&Error<E>> for Failure
103where
104    E: ErrorDiagnostic,
105{
106    fn from(err: &Error<E>) -> Self {
107        Self {
108            inner: FailureDiagnostic(err.inner.clone()),
109        }
110    }
111}
112
113impl<E> IntoFailure for E
114where
115    E: ErrorDiagnostic + Into<Error<E>>,
116{
117    #[track_caller]
118    fn fail(self) -> Failure {
119        self.into_failure()
120    }
121}
122
123impl error::Error for Failure {
124    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
125        self.inner.source()
126    }
127}
128
129impl fmt::Debug for Failure {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        fmt::Debug::fmt(&self.inner, f)
132    }
133}
134
135impl fmt::Display for Failure {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        fmt::Display::fmt(&self.inner, f)
138    }
139}
140
141impl Clone for Failure {
142    fn clone(&self) -> Self {
143        Failure {
144            inner: FailureDiagnostic(self.inner.0.clone()),
145        }
146    }
147}
148
149impl AsError for Failure {
150    type Target = FailureDiagnostic;
151
152    fn as_diag(&self) -> &FailureDiagnostic {
153        &self.inner
154    }
155}
156
157impl ErrorDiagnostic for Failure {
158    fn signature(&self) -> &'static str {
159        self.inner.0.signature()
160    }
161
162    fn tag(&self) -> Option<&Bytes> {
163        self.inner.0.tag()
164    }
165
166    fn service(&self) -> Option<&'static str> {
167        self.inner.0.service()
168    }
169
170    fn backtrace(&self) -> Option<&Backtrace> {
171        self.inner.0.backtrace()
172    }
173
174    fn into_failure(self) -> Failure {
175        self
176    }
177}
178
179impl ErrorDiagnostic for FailureDiagnostic {
180    fn signature(&self) -> &'static str {
181        self.0.signature()
182    }
183
184    fn tag(&self) -> Option<&Bytes> {
185        self.0.tag()
186    }
187
188    fn service(&self) -> Option<&'static str> {
189        self.0.service()
190    }
191
192    fn backtrace(&self) -> Option<&Backtrace> {
193        self.0.backtrace()
194    }
195}
196
197impl error::Error for FailureDiagnostic {
198    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
199        self.0.source()
200    }
201}
202
203impl fmt::Debug for FailureDiagnostic {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        fmt::Debug::fmt(&self.0, f)
206    }
207}
208
209impl fmt::Display for FailureDiagnostic {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        fmt::Display::fmt(&self.0, f)
212    }
213}