1#![deny(clippy::pedantic)]
3#![allow(
4 clippy::must_use_candidate,
5 clippy::missing_errors_doc,
6 clippy::missing_panics_doc
7)]
8use std::{error::Error as StdError, fmt};
9
10use ntex_bytes::Bytes;
11
12mod bt;
13mod error;
14mod ext;
15mod info;
16mod message;
17mod repr;
18pub mod utils;
19
20pub use crate::bt::{Backtrace, BacktraceRaw, BacktraceResolver};
21pub use crate::error::Error;
22pub use crate::info::Failure;
23pub use crate::message::{ErrorMessage, ErrorMessageChained};
24pub use crate::message::{fmt_diag, fmt_diag_string, fmt_diag_typ, fmt_err, fmt_err_string};
25pub use crate::utils::{ResultSignature, Retryable, Success, with_service};
26
27#[doc(hidden)]
28pub use crate::bt::{set_backtrace_start, set_backtrace_start_alt};
29
30#[doc(hidden)]
31#[deprecated(since = "2.6.0")]
32pub type ErrorInfo = Failure;
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
36pub enum ResultType {
37 Success,
38 ClientError,
39 ServiceError,
40}
41
42impl ResultType {
43 pub const fn as_str(&self) -> &'static str {
45 match self {
46 ResultType::Success => "Success",
47 ResultType::ClientError => "ClientError",
48 ResultType::ServiceError => "ServiceError",
49 }
50 }
51}
52
53pub trait AsError {
54 type Target: ErrorDiagnostic;
55
56 fn as_diag(&self) -> &Self::Target;
57}
58
59pub trait ErrorDiagnostic: StdError + 'static {
63 #[doc(hidden)]
64 #[deprecated(since = "2.1.0")]
65 fn typ(&self) -> ResultType {
67 ResultType::ServiceError
68 }
69
70 fn signature(&self) -> &'static str;
74
75 fn tag(&self) -> Option<&Bytes> {
80 None
81 }
82
83 fn service(&self) -> Option<&'static str> {
87 None
88 }
89
90 fn backtrace(&self) -> Option<&Backtrace> {
92 None
93 }
94}
95
96pub trait ErrorMapping<T, E, U> {
98 fn into_error(self) -> Result<T, Error<U>>;
100}
101
102impl fmt::Display for ResultType {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(f, "{}", self.as_str())
105 }
106}
107
108impl ErrorDiagnostic for ResultType {
109 fn signature(&self) -> &'static str {
110 self.as_str()
111 }
112}
113
114pub trait IntoFailure: Sized {
116 fn fail(self) -> Failure;
117}
118
119#[cfg(test)]
120mod tests {
121 use std::{error::Error as StdError, mem};
122
123 use super::*;
124
125 #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
126 enum TestError {
127 #[error("Connect err: {0}")]
128 Connect(&'static str),
129 #[error("Disconnect")]
130 Disconnect,
131 #[error("InternalServiceError")]
132 Service(&'static str),
133 }
134
135 impl ErrorDiagnostic for TestError {
136 fn signature(&self) -> &'static str {
137 match self {
138 TestError::Connect(_) => "Client-Connect",
139 TestError::Disconnect => "Client-Disconnect",
140 TestError::Service(_) => "Service-Internal",
141 }
142 }
143
144 fn service(&self) -> Option<&'static str> {
145 Some("test")
146 }
147 }
148
149 impl From<&TestError> for ResultType {
150 fn from(err: &TestError) -> ResultType {
151 match err {
152 TestError::Connect(_) | TestError::Disconnect => ResultType::ClientError,
153 TestError::Service(_) => ResultType::ServiceError,
154 }
155 }
156 }
157
158 #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
159 #[error("TestError2")]
160 struct TestError2;
161 impl ErrorDiagnostic for TestError2 {
162 fn typ(&self) -> ResultType {
163 ResultType::ClientError
164 }
165
166 fn signature(&self) -> &'static str {
167 "TestError2"
168 }
169 }
170
171 impl From<TestError> for TestError2 {
172 fn from(_err: TestError) -> TestError2 {
173 TestError2
174 }
175 }
176
177 #[ntex::test]
178 async fn test_error() {
179 let err: Error<TestError> = TestError::Service("409 Error").into();
180 let err = err.clone();
181 assert_eq!(err.to_string(), "InternalServiceError");
182 assert_eq!(err.service(), Some("test"));
183 assert_eq!(err.signature(), "Service-Internal");
184 assert_eq!(
185 err,
186 Into::<Error<TestError>>::into(TestError::Service("409 Error"))
187 );
188 assert!(err.backtrace().is_some());
189
190 let err = err.set_service("SVC");
191 assert_eq!(err.service(), Some("SVC"));
192 let err = err.set_tag("TAG");
193 assert_eq!(err.tag().unwrap(), &b"TAG"[..]);
194
195 let err2: Error<TestError> = Error::new(TestError::Service("409 Error"), "TEST");
196 assert_ne!(err, err2);
197 assert_eq!(err, TestError::Service("409 Error"));
198
199 let err2 = err2.set_tag("TAG");
200 assert_eq!(err.tag().unwrap(), &b"TAG"[..]);
201 let err2 = err2.set_service("SVC");
202 assert_eq!(err, err2);
203 let err2 = err2.map(|_| TestError::Disconnect);
204 assert_ne!(err, err2);
205 let err2 = err2.forward(|_| TestError::Disconnect);
206 assert_ne!(err, err2);
207
208 assert_eq!(TestError::Connect("").to_string(), "Connect err: ");
209 assert_eq!(TestError::Disconnect.to_string(), "Disconnect");
210 assert_eq!(TestError::Disconnect.service(), Some("test"));
211 assert!(TestError::Disconnect.backtrace().is_none());
212
213 assert_eq!(ResultType::ClientError.as_str(), "ClientError");
214 assert_eq!(ResultType::ServiceError.as_str(), "ServiceError");
215 assert_eq!(ResultType::ClientError.to_string(), "ClientError");
216 assert_eq!(ResultType::ServiceError.to_string(), "ServiceError");
217 assert_eq!(format!("{}", ResultType::ClientError), "ClientError");
218
219 assert_eq!(TestError::Connect("").signature(), "Client-Connect");
220 assert_eq!(TestError::Disconnect.signature(), "Client-Disconnect");
221 assert_eq!(TestError::Service("").signature(), "Service-Internal");
222
223 let err = err.into_error();
224 assert_eq!(err.to_string(), "InternalServiceError");
225 assert!(err.source().is_none());
226 assert!(format!("{err:?}").contains("Service(\"409 Error\")"));
227
228 #[cfg(unix)]
229 {
230 let err: Error<TestError> = TestError::Service("404 Error").into();
231 if let Some(bt) = err.backtrace() {
232 bt.resolver().resolve();
233 assert!(
234 format!("{bt}").contains("ntex_error::tests::test_error"),
235 "{bt}",
236 );
237 assert!(
238 bt.repr().unwrap().contains("ntex_error::tests::test_error"),
239 "{bt}"
240 );
241 }
242 }
243
244 assert_eq!(24, mem::size_of::<TestError>());
245 assert_eq!(8, mem::size_of::<Error<TestError>>());
246
247 assert_eq!(TestError2.service(), None);
248 assert_eq!(TestError2.signature(), "TestError2");
249
250 let err: Error<TestError> = TestError::Service("409 Error").into();
252 let msg = fmt_err_string(&err);
253 assert_eq!(msg, "InternalServiceError\n");
254 let msg = fmt_diag_string(&err);
255 assert!(msg.contains("err: InternalServiceError"));
256
257 let err: Failure = err.set_service("SVC").into();
258 assert_eq!(err.service(), Some("SVC"));
259 assert_eq!(err.signature(), "Service-Internal");
260 assert_eq!(err.as_diag().service(), Some("SVC"));
261 assert_eq!(err.as_diag().signature(), "Service-Internal");
262 assert!(err.backtrace().is_some());
263 assert!(err.as_diag().backtrace().is_some());
264
265 let res = Err(TestError::Service("409 Error"));
266 let res: Result<(), Error<TestError>> = res.into_error();
267 let _res: Result<(), Error<TestError2>> = res.into_error();
268
269 let msg = fmt_err_string(&err);
270 assert_eq!(msg, "InternalServiceError\n");
271
272 let err: Error<TestError> = TestError::Service("409 Error").into();
274 assert_eq!(err.get_item::<&str>(), None);
275 let err = err.insert_item("Test");
276 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
277 let err2 = err.clone();
278 assert_eq!(err2.get_item::<&str>(), Some(&"Test"));
279 let err2 = err2.insert_item("Test2");
280 assert_eq!(err2.get_item::<&str>(), Some(&"Test2"));
281 assert_eq!(err.get_item::<&str>(), Some(&"Test"));
282 let err2 = err.clone().map(|_| TestError::Disconnect);
283 assert_eq!(err2.get_item::<&str>(), Some(&"Test"));
284
285 let info = Failure::from(&err2);
286 assert_eq!(info.get_item::<&str>(), Some(&"Test"));
287
288 let err3 = err
289 .clone()
290 .try_map(|_| Err::<(), _>(TestError2))
291 .err()
292 .unwrap();
293 assert_eq!(err3.signature(), "TestError2");
294 assert_eq!(err3.get_item::<&str>(), Some(&"Test"));
295
296 let res = err.clone().try_map(|_| Ok::<_, TestError2>(()));
297 assert_eq!(res, Ok(()));
298 assert_eq!(format!("{Success}"), "Success");
299
300 let res = Ok::<_, TestError>(());
301 let info = ResultSignature::from(&res);
302 assert_eq!(info.signature(), "Success");
303
304 let res = Err::<(), _>(TestError::Service("409 Error"));
305 let info = ResultSignature::from(&res);
306 assert_eq!(info.signature(), "Service-Internal");
307 }
308}