1#![doc = include_str!("../README.md")]
2
3use std::any::TypeId;
4use std::fmt::{Debug, Display, Formatter};
5use std::mem::ManuallyDrop;
6use std::ptr;
7
8pub use conerror_macro::conerror;
9
10pub type Result<T> = std::result::Result<T, Error>;
11
12pub struct Error(Box<Inner>);
14
15impl Error {
16 pub fn new<T>(
26 error: T,
27 file: &'static str,
28 line: u32,
29 func: &'static str,
30 module: &'static str,
31 ) -> Self
32 where
33 T: Into<Box<dyn std::error::Error + Send + Sync>>,
34 {
35 Self(Box::new(Inner {
36 source: error.into(),
37 location: Some(vec![Location {
38 file,
39 line,
40 func,
41 module,
42 }]),
43 context: Vec::new(),
44 }))
45 }
46
47 pub fn plain<T>(error: T) -> Self
49 where
50 T: Into<Box<dyn std::error::Error + Send + Sync>>,
51 {
52 Self(Box::new(Inner {
53 source: error.into(),
54 location: None,
55 context: Vec::new(),
56 }))
57 }
58
59 pub fn chain<T>(
72 error: T,
73 file: &'static str,
74 line: u32,
75 func: &'static str,
76 module: &'static str,
77 ) -> Self
78 where
79 T: std::error::Error + Send + Sync + 'static,
80 {
81 if TypeId::of::<T>() == TypeId::of::<Self>() {
82 let error = ManuallyDrop::new(error);
83 let mut error = unsafe { ptr::read(&error as *const _ as *const Self) };
85 if let Some(ref mut location) = error.0.location {
86 location.push(Location {
87 file,
88 line,
89 func,
90 module,
91 });
92 }
93 return error;
94 }
95
96 Self::new(error, file, line, func, module)
97 }
98
99 pub fn context(mut self, context: impl ToString) -> Self {
100 self.0.context.push(context.to_string());
101 self
102 }
103
104 pub fn location(&self) -> Option<&[Location]> {
106 self.0.location.as_ref().map(|v| v.as_slice())
107 }
108}
109
110impl Debug for Error {
111 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
112 f.debug_struct("Error")
113 .field("source", &self.0.source)
114 .field("location", &self.0.location)
115 .field("context", &self.0.context)
116 .finish()
117 }
118}
119
120impl Display for Error {
121 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
122 for c in self.0.context.iter().rev() {
123 write!(f, "{}: ", c)?;
124 }
125 Display::fmt(&self.0.source, f)?;
126 if let Some(ref location) = self.0.location {
127 for (i, v) in location.iter().enumerate() {
128 write!(f, "\n#{} {}", i, v)?;
129 }
130 }
131 Ok(())
132 }
133}
134
135impl std::error::Error for Error {
136 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
137 Some(&*self.0.source)
138 }
139}
140
141struct Inner {
142 source: Box<dyn std::error::Error + Send + Sync>,
143 location: Option<Vec<Location>>,
144 context: Vec<String>,
145}
146
147#[derive(Debug)]
149pub struct Location {
150 pub file: &'static str,
151 pub line: u32,
152 pub func: &'static str,
153 pub module: &'static str,
155}
156
157impl Display for Location {
158 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
159 write!(
160 f,
161 "{}:{} {}::{}()",
162 self.file, self.line, self.module, self.func
163 )
164 }
165}