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 if type_name::<T>() != "()" {
91 write!(f, "{}:{:?} ", type_name::<T>(), self.code)?;
92 }
93
94 if self.file.is_some() && self.line.is_some() {
95 let file = self.file.as_ref().unwrap();
96 let file = if let Some(pos) = file.rfind("src") {
97 if pos == 0 {
98 file
99 } else if let Some(pos) = &file[..pos-1].rfind(&['/', '\\']) {
100 &file[pos+1..]
101 } else {
102 file
103 }
104 } else {
105 file
106 };
107 write!(f, "at:[{}:{}] ", file, self.line.as_ref().unwrap())?;
108 }
109
110 if !self.msg.is_empty() {
111 write!(f, "{}", self.msg)?;
112 }
113 if let Some(backtrace) = &self.backtrace {
114 if let BacktraceStatus::Captured = backtrace.status() {
115 let mut backtrace = backtrace.to_string();
116 write!(f, "\n")?;
117 if backtrace.starts_with("stack backtrace:") {
118 backtrace.replace_range(0..1, "S");
120 } else {
121 writeln!(f, "Stack backtrace:")?;
124 }
125 backtrace.truncate(backtrace.trim_end().len());
126 write!(f, "{}", backtrace)?;
127 }
128 }
129 if self.source.is_some() {
130 write!(f, "\nCaused by: {:?}", self.source.as_ref().unwrap())?;
131 }
132 Ok(())
133 }
134}
135
136impl<T: Debug> Display for Error<T> {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 if type_name::<T>() != "()" {
139 write!(f, "{}:{:?} ", type_name::<T>(), self.code)?;
140 }
141
142 if self.file.is_some() && self.line.is_some() {
143 let file = self.file.as_ref().unwrap();
144 let file = if let Some(pos) = file.rfind("src") {
145 if pos == 0 {
146 file
147 } else if let Some(pos) = &file[..pos-1].rfind(&['/', '\\']) {
148 &file[pos+1..]
149 } else {
150 file
151 }
152 } else {
153 file
154 };
155 write!(f, "at:[{}:{}] ", file, self.line.as_ref().unwrap())?;
156 }
157
158 if !self.msg.is_empty() {
159 write!(f, "{}", self.msg)?;
160 }
161 if let Some(backtrace) = &self.backtrace {
162 if let BacktraceStatus::Captured = backtrace.status() {
163 let mut backtrace = backtrace.to_string();
164 write!(f, "\n")?;
165 if backtrace.starts_with("stack backtrace:") {
166 backtrace.replace_range(0..1, "S");
168 } else {
169 writeln!(f, "Stack backtrace:")?;
172 }
173 backtrace.truncate(backtrace.trim_end().len());
174 write!(f, "{}", backtrace)?;
175 }
176 }
177 if self.source.is_some() {
178 write!(f, "\nCaused by: {:?}", self.source.as_ref().unwrap())?;
179 }
180 Ok(())
181 }
182}
183
184impl<T: Default> From<String> for Error<T> {
185 fn from(value: String) -> Self {
186 #[cfg(feature = "backtrace")]
187 let backtrace = Some(Backtrace::force_capture());
188
189 #[cfg(not(feature = "backtrace"))]
190 let backtrace = None;
191 Self {
192 code: Default::default(),
193 msg: value,
194 source: None,
195 backtrace,
196 file: None,
197 line: None,
198 }
199 }
200}
201
202impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, String, E)> for Error<T> {
203 fn from(value: (T, String, E)) -> Self {
204 #[cfg(feature = "backtrace")]
205 let backtrace = Some(Backtrace::force_capture());
206
207 #[cfg(not(feature = "backtrace"))]
208 let backtrace = None;
209
210 Self {
211 code: value.0,
212 msg: value.1,
213 source: Some(Box::new(value.2)),
214 backtrace,
215 file: None,
216 line: None,
217 }
218 }
219}
220
221impl<T: Default, E: std::error::Error + 'static + Send + Sync> From<(String, E)> for Error<T> {
222 fn from(value: (String, E)) -> Self {
223 #[cfg(feature = "backtrace")]
224 let backtrace = Some(Backtrace::force_capture());
225
226 #[cfg(not(feature = "backtrace"))]
227 let backtrace = None;
228
229 Self {
230 code: Default::default(),
231 msg: value.0,
232 source: Some(Box::new(value.1)),
233 backtrace,
234 file: None,
235 line: None,
236 }
237 }
238}
239
240impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, String, E, &str, u32)> for Error<T> {
241 fn from(value: (T, String, E, &str, u32)) -> Self {
242 #[cfg(feature = "backtrace")]
243 let backtrace = Some(Backtrace::force_capture());
244
245 #[cfg(not(feature = "backtrace"))]
246 let backtrace = None;
247
248 Self {
249 code: value.0,
250 msg: value.1,
251 source: Some(Box::new(value.2)),
252 backtrace,
253 file: Some(value.3.to_string()),
254 line: Some(value.4),
255 }
256 }
257}
258
259impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, &str, E, &str, u32)> for Error<T> {
260 fn from(value: (T, &str, E, &str, u32)) -> Self {
261 #[cfg(feature = "backtrace")]
262 let backtrace = Some(Backtrace::force_capture());
263
264 #[cfg(not(feature = "backtrace"))]
265 let backtrace = None;
266 Self {
267 code: value.0,
268 msg: value.1.to_string(),
269 source: Some(Box::new(value.2)),
270 backtrace,
271 file: Some(value.3.to_string()),
272 line: Some(value.4),
273 }
274 }
275}
276
277#[cfg(feature = "log")]
278pub use log::error as serror;
279
280#[cfg(feature = "log")]
281#[macro_export]
282macro_rules! error {
283 (target: $target:expr, $($arg:tt)+) => ($crate::serror!($target, $($arg)+));
286
287 ($($arg:tt)+) => ($crate::serror!($($arg)+))
289}
290#[cfg(not(feature = "log"))]
291#[macro_export]
292macro_rules! error {
293 (target: $target:expr, $($arg:tt)+) => ();
296
297 ($($arg:tt)+) => ()
299}
300
301#[macro_export]
302macro_rules! err {
303 ( $fmt:literal ) => {{
304 $crate::error!($fmt);
305 $crate::Error::new2(
306 ::std::default::Default::default(),
307 format!($fmt),
308 file!(),
309 line!()
310 )
311 }};
312 ( $fmt:literal, $($arg:tt)* ) => {{
313 $crate::error!($fmt, $($arg)*);
314 $crate::Error::new2(
315 ::std::default::Default::default(),
316 format!($fmt, $($arg)*),
317 file!(),
318 line!()
319 )
320 }};
321 ( $err: path) => {
322 {
323 $crate::error!("{:?}", $err);
324 $crate::Error::new2($err, "".to_string(), file!(), line!())
325 }
326 };
327 ( $err:path, $($arg:tt)*) => {{
328 $crate::error!($($arg)*);
329 $crate::Error::new2($err, format!($($arg)*), file!(), line!())
330 }};
331}
332
333#[macro_export]
334macro_rules! into_err {
335 ( $fmt:literal ) => {
336 |e| {
337 $crate::error!("{} err:{:?}", format!($fmt), e);
338 let msg = format!($fmt);
339 $crate::Error::from((
340 ::std::default::Default::default(),
341 msg,
342 e,
343 file!(),
344 line!()))
345 }
346 };
347 ( $fmt:literal, $($arg:tt)* ) => {
348 |e| {
349 $crate::error!("{} err:{:?}", format!($fmt, $($arg)*), e);
350 let msg = format!($fmt, $($arg)*);
351 $crate::Error::from((
352 ::std::default::Default::default(),
353 msg,
354 e,
355 file!(),
356 line!()))
357 }
358 };
359 ($err: path) => {
360 |e| {
361 $crate::error!("err:{:?}", e);
362 $crate::Error::from(($err, "".to_string(), e, file!(), line!()))
363 }
364 };
365 ($err: path, $($arg:tt)*) => {
366 |e| {
367 $crate::error!("{} err:{:?}", format!($($arg)*), e);
368 $crate::Error::from(($err, format!($($arg)*), e, file!(), line!()))
369 }
370 };
371}
372
373#[cfg(test)]
374mod test {
375 use crate::test::TestCode::Test1;
376
377 #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
378 pub enum TestCode {
379 #[default]
380 Test1,
381 Test2,
382 }
383 pub type Error = super::Error<TestCode>;
384 pub type TestResult<T> = super::Result<T, TestCode>;
385 pub type TestResult2<T> = super::Result<T, ()>;
386
387 fn test2() -> TestResult2<()> {
388 Err(err!("test {}", 2))
390 }
391
392 #[derive(Debug)]
393 struct Test {
394 name: String,
395 }
396
397 impl Test {
398 pub fn new(name: String) -> Self {
399 Self { name }
400 }
401
402 pub fn test(&self) -> TestResult<()> {
403 let error = err!("test {:?}", self.name);
404 println!("{:?}", error);
405 Err(error)
406 }
407 }
408
409 #[test]
410 fn test() {
411 use crate as sfo_result;
412 let error = sfo_result::Error::new2(TestCode::Test2, "test".to_string(), file!(), line!());
413 println!("{:?}", error);
414
415 let error = err!(Test1, "test");
416 println!("{:?}", error);
417
418 let error = Error::from((TestCode::Test1, "test".to_string(), error));
419 println!("{:?}", error);
420
421 let error: Error = err!("test {}", 1);
422 println!("{:?}", error);
423 let error: Error = err!("test");
424 println!("{:?}", error);
425 let error: super::Error<()> = err!("test {}", 1);
426 println!("{:?}", error);
427
428 let ret: TestResult<()> = test2().map_err(into_err!(Test1, "test333"));
429 println!("{:?}", ret);
430 let ret: TestResult<()> = test2().map_err(into_err!(TestCode::Test1, "test333 {val}", val=1+1));
431 println!("{:?}", ret);
432 let t = "test333".to_string();
433 let ret: TestResult<()> = test2().map_err(into_err!("test333 {v}", v=t));
434 println!("{:?}", ret);
435 let test = Test::new("test".to_string());
436 let ret: TestResult<()> = test.test().map_err(into_err!("test333 {}", 1));
437 println!("{:?}", ret);
438 let ret: TestResult<()> = test.test().map_err(into_err!("test333"));
439 println!("{:?}", ret);
440 }
443}