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