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