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