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