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, file: &str, line: u32) -> 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: Some(file.to_string()),
47 line: Some(line),
48 }
49 }
50
51 pub fn code(&self) -> T {
52 self.code
53 }
54
55 pub fn msg(&self) -> &str {
56 &self.msg
57 }
58
59 #[cfg(feature = "backtrace")]
60 pub fn backtrace(&self) -> Option<&Backtrace> {
61 self.backtrace.as_ref()
62 }
63}
64
65impl<T: Debug + Clone + Copy> std::error::Error for Error<T> {
66 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
67 self.source.as_ref().map(|e| e.as_ref() as _)
68 }
69}
70
71impl<T: Debug> Debug for Error<T> {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 write!(f, "{}:{:?}", type_name::<T>(), self.code)?;
74
75 if self.file.is_some() && self.line.is_some() {
76 write!(f, " at:[{}:{}]", self.file.as_ref().unwrap(), self.line.as_ref().unwrap())?;
77 }
78
79 if !self.msg.is_empty() {
80 write!(f, ", msg:{}", self.msg)?;
81 }
82 if let Some(backtrace) = &self.backtrace {
83 if let BacktraceStatus::Captured = backtrace.status() {
84 let mut backtrace = backtrace.to_string();
85 write!(f, "\n")?;
86 if backtrace.starts_with("stack backtrace:") {
87 backtrace.replace_range(0..1, "S");
89 } else {
90 writeln!(f, "Stack backtrace:")?;
93 }
94 backtrace.truncate(backtrace.trim_end().len());
95 write!(f, "{}", backtrace)?;
96 }
97 }
98 if self.source.is_some() {
99 write!(f, "\nCaused by: {:?}", self.source.as_ref().unwrap())?;
100 }
101 Ok(())
102 }
103}
104
105impl<T: Debug> Display for Error<T> {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 write!(f, "{}:{:?}", type_name::<T>(), self.code)?;
108
109 if self.file.is_some() && self.line.is_some() {
110 write!(f, " at:[{}:{}]", self.file.as_ref().unwrap(), self.line.as_ref().unwrap())?;
111 }
112
113 if !self.msg.is_empty() {
114 write!(f, ", msg:{}", self.msg)?;
115 }
116 if let Some(backtrace) = &self.backtrace {
117 if let BacktraceStatus::Captured = backtrace.status() {
118 let mut backtrace = backtrace.to_string();
119 write!(f, "\n")?;
120 if backtrace.starts_with("stack backtrace:") {
121 backtrace.replace_range(0..1, "S");
123 } else {
124 writeln!(f, "Stack backtrace:")?;
127 }
128 backtrace.truncate(backtrace.trim_end().len());
129 write!(f, "{}", backtrace)?;
130 }
131 }
132 if self.source.is_some() {
133 write!(f, "\nCaused by: {:?}", self.source.as_ref().unwrap())?;
134 }
135 Ok(())
136 }
137}
138
139impl<T: Default> From<String> for Error<T> {
140 fn from(value: String) -> Self {
141 #[cfg(feature = "backtrace")]
142 let backtrace = Some(Backtrace::force_capture());
143
144 #[cfg(not(feature = "backtrace"))]
145 let backtrace = None;
146 Self {
147 code: Default::default(),
148 msg: value,
149 source: None,
150 backtrace,
151 file: None,
152 line: None,
153 }
154 }
155}
156
157impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, String, E)> for Error<T> {
158 fn from(value: (T, String, E)) -> Self {
159 #[cfg(feature = "backtrace")]
160 let backtrace = Some(Backtrace::force_capture());
161
162 #[cfg(not(feature = "backtrace"))]
163 let backtrace = None;
164
165 Self {
166 code: value.0,
167 msg: value.1,
168 source: Some(Box::new(value.2)),
169 backtrace,
170 file: None,
171 line: None,
172 }
173 }
174}
175
176impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, String, E, &str, u32)> for Error<T> {
177 fn from(value: (T, String, E, &str, u32)) -> Self {
178 #[cfg(feature = "backtrace")]
179 let backtrace = Some(Backtrace::force_capture());
180
181 #[cfg(not(feature = "backtrace"))]
182 let backtrace = None;
183
184 Self {
185 code: value.0,
186 msg: value.1,
187 source: Some(Box::new(value.2)),
188 backtrace,
189 file: Some(value.3.to_string()),
190 line: Some(value.4),
191 }
192 }
193}
194
195impl<T, E: std::error::Error + 'static + Send + Sync> From<(T, &str, E, &str, u32)> for Error<T> {
196 fn from(value: (T, &str, E, &str, u32)) -> Self {
197 #[cfg(feature = "backtrace")]
198 let backtrace = Some(Backtrace::force_capture());
199
200 #[cfg(not(feature = "backtrace"))]
201 let backtrace = None;
202 Self {
203 code: value.0,
204 msg: value.1.to_string(),
205 source: Some(Box::new(value.2)),
206 backtrace,
207 file: Some(value.3.to_string()),
208 line: Some(value.4),
209 }
210 }
211}
212
213#[cfg(feature = "log")]
214pub use log::error as serror;
215
216#[cfg(feature = "log")]
217#[macro_export]
218macro_rules! error {
219 (target: $target:expr, $($arg:tt)+) => ($crate::serror!($target, $($arg)+));
222
223 ($($arg:tt)+) => ($crate::serror!($($arg)+))
225}
226#[cfg(not(feature = "log"))]
227#[macro_export]
228macro_rules! error {
229 (target: $target:expr, $($arg:tt)+) => ();
232
233 ($($arg:tt)+) => ()
235}
236
237#[macro_export]
238macro_rules! err {
239 ( $err: expr) => {
240 {
241 $crate::error!("{:?}", $err);
242 $crate::Error::new($err, "".to_string(), file!(), line!())
243 }
244 };
245 ( $err: expr, $($arg:tt)*) => {
246 {
247 $crate::error!("{}", format!($($arg)*));
248 $crate::Error::new($err, format!("{}", format!($($arg)*)), file!(), line!())
249 }
250 };
251}
252
253#[macro_export]
254macro_rules! into_err {
255 ($err: expr) => {
256 |e| {
257 $crate::error!("err:{:?}", e);
258 $crate::Error::from(($err, "".to_string(), e, file!(), line!()))
259 }
260 };
261 ($err: expr, $($arg:tt)*) => {
262 |e| {
263 $crate::error!("{} err:{:?}", format!($($arg)*), e);
264 $crate::Error::from(($err, format!($($arg)*), e, file!(), line!()))
265 }
266 };
267}
268
269#[cfg(test)]
270mod test {
271 #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
272 pub enum TestCode {
273 #[default]
274 Test1,
275 Test2,
276 }
277 pub type Error = super::Error<TestCode>;
278
279 #[test]
280 fn test() {
281 use crate as sfo_result;
282 let error = sfo_result::Error::new(1, "test".to_string(), file!(), line!());
283 println!("{:?}", error);
284
285 let error = err!(1, "test");
286 println!("{:?}", error);
287
288 let error = Error::from((TestCode::Test1, "test".to_string(), error));
289 println!("{:?}", error);
290
291 }
294}