1use std::{error, fmt, fmt::Write, rc::Rc};
2
3use ntex_bytes::ByteString;
4
5use crate::{AsError, ErrorDiagnostic, ResultType};
6
7struct Wrt<'a> {
8 written: usize,
9 fmt: &'a mut dyn fmt::Write,
10}
11
12impl<'a> Wrt<'a> {
13 fn new(fmt: &'a mut dyn fmt::Write) -> Self {
14 Wrt { fmt, written: 0 }
15 }
16
17 fn wrote(&mut self) -> bool {
18 let res = self.written != 0;
19 self.written = 0;
20 res
21 }
22}
23
24impl fmt::Write for Wrt<'_> {
25 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
26 self.written += s.len();
27 self.fmt.write_str(s)
28 }
29
30 fn write_char(&mut self, c: char) -> Result<(), fmt::Error> {
31 self.written += 1;
32 self.fmt.write_char(c)
33 }
34}
35
36pub fn fmt_err_string(e: &dyn error::Error) -> String {
37 let mut buf = String::new();
38 _ = fmt_err(&mut buf, e);
39 buf
40}
41
42pub fn fmt_err(f: &mut dyn fmt::Write, e: &dyn error::Error) -> fmt::Result {
43 let mut wrt = Wrt::new(f);
44 let mut current = Some(e);
45 while let Some(std_err) = current {
46 write!(&mut wrt, "{std_err}")?;
47 if wrt.wrote() {
48 writeln!(wrt.fmt)?;
49 }
50 current = std_err.source();
51 }
52 Ok(())
53}
54
55pub fn fmt_diag_string<'a, T>(e: &'a T) -> String
57where
58 T: ErrorDiagnostic + AsError,
59 ResultType: From<&'a T::Target>,
60{
61 let mut buf = String::new();
62 _ = fmt_diag(&mut buf, e);
63 buf
64}
65
66pub fn fmt_diag<'a, T>(f: &mut dyn fmt::Write, container: &'a T) -> fmt::Result
71where
72 T: ErrorDiagnostic + AsError,
73 ResultType: From<&'a T::Target>,
74{
75 let e = container.as_diag();
76 let tp = ResultType::from(e);
77
78 writeln!(f, "err: {e}")?;
79 writeln!(f, "type: {}", tp.as_str())?;
80 writeln!(f, "signature: {}", container.signature())?;
81
82 if let Some(tag) = container.tag() {
83 if let Ok(s) = ByteString::try_from(tag) {
84 writeln!(f, "tag: {s}")?;
85 } else {
86 writeln!(f, "tag: {tag:?}")?;
87 }
88 }
89 if let Some(svc) = container.service() {
90 writeln!(f, "service: {svc}")?;
91 }
92 writeln!(f)?;
93
94 let mut wrt = Wrt::new(f);
95 write!(&mut wrt, "{e:?}")?;
96 if wrt.wrote() {
97 writeln!(wrt.fmt)?;
98 }
99
100 let mut current = container.source();
101 while let Some(err) = current {
102 write!(&mut wrt, "{err:?}")?;
103 if wrt.wrote() {
104 writeln!(wrt.fmt)?;
105 }
106 current = err.source();
107 }
108
109 if tp == ResultType::ServiceError
110 && let Some(bt) = container.backtrace()
111 && let Some(repr) = bt.repr()
112 {
113 writeln!(wrt.fmt, "{repr}")?;
114 }
115
116 Ok(())
117}
118
119#[derive(Clone, PartialEq, Eq, thiserror::Error)]
120pub struct ErrorMessage(ByteString);
121
122#[derive(Clone)]
123pub struct ErrorMessageChained {
124 msg: ByteString,
125 source: Option<Rc<dyn error::Error>>,
126}
127
128impl ErrorMessageChained {
129 pub fn new<M, E>(ctx: M, source: E) -> Self
130 where
131 M: Into<ErrorMessage>,
132 E: error::Error + 'static,
133 {
134 ErrorMessageChained {
135 msg: ctx.into().into_string(),
136 source: Some(Rc::new(source)),
137 }
138 }
139
140 pub const fn from_bstr(msg: ByteString) -> Self {
142 Self { msg, source: None }
143 }
144
145 pub fn msg(&self) -> &ByteString {
146 &self.msg
147 }
148}
149
150impl ErrorMessage {
151 pub const fn empty() -> Self {
153 Self(ByteString::from_static(""))
154 }
155
156 pub const fn from_bstr(msg: ByteString) -> ErrorMessage {
158 ErrorMessage(msg)
159 }
160
161 pub const fn from_static(msg: &'static str) -> Self {
163 ErrorMessage(ByteString::from_static(msg))
164 }
165
166 pub fn is_empty(&self) -> bool {
167 self.0.is_empty()
168 }
169
170 pub fn as_str(&self) -> &str {
171 &self.0
172 }
173
174 pub fn as_bstr(&self) -> &ByteString {
175 &self.0
176 }
177
178 pub fn into_string(self) -> ByteString {
179 self.0
180 }
181
182 pub fn with_source<E: error::Error + 'static>(self, source: E) -> ErrorMessageChained {
183 ErrorMessageChained::new(self, source)
184 }
185}
186
187impl From<String> for ErrorMessage {
188 fn from(value: String) -> Self {
189 Self(ByteString::from(value))
190 }
191}
192
193impl From<ByteString> for ErrorMessage {
194 fn from(value: ByteString) -> Self {
195 Self(value)
196 }
197}
198
199impl From<&'static str> for ErrorMessage {
200 fn from(value: &'static str) -> Self {
201 Self(ByteString::from_static(value))
202 }
203}
204
205impl fmt::Debug for ErrorMessage {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 fmt::Display::fmt(&self.0, f)
208 }
209}
210
211impl fmt::Display for ErrorMessage {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 fmt::Display::fmt(&self.0, f)
214 }
215}
216
217impl From<ErrorMessage> for ByteString {
218 fn from(msg: ErrorMessage) -> Self {
219 msg.0
220 }
221}
222
223impl<'a> From<&'a ErrorMessage> for ByteString {
224 fn from(msg: &'a ErrorMessage) -> Self {
225 msg.0.clone()
226 }
227}
228
229impl<M: Into<ErrorMessage>> From<M> for ErrorMessageChained {
230 fn from(value: M) -> Self {
231 ErrorMessageChained {
232 msg: value.into().0,
233 source: None,
234 }
235 }
236}
237
238impl error::Error for ErrorMessageChained {
239 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
240 self.source.as_ref().map(AsRef::as_ref)
241 }
242}
243
244impl fmt::Debug for ErrorMessageChained {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 fmt::Display::fmt(&self, f)
247 }
248}
249
250impl fmt::Display for ErrorMessageChained {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 if self.msg.is_empty() {
253 Ok(())
254 } else {
255 fmt::Display::fmt(&self.msg, f)
256 }
257 }
258}
259
260#[cfg(test)]
261#[allow(dead_code)]
262mod tests {
263 use ntex_bytes::Bytes;
264 use std::{error::Error, io};
265
266 use super::*;
267
268 #[test]
269 fn error_message() {
270 let msg = ErrorMessage::empty();
271 assert!(msg.is_empty());
272 assert_eq!(msg.as_str(), "");
273 assert_eq!(msg.as_bstr(), ByteString::new());
274 assert_eq!(ByteString::new(), msg.as_bstr());
275 assert_eq!(ByteString::new(), msg.into_string());
276
277 let msg = ErrorMessage::from("test");
278 assert!(!msg.is_empty());
279 assert_eq!(format!("{msg}"), "test");
280 assert_eq!(format!("{msg:?}"), "test");
281 assert_eq!(msg.as_str(), "test");
282 assert_eq!(msg.as_bstr(), ByteString::from("test"));
283
284 let msg = ErrorMessage::from("test".to_string());
285 assert!(!msg.is_empty());
286 assert_eq!(msg.as_str(), "test");
287 assert_eq!(msg.as_bstr(), ByteString::from("test"));
288
289 let msg = ErrorMessage::from_bstr(ByteString::from("test"));
290 assert!(!msg.is_empty());
291 assert_eq!(msg.as_str(), "test");
292 assert_eq!(msg.as_bstr(), ByteString::from("test"));
293
294 let msg = ErrorMessage::from(ByteString::from("test"));
295 assert!(!msg.is_empty());
296 assert_eq!(msg.as_str(), "test");
297 assert_eq!(msg.as_bstr(), ByteString::from("test"));
298
299 let msg = ErrorMessage::from_static("test");
300 assert!(!msg.is_empty());
301 assert_eq!(msg.as_str(), "test");
302 assert_eq!(msg.as_bstr(), ByteString::from("test"));
303
304 assert_eq!(ByteString::from(&msg), "test");
305 assert_eq!(ByteString::from(msg), "test");
306 }
307
308 #[test]
309 fn error_message_chained() {
310 let chained = ErrorMessageChained::from(ByteString::from("test"));
311 assert_eq!(chained.msg(), "test");
312 assert!(chained.source().is_none());
313
314 let chained = ErrorMessageChained::from_bstr(ByteString::from("test"));
315 assert_eq!(chained.msg(), "test");
316 assert!(chained.source().is_none());
317 assert_eq!(format!("{chained}"), "test");
318 assert_eq!(format!("{chained:?}"), "test");
319
320 let msg = ErrorMessage::from(ByteString::from("test"));
321 let chained = msg.with_source(io::Error::other("io-test"));
322 assert_eq!(chained.msg(), "test");
323 assert!(chained.source().is_some());
324
325 let err = ErrorMessageChained::new("test", io::Error::other("io-test"));
326 let msg = fmt_err_string(&err);
327 assert_eq!(msg, "test\nio-test\n");
328
329 let chained = ErrorMessageChained::from(ByteString::new());
330 assert_eq!(format!("{chained}"), "");
331 }
332
333 #[derive(thiserror::Error, derive_more::Debug)]
334 enum TestError {
335 #[error("Disconnect")]
336 #[debug("")]
337 Disconnect(#[source] io::Error),
338 #[error("InternalServiceError")]
339 #[debug("InternalServiceError {_0}")]
340 Service(&'static str),
341 }
342
343 impl Clone for TestError {
344 fn clone(&self) -> Self {
345 panic!()
346 }
347 }
348
349 impl ErrorDiagnostic for TestError {
350 fn signature(&self) -> &'static str {
351 match self {
352 TestError::Service(_) => ResultType::ServiceError.as_str(),
353 TestError::Disconnect(_) => ResultType::ClientError.as_str(),
354 }
355 }
356 }
357
358 impl From<&TestError> for ResultType {
359 fn from(err: &TestError) -> ResultType {
360 match err {
361 TestError::Service(_) => ResultType::ServiceError,
362 TestError::Disconnect(_) => ResultType::ClientError,
363 }
364 }
365 }
366
367 #[test]
368 fn fmt_diag() {
369 let err = TestError::Service("409 Error");
370
371 let msg = fmt_err_string(&err);
372 assert_eq!(msg, "InternalServiceError\n");
373
374 let err =
375 crate::Error::from(TestError::Disconnect(io::Error::other("Test io error")));
376 if let Some(bt) = err.backtrace() {
377 bt.resolver().resolve();
378 }
379 let msg = fmt_diag_string(&err);
380 assert!(msg.contains("Test io error"), "{msg}");
381
382 assert!(
383 format!("{:?}", err.source()).contains("Test io erro"),
384 "{:?}",
385 err.source().unwrap()
386 );
387
388 let err = err.set_tag(Bytes::from("test-tag"));
389 let msg = fmt_diag_string(&err);
390 assert!(msg.contains("test-tag"), "{msg}");
391 }
392}