1use std::any::{type_name};
2use std::backtrace::{Backtrace, BacktraceStatus};
3use std::fmt::{Debug, Display};
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8#[cfg(feature = "serde")]
9#[derive(Serialize, Deserialize)]
10pub struct Error<T> {
11 code: T,
12 msg: String,
13 #[serde(skip)]
14 source: Option<Box<(dyn std::error::Error + 'static + Send + Sync)>>,
15 #[serde(skip)]
16 backtrace: Option<Backtrace>,
17 file: Option<String>,
18 line: Option<u32>,
19}
20
21#[cfg(not(feature = "serde"))]
22pub struct Error<T> {
23 code: T,
24 msg: String,
25 source: Option<Box<(dyn std::error::Error + 'static + Send + Sync)>>,
26 backtrace: Option<Backtrace>,
27 file: Option<String>,
28 line: Option<u32>,
29}
30
31pub type Result<T, C> = std::result::Result<T, Error<C>>;
32
33impl<T: Debug + Copy + Sync + Send + 'static> Error<T> {
34 pub fn new(code: T, msg: String) -> Self {
35 #[cfg(feature = "backtrace")]
36 let backtrace = Some(Backtrace::force_capture());
37
38 #[cfg(not(feature = "backtrace"))]
39 let backtrace = None;
40
41 Self {
42 code,
43 msg,
44 source: None,
45 backtrace,
46 file: None,
47 line: None,
48 }
49 }
50
51 pub fn new2(code: T, msg: String, file: &str, line: u32) -> Self {
52 #[cfg(feature = "backtrace")]
53 let backtrace = Some(Backtrace::force_capture());
54
55 #[cfg(not(feature = "backtrace"))]
56 let backtrace = None;
57
58 Self {
59 code,
60 msg,
61 source: None,
62 backtrace,
63 file: Some(file.to_string()),
64 line: Some(line),
65 }
66 }
67
68 pub fn code(&self) -> T {
69 self.code
70 }
71
72 pub fn msg(&self) -> &str {
73 &self.msg
74 }
75
76 #[cfg(feature = "backtrace")]
77 pub fn backtrace(&self) -> Option<&Backtrace> {
78 self.backtrace.as_ref()
79 }
80}
81
82impl<T: Debug + Clone + Copy> std::error::Error for Error<T> {
83 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84 self.source.as_ref().map(|e| e.as_ref() as _)
85 }
86}
87
88impl<T: Debug> Debug for Error<T> {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 write!(f, "{}:{:?}", type_name::<T>(), self.code)?;
91
92 if self.file.is_some() && self.line.is_some() {
93 let file = self.file.as_ref().unwrap();
94 let file = if let Some(pos) = file.rfind("src") {
95 if pos == 0 {
96 file
97 } else if let Some(pos) = &file[..pos-1].rfind(&['/', '\\']) {
98 &file[pos+1..]
99 } else {
100 file
101 }
102 } else {
103 file
104 };
105 write!(f, " at:[{}:{}]", file, self.line.as_ref().unwrap())?;
106 }
107
108 if !self.msg.is_empty() {
109 write!(f, ", msg:{}", self.msg)?;
110 }
111 if let Some(backtrace) = &self.backtrace {
112 if let BacktraceStatus::Captured = backtrace.status() {
113 let mut backtrace = backtrace.to_string();
114 write!(f, "\n")?;
115 if backtrace.starts_with("stack backtrace:") {
116 backtrace.replace_range(0..1, "S");
118 } else {
119 writeln!(f, "Stack backtrace:")?;
122 }
123 backtrace.truncate(backtrace.trim_end().len());
124 write!(f, "{}", backtrace)?;
125 }
126 }
127 if self.source.is_some() {
128 write!(f, "\nCaused by: {:?}", self.source.as_ref().unwrap())?;
129 }
130 Ok(())
131 }
132}
133
134impl<T: Debug> Display for Error<T> {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 write!(f, "{}:{:?}", type_name::<T>(), self.code)?;
137
138 if self.file.is_some() && self.line.is_some() {
139 let file = self.file.as_ref().unwrap();
140 let file = if let Some(pos) = file.rfind("src") {
141 if pos == 0 {
142 file
143 } else if let Some(pos) = &file[..pos-1].rfind(&['/', '\\']) {
144 &file[pos+1..]
145 } else {
146 file
147 }
148 } else {
149 file
150 };
151 write!(f, " at:[{}:{}]", file, self.line.as_ref().unwrap())?;
152 }
153
154 if !self.msg.is_empty() {
155 write!(f, ", msg:{}", self.msg)?;
156 }
157 if let Some(backtrace) = &self.backtrace {
158 if let BacktraceStatus::Captured = backtrace.status() {
159 let mut backtrace = backtrace.to_string();
160 write!(f, "\n")?;
161 if backtrace.starts_with("stack backtrace:") {
162 backtrace.replace_range(0..1, "S");
164 } else {
165 writeln!(f, "Stack backtrace:")?;
168 }
169 backtrace.truncate(backtrace.trim_end().len());
170 write!(f, "{}", backtrace)?;
171 }
172 }
173 if self.source.is_some() {
174 write!(f, "\nCaused by: {:?}", self.source.as_ref().unwrap())?;
175 }
176 Ok(())
177 }
178}
179
180impl<T: Default> From<String> for Error<T> {
181 fn from(value: String) -> Self {
182 #[cfg(feature = "backtrace")]
183 let backtrace = Some(Backtrace::force_capture());
184
185 #[cfg(not(feature = "backtrace"))]
186 let backtrace = None;
187 Self {
188 code: Default::default(),
189 msg: value,
190 source: None,
191 backtrace,
192 file: None,
193 line: None,
194 }
195 }
196}
197
198impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, String, E)> for Error<T> {
199 fn from(value: (T, String, E)) -> Self {
200 #[cfg(feature = "backtrace")]
201 let backtrace = Some(Backtrace::force_capture());
202
203 #[cfg(not(feature = "backtrace"))]
204 let backtrace = None;
205
206 Self {
207 code: value.0,
208 msg: value.1,
209 source: Some(Box::new(value.2)),
210 backtrace,
211 file: None,
212 line: None,
213 }
214 }
215}
216
217impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, String, E, &str, u32)> for Error<T> {
218 fn from(value: (T, String, E, &str, u32)) -> Self {
219 #[cfg(feature = "backtrace")]
220 let backtrace = Some(Backtrace::force_capture());
221
222 #[cfg(not(feature = "backtrace"))]
223 let backtrace = None;
224
225 Self {
226 code: value.0,
227 msg: value.1,
228 source: Some(Box::new(value.2)),
229 backtrace,
230 file: Some(value.3.to_string()),
231 line: Some(value.4),
232 }
233 }
234}
235
236impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, &str, E, &str, u32)> for Error<T> {
237 fn from(value: (T, &str, E, &str, u32)) -> Self {
238 #[cfg(feature = "backtrace")]
239 let backtrace = Some(Backtrace::force_capture());
240
241 #[cfg(not(feature = "backtrace"))]
242 let backtrace = None;
243 Self {
244 code: value.0,
245 msg: value.1.to_string(),
246 source: Some(Box::new(value.2)),
247 backtrace,
248 file: Some(value.3.to_string()),
249 line: Some(value.4),
250 }
251 }
252}
253
254#[cfg(feature = "log")]
255pub use log::error as serror;
256
257#[cfg(feature = "log")]
258#[macro_export]
259macro_rules! error {
260 (target: $target:expr, $($arg:tt)+) => ($crate::serror!($target, $($arg)+));
263
264 ($($arg:tt)+) => ($crate::serror!($($arg)+))
266}
267#[cfg(not(feature = "log"))]
268#[macro_export]
269macro_rules! error {
270 (target: $target:expr, $($arg:tt)+) => ();
273
274 ($($arg:tt)+) => ()
276}
277
278#[macro_export]
279macro_rules! err {
280 ( $err: expr) => {
281 {
282 $crate::error!("{:?}", $err);
283 $crate::Error::new2($err, "".to_string(), file!(), line!())
284 }
285 };
286 ( $err: expr, $($arg:tt)*) => {
287 {
288 $crate::error!("{}", format!($($arg)*));
289 $crate::Error::new2($err, format!("{}", format!($($arg)*)), file!(), line!())
290 }
291 };
292}
293
294#[macro_export]
295macro_rules! into_err {
296 ($err: expr) => {
297 |e| {
298 $crate::error!("err:{:?}", e);
299 $crate::Error::from(($err, "".to_string(), e, file!(), line!()))
300 }
301 };
302 ($err: expr, $($arg:tt)*) => {
303 |e| {
304 $crate::error!("{} err:{:?}", format!($($arg)*), e);
305 $crate::Error::from(($err, format!($($arg)*), e, file!(), line!()))
306 }
307 };
308}
309
310#[cfg(test)]
311mod test {
312 #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
313 pub enum TestCode {
314 #[default]
315 Test1,
316 Test2,
317 }
318 pub type Error = super::Error<TestCode>;
319
320 #[test]
321 fn test() {
322 use crate as sfo_result;
323 let error = sfo_result::Error::new2(1, "test".to_string(), file!(), line!());
324 println!("{:?}", error);
325
326 let error = err!(1, "test");
327 println!("{:?}", error);
328
329 let error = Error::from((TestCode::Test1, "test".to_string(), error));
330 println!("{:?}", error);
331
332 }
335}