1use std::{error::Error as StdError, fmt, rc::Rc};
2
3use ntex_bytes::ByteString;
4
5use crate::{ErrorDiagnostic, ResultKind, ResultType};
6
7pub fn fmt_err_string(e: &dyn StdError) -> String {
8 let mut buf = String::new();
9 _ = fmt_err(&mut buf, e);
10 buf
11}
12
13pub fn fmt_err(f: &mut dyn fmt::Write, e: &dyn StdError) -> fmt::Result {
14 let mut current = Some(e);
15 while let Some(std_err) = current {
16 writeln!(f, "{std_err}")?;
17 current = std_err.source();
18 }
19 Ok(())
20}
21
22pub fn fmt_diag_string<K: ResultKind>(e: &dyn ErrorDiagnostic<Kind = K>) -> String {
23 let mut buf = String::new();
24 _ = fmt_diag(&mut buf, e);
25 buf
26}
27
28pub fn fmt_diag<K>(f: &mut dyn fmt::Write, e: &dyn ErrorDiagnostic<Kind = K>) -> fmt::Result
29where
30 K: ResultKind,
31{
32 let k = e.kind();
33 let tp = k.tp();
34
35 writeln!(f, "err: {e}")?;
36 writeln!(f, "type: {}", tp.as_str())?;
37 writeln!(f, "signature: {}", k.signature())?;
38
39 if let Some(svc) = e.service() {
40 writeln!(f, "service: {svc}")?;
41 }
42
43 writeln!(f, "\n{e:?}")?;
44
45 let mut current = e.source();
46 while let Some(err) = current {
47 writeln!(f, "{err:?}")?;
48 current = err.source();
49 }
50
51 if tp == ResultType::ServiceError
52 && let Some(bt) = e.backtrace()
53 {
54 writeln!(f, "{bt}")?;
55 }
56
57 Ok(())
58}
59
60#[derive(Clone, PartialEq, Eq, thiserror::Error)]
61pub struct ErrorMessage(ByteString);
62
63#[derive(Clone)]
64pub struct ErrorMessageChained {
65 msg: ByteString,
66 source: Option<Rc<dyn StdError>>,
67}
68
69impl ErrorMessageChained {
70 pub fn new<M, E>(ctx: M, source: E) -> Self
71 where
72 M: Into<ErrorMessage>,
73 E: StdError + 'static,
74 {
75 ErrorMessageChained {
76 msg: ctx.into().into_string(),
77 source: Some(Rc::new(source)),
78 }
79 }
80
81 pub const fn from_bstr(msg: ByteString) -> Self {
83 Self { msg, source: None }
84 }
85
86 pub fn msg(&self) -> &ByteString {
87 &self.msg
88 }
89}
90
91impl ErrorMessage {
92 pub const fn empty() -> Self {
94 Self(ByteString::from_static(""))
95 }
96
97 pub const fn from_bstr(msg: ByteString) -> ErrorMessage {
99 ErrorMessage(msg)
100 }
101
102 pub const fn from_static(msg: &'static str) -> Self {
104 ErrorMessage(ByteString::from_static(msg))
105 }
106
107 pub fn is_empty(&self) -> bool {
108 self.0.is_empty()
109 }
110
111 pub fn as_str(&self) -> &str {
112 &self.0
113 }
114
115 pub fn as_bstr(&self) -> &ByteString {
116 &self.0
117 }
118
119 pub fn into_string(self) -> ByteString {
120 self.0
121 }
122
123 pub fn with_source<E: StdError + 'static>(self, source: E) -> ErrorMessageChained {
124 ErrorMessageChained::new(self, source)
125 }
126}
127
128impl From<String> for ErrorMessage {
129 fn from(value: String) -> Self {
130 Self(ByteString::from(value))
131 }
132}
133
134impl From<ByteString> for ErrorMessage {
135 fn from(value: ByteString) -> Self {
136 Self(value)
137 }
138}
139
140impl From<&'static str> for ErrorMessage {
141 fn from(value: &'static str) -> Self {
142 Self(ByteString::from_static(value))
143 }
144}
145
146impl fmt::Debug for ErrorMessage {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 fmt::Display::fmt(&self.0, f)
149 }
150}
151
152impl fmt::Display for ErrorMessage {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 fmt::Display::fmt(&self.0, f)
155 }
156}
157
158impl From<ErrorMessage> for ByteString {
159 fn from(msg: ErrorMessage) -> Self {
160 msg.0
161 }
162}
163
164impl<'a> From<&'a ErrorMessage> for ByteString {
165 fn from(msg: &'a ErrorMessage) -> Self {
166 msg.0.clone()
167 }
168}
169
170impl<M: Into<ErrorMessage>> From<M> for ErrorMessageChained {
171 fn from(value: M) -> Self {
172 ErrorMessageChained {
173 msg: value.into().0,
174 source: None,
175 }
176 }
177}
178
179impl StdError for ErrorMessageChained {
180 fn source(&self) -> Option<&(dyn StdError + 'static)> {
181 self.source.as_ref().map(AsRef::as_ref)
182 }
183}
184
185impl fmt::Debug for ErrorMessageChained {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 fmt::Display::fmt(&self, f)
188 }
189}
190
191impl fmt::Display for ErrorMessageChained {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 if self.msg.is_empty() {
194 Ok(())
195 } else {
196 fmt::Display::fmt(&self.msg, f)
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use std::io;
204
205 use super::*;
206
207 #[test]
208 fn error_message() {
209 let msg = ErrorMessage::empty();
210 assert!(msg.is_empty());
211 assert_eq!(msg.as_str(), "");
212 assert_eq!(msg.as_bstr(), ByteString::new());
213 assert_eq!(ByteString::new(), msg.as_bstr());
214 assert_eq!(ByteString::new(), msg.into_string());
215
216 let msg = ErrorMessage::from("test");
217 assert!(!msg.is_empty());
218 assert_eq!(format!("{msg}"), "test");
219 assert_eq!(msg.as_str(), "test");
220 assert_eq!(msg.as_bstr(), ByteString::from("test"));
221
222 let msg = ErrorMessage::from("test".to_string());
223 assert!(!msg.is_empty());
224 assert_eq!(msg.as_str(), "test");
225 assert_eq!(msg.as_bstr(), ByteString::from("test"));
226
227 let msg = ErrorMessage::from_bstr(ByteString::from("test"));
228 assert!(!msg.is_empty());
229 assert_eq!(msg.as_str(), "test");
230 assert_eq!(msg.as_bstr(), ByteString::from("test"));
231
232 let msg = ErrorMessage::from(ByteString::from("test"));
233 assert!(!msg.is_empty());
234 assert_eq!(msg.as_str(), "test");
235 assert_eq!(msg.as_bstr(), ByteString::from("test"));
236
237 let msg = ErrorMessage::from_static("test");
238 assert!(!msg.is_empty());
239 assert_eq!(msg.as_str(), "test");
240 assert_eq!(msg.as_bstr(), ByteString::from("test"));
241
242 assert_eq!(ByteString::from(&msg), "test");
243 assert_eq!(ByteString::from(msg), "test");
244 }
245
246 #[test]
247 fn error_message_chained() {
248 let chained = ErrorMessageChained::from(ByteString::from("test"));
249 assert_eq!(chained.msg(), "test");
250 assert!(chained.source().is_none());
251
252 let chained = ErrorMessageChained::from_bstr(ByteString::from("test"));
253 assert_eq!(chained.msg(), "test");
254 assert!(chained.source().is_none());
255 assert_eq!(format!("{chained}"), "test");
256 assert_eq!(format!("{chained:?}"), "test");
257
258 let err = ErrorMessageChained::new("test", io::Error::other("io-test"));
259 let msg = fmt_err_string(&err);
260 assert_eq!(msg, "test\nio-test\n");
261 }
262}