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