1#![cfg_attr(nightly, feature(error_generic_member_access))]
2use std::{
3 any::Any,
4 borrow::Cow,
5 fmt::{self, Debug, Display},
6 str::FromStr,
7};
8
9use mdsn::DsnError;
10use source::Inner;
11use thiserror::Error;
12
13mod code;
14mod source;
15
16pub use code::Code;
17
18#[derive(Error)]
44#[must_use]
45pub struct Error {
46 code: Code,
48 context: Option<String>,
50 #[cfg_attr(nightly, backtrace)]
52 source: Inner,
53}
54
55unsafe impl Send for Error {}
56unsafe impl Sync for Error {}
57
58impl Debug for Error {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 if f.alternate() {
61 f.debug_struct("Error")
62 .field("code", &self.code)
63 .field("context", &self.context)
64 .field("source", &self.source)
65 .finish()
66 } else {
67 if self.code != Code::FAILED {
69 write!(f, "[{:#06X}] ", self.code)?;
70 }
71 if let Some(context) = &self.context {
72 f.write_fmt(format_args!("{}", context))?;
73 writeln!(f)?;
74 writeln!(f)?;
75 writeln!(f, "Caused by:")?;
76
77 let chain = self.source.chain();
78 for (idx, source) in chain.enumerate() {
79 writeln!(f, "{:4}: {}", idx, source)?;
80 }
81 } else {
82 let mut chain = self.source.chain();
83 if let Some(context) = chain.next() {
84 f.write_fmt(format_args!("{}", context))?;
85 }
86
87 if self.source.deep() {
88 writeln!(f)?;
89 writeln!(f)?;
90 writeln!(f, "Caused by:")?;
91 for (idx, source) in chain.enumerate() {
92 writeln!(f, "{:4}: {}", idx, source)?;
93 }
94 }
95 }
96 #[cfg(nightly)]
97 {
98 writeln!(f)?;
99 writeln!(f, "Backtrace:")?;
100 writeln!(f, "{}", self.source.backtrace())?;
101 }
102
103 Ok(())
104 }
105 }
106}
107
108impl Display for Error {
109 #[inline]
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 if self.code != Code::FAILED {
113 write!(f, "[{:#06X}] ", self.code)?;
114 }
115 if let Some(context) = self.context.as_deref() {
117 write!(f, "{}", context)?;
118
119 if self.source.is_empty() {
120 return Ok(());
121 }
122 f.write_str(": ")?;
124 } else if self.source.is_empty() {
125 return f.write_str("Unknown error");
126 }
127
128 if f.alternate() {
129 write!(f, "{:#}", self.source)?;
130 } else {
131 write!(f, "{}", self.source)?;
132 }
133 Ok(())
134 }
135}
136
137impl From<DsnError> for Error {
138 fn from(dsn: DsnError) -> Self {
139 Self::new(Code::FAILED, dsn.to_string())
140 }
141}
142
143impl From<anyhow::Error> for Error {
144 fn from(error: anyhow::Error) -> Self {
145 Self {
146 code: Code::FAILED,
147 context: None,
148 source: Inner::any(error),
149 }
150 }
151}
152
153impl<C: Into<Code>> From<C> for Error {
154 fn from(value: C) -> Self {
155 Self::from_code(value.into())
156 }
157}
158
159impl<'a> From<&'a str> for Error {
160 fn from(value: &'a str) -> Self {
161 Self::from_string(value.to_string())
162 }
163}
164
165pub type Result<T> = std::result::Result<T, Error>;
166
167impl Error {
168 #[inline(always)]
169 pub fn new_with_context(
170 code: impl Into<Code>,
171 err: impl Into<String>,
172 context: impl Into<String>,
173 ) -> Self {
174 Self {
175 code: code.into(),
176 context: Some(context.into()),
177 source: err.into().into(),
178 }
179 }
180 #[inline]
181 pub fn new(code: impl Into<Code>, err: impl Into<String>) -> Self {
182 Self {
183 code: code.into(),
184 context: None,
185 source: err.into().into(),
186 }
187 }
188
189 #[inline]
190 pub fn context(mut self, context: impl Into<String>) -> Self {
191 self.context = Some(match self.context {
192 Some(pre) => format!("{}: {}", context.into(), pre),
193 None => context.into(),
194 });
195 self
196 }
197
198 #[inline]
199 #[deprecated = "Use self.code() instead"]
200 pub fn errno(&self) -> Code {
201 self.code
202 }
203
204 #[inline]
205 pub const fn code(&self) -> Code {
206 self.code
207 }
208 #[inline]
209 pub fn message(&self) -> String {
210 self.source.to_string()
211 }
212
213 #[inline(always)]
214 pub fn from_code(code: impl Into<Code>) -> Self {
215 let code = code.into();
216 if let Some(str) = code._priv_err_str() {
217 Self::new(code, str)
218 } else {
219 Self {
220 code,
221 context: None,
222 source: Inner::empty(),
223 }
224 }
225 }
226
227 #[inline]
228 pub fn from_string(err: impl Into<Cow<'static, str>>) -> Self {
229 anyhow::format_err!("{}", err.into()).into()
230 }
231
232 #[inline]
233 pub fn from_any(err: impl Into<anyhow::Error>) -> Self {
234 err.into().into()
235 }
236
237 #[inline]
238 pub fn any(err: impl Into<anyhow::Error> + 'static) -> Self {
239 if err.type_id() == std::any::TypeId::of::<Self>() {
240 let err = &err as &dyn Any;
242 let err = err.downcast_ref::<Self>().unwrap();
243 dbg!(err);
244 return Self {
245 code: err.code,
246 context: err.context.clone(),
247 source: err.source.clone(),
248 };
249 }
250 err.into().into()
251 }
252
253 #[inline]
254 pub fn success(&self) -> bool {
255 self.code == 0
256 }
257
258 #[inline]
259 pub fn with_code(mut self, code: impl Into<Code>) -> Self {
260 self.code = code.into();
261 self
262 }
263}
264
265#[macro_export]
340macro_rules! format_err {
341 (code = $c:expr, raw = $arg:expr, context = $arg2:expr) => {
342 $crate::Error::new_with_context($c, $arg, $arg2)
343 };
344 (code = $c:expr, raw = $arg:expr) => {
345 $crate::Error::new($c, $arg)
346 };
347 (code = $c:expr, raw = ($($arg:tt)*), context = ($($arg2:tt)*) $(,)?) => {
348 $crate::Error::new_with_context($c, __priv_format!($($arg)*), __priv_format!($($arg2)*))
349 };
350 (code = $c:expr, context = $($arg2:tt)*) => {
351 $crate::Error::from($c).context(format!($($arg2)*))
352 };
353 (code = $c:expr, raw = $arg:literal, context = $($arg2:tt)*) => {
360 $crate::Error::new_with_context($c, format!($arg), $crate::__priv_format!($($arg2)*))
361 };
362 (code = $c:expr, raw = $arg:ident, context = $($arg2:tt)*) => {
363 $crate::Error::new_with_context($c, $arg, $crate::__priv_format!($($arg2)*))
364 };
365 (code = $c:expr) => {
366 $crate::Error::from_code($c)
367 };
368 (code = $c:expr, raw = $($arg:tt)*) => {
369 $crate::Error::new($c, format!($($arg)*))
370 };
371 (code = $c:expr, $($arg:tt)*) => {
372 $crate::Error::new($c, format!($($arg)*))
373 };
374 (any = $($arg:tt)*) => {
375 $crate::Error::from_string(format!($($arg)*))
376 };
377 (raw = $($arg:tt)*) => {
378 compile_error!("`raw` error message must be used along with an error code!")
379 };
380
381 ($c:expr, raw = $arg:expr) => {
382 $crate::Error::new($c, $arg)
383 };
384 ($c:expr, raw = $arg:expr, context = $arg2:expr) => {
385 $crate::Error::new_with_context($c, $arg, $arg2)
386 };
387 ($c:expr, raw = ($($arg:tt)*), context = ($($arg2:tt)*) $(,)?) => {
388 $crate::Error::new_with_context($c, format!($($arg)*), format!($($arg2)*))
389 };
390 ($c:expr, context = $arg:expr) => {
391 $crate::Error::from($c).context($arg)
392 };
393 ($c:expr, context = $($arg2:tt)*) => {
394 $crate::Error::from($c).context(format!($($arg2)*))
395 };
396 ($c:expr, raw = $($arg:tt)*) => {
397 $crate::Error::new($c, format!($($arg)*))
398 };
399 ($c:expr) => {
400 $crate::Error::from($c)
401 };
402 ($($arg:tt)*) => {
403 $crate::Error::from_string(format!($($arg)*))
404 };
405}
406#[macro_export]
407macro_rules! __priv_format {
408 ($msg:literal $(,)?) => {
409 literal.to_string()
410 };
411 ($err:expr $(,)?) => {
412 $err
413 };
414 ($fmt:expr, $($arg:tt)*) => {
415 format!($fmt, $($arg)*)
416 };
417}
418
419#[macro_export]
420macro_rules! bail {
421 ($($arg:tt)*) => {
422 return std::result::Result::Err($crate::format_err!($($arg)*))
423 };
424}
425
426impl FromStr for Error {
427 type Err = ();
428
429 #[inline]
430 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
431 Ok(Self::from_string(s.to_string()))
432 }
433}
434
435#[cfg(feature = "serde")]
436impl serde::de::Error for Error {
437 #[inline]
438 fn custom<T: fmt::Display>(msg: T) -> Error {
439 Error::from_string(format!("{}", msg))
440 }
441}
442
443#[test]
444fn test_format_err() {
445 let code = 0xF000;
446 let raw = "Nothing";
447 let context = "Error";
448 let err = dbg!(format_err!(code, raw = raw, context = context));
449 assert_eq!(err.to_string(), "[0xF000] Error: Internal error: `Nothing`");
450 let err = dbg!(format_err!(code, raw = raw));
451 assert_eq!(err.to_string(), "[0xF000] Internal error: `Nothing`");
452 let err = dbg!(format_err!(code, context = context));
453 assert_eq!(err.to_string(), "[0xF000] Error");
454
455 let err = dbg!(format_err!(code = 0xF000, context = "Error here"));
456 assert_eq!(err.to_string(), "[0xF000] Error here");
457 let err = dbg!(format_err!(0x6789, context = "Error here: {}", 1));
458 assert_eq!(err.to_string(), "[0x6789] Error here: 1");
459 let err = dbg!(format_err!(code = 0x6789, context = "Error here: {}", 1));
460 assert_eq!(err.to_string(), "[0x6789] Error here: 1");
461
462 let err = dbg!(format_err!(code = 0x6789, raw = "Error here: {}", 1));
463 assert_eq!(err.to_string(), "[0x6789] Internal error: `Error here: 1`");
464
465 let err = dbg!(format_err!(0x6789, raw = "Error here: {}", 1));
466 assert_eq!(err.to_string(), "[0x6789] Internal error: `Error here: 1`");
467
468 let err = dbg!(format_err!(
469 code = 0x6789,
470 raw = ("Error here: {}", 1),
471 context = ("Query error with {:?}", "sql"),
472 ));
473 assert_eq!(
474 err.to_string(),
475 "[0x6789] Query error with \"sql\": Internal error: `Error here: 1`"
476 );
477
478 let err = dbg!(format_err!("Error here"));
479 assert_eq!(err.to_string(), "Error here");
480
481 let err = dbg!(format_err!(0x2603));
482 assert_eq!(
483 err.to_string(),
484 "[0x2603] Internal error: `Table does not exist`"
485 );
486
487 let err = dbg!(format_err!(0x6789));
488 assert_eq!(err.to_string(), "[0x6789] Unknown error");
489
490 let err = dbg!(format_err!(0x6789, context = "Error here"));
491 assert_eq!(err.to_string(), "[0x6789] Error here");
492}
493
494#[test]
495fn test_bail() {
496 fn use_bail() -> Result<()> {
497 bail!(code = 0x2603, context = "Failed to insert into table `abc`");
498 }
499 let err = use_bail();
500 dbg!(&err);
501 assert!(err.is_err());
502 println!("{:?}", err.unwrap_err());
503
504 println!("{:?}", Error::any(use_bail().unwrap_err()));
505}
506
507#[test]
508fn test_display() {
509 let err = Error::new(Code::SUCCESS, "Success").context("nothing");
510 assert!(dbg!(format!("{}", err)).contains("[0x0000] nothing"));
511 let result = std::panic::catch_unwind(|| {
512 let err = Error::new(Code::SUCCESS, "Success").context("nothing");
513 panic!("{:?}", err);
514 });
515 assert!(result.is_err());
516}
517
518#[test]
519fn test_error() {
520 let err = Error::new(Code::SUCCESS, "success");
521 assert_eq!(err.code(), Code::SUCCESS);
522 assert_eq!(err.message(), "Internal error: `success`");
523
524 let _ = Error::from_code(1);
525 assert_eq!(Error::from_string("any").to_string(), "any");
526 assert_eq!(Error::from_string("any").to_string(), "any");
527
528 fn raise_error() -> Result<()> {
529 Err(Error::from_any(DsnError::InvalidDriver("mq".to_string())))
530 }
531 assert_eq!(raise_error().unwrap_err().to_string(), "invalid driver mq");
532}
533
534#[cfg(feature = "serde")]
535#[test]
536fn test_serde_error() {
537 use serde::de::Error as DeError;
538
539 let _ = Error::custom("");
540}