1use anyhow::Error as AnyhowError;
2use serde_json::Value;
3use std::fmt;
4
5#[derive(Debug, serde::Serialize, serde::Deserialize)]
7pub struct AppError {
8 pub code: String,
10 pub message: String,
12 pub message_detail: Option<String>,
14 pub context: Value,
16 #[serde(skip_serializing, skip_deserializing)]
18 pub source: Option<AnyhowError>,
19}
20
21impl AppError {
22 pub fn new(code: &str, message: &str) -> Self {
24 Self {
25 code: code.to_string(),
26 message: message.to_string(),
27 message_detail: None,
28 context: Value::Object(serde_json::Map::new()),
29 source: None,
30 }
31 }
32
33 pub fn code(&self) -> &str {
35 &self.code
36 }
37
38 pub fn message(&self) -> &str {
40 &self.message
41 }
42
43 pub fn message_detail(&self) -> Option<&str> {
45 self.message_detail.as_deref()
46 }
47
48 pub fn with_message_detail(mut self, detail: &str) -> Self {
50 self.message_detail = Some(detail.to_string());
51 self
52 }
53
54 pub fn with_attribute(mut self, key: &str, value: impl Into<Value>) -> Self {
56 if let Value::Object(ref mut map) = self.context {
57 map.insert(key.to_string(), value.into());
58 }
59 self
60 }
61
62 pub fn get_attribute(&self, key: &str) -> Option<&Value> {
64 self.context.get(key)
65 }
66
67 pub fn source(&self) -> Option<&AnyhowError> {
69 self.source.as_ref()
70 }
71
72 pub fn with_context(mut self, context: Value) -> Self {
74 self.context = context;
75 self
76 }
77
78 pub fn with_source(mut self, source: AnyhowError) -> Self {
80 self.source = Some(source);
81 self
82 }
83
84 pub fn from_error<E: std::error::Error + Send + Sync + 'static>(
86 code: &str,
87 message: &str,
88 err: E,
89 ) -> Self {
90 Self {
91 code: code.to_string(),
92 message: message.to_string(),
93 message_detail: None,
94 context: Value::Object(serde_json::Map::new()),
95 source: Some(AnyhowError::new(err)),
96 }
97 }
98
99 pub fn from_anyhow(code: &str, message: &str, err: AnyhowError) -> Self {
101 Self::new(code, message).with_source(err)
102 }
103
104 pub fn from_display<E: std::fmt::Display>(code: &str, message: &str, err: E) -> Self {
106 Self::new(code, message).with_message_detail(&err.to_string())
107 }
108
109 pub fn from_display_with_source<E: std::fmt::Display + std::error::Error + Send + Sync + 'static>(
111 code: &str,
112 message: &str,
113 err: E,
114 ) -> Self {
115 Self::new(code, message)
116 .with_message_detail(&err.to_string())
117 .with_source(AnyhowError::new(err))
118 }
119
120 pub fn get_cause(&self) -> Option<String> {
122 self.source.as_ref().map(|err| {
123 let mut trace = String::new();
124
125 trace.push_str(&format!("Error: {}\n", err));
127
128 let mut error: &dyn std::error::Error = err.as_ref();
130 let mut depth = 0;
131 while let Some(source) = error.source() {
132 depth += 1;
133 trace.push_str(&format!("CausedBy ({}): {}\n", depth, source));
134 error = source;
135 }
136
137 let backtrace = err.backtrace();
139 if backtrace.status() == std::backtrace::BacktraceStatus::Captured {
140 trace.push_str(&format!("Backtrace:\n{}", backtrace));
141 }
142
143 trace
144 })
145 }
146}
147
148impl fmt::Display for AppError {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 write!(f, "AppError [{}] {}", self.code, self.message)?;
152
153 if let Some(detail) = &self.message_detail {
155 write!(f, "\n\tMessageDetail: {}", detail)?;
156 }
157
158 if let Value::Object(map) = &self.context && !map.is_empty() {
160 let _ = write!(f, "\n\tContextAttribute:");
161 for (key, value) in map {
162 write!(f, "\n\t\t{}: {}", key, value)?;
163 }
164 }
165
166 if let Some(source) = &self.source {
168 write!(f, "\n\tErrorSource: {}", source)?;
169
170 if let Some(cause) = self.get_cause() {
172 let _ = write!(f, "\n\tErrorCause:");
173 for line in cause.lines() {
174 write!(f, "\n\t\t{}", line)?;
175 }
176 }
177 }
178
179 Ok(())
180 }
181}
182
183impl std::error::Error for AppError {}
184
185impl Clone for AppError {
186 fn clone(&self) -> Self {
187 Self {
188 code: self.code.clone(),
189 message: self.message.clone(),
190 message_detail: self.message_detail.clone(),
191 context: self.context.clone(),
192 source: self.source.as_ref().map(|err| {
193 AnyhowError::msg(err.to_string())
195 }),
196 }
197 }
198}
199
200impl From<std::io::Error> for AppError {
217 fn from(err: std::io::Error) -> Self {
218 let kind_str = format!("{:?}", err.kind());
219 let message = err.to_string();
220
221 AppError::from_error("IO_ERROR", "IO异常", err)
222 .with_attribute("io_error_kind", kind_str)
223 .with_attribute("io_error_message", message)
224 }
225}
226
227impl From<tracing_subscriber::util::TryInitError> for AppError {
229 fn from(err: tracing_subscriber::util::TryInitError) -> Self {
230 let message = err.to_string();
231 AppError::from_error("LOGGING_INIT_ERROR", "日志系统初始化失败", err)
232 .with_attribute("tracing_init_error", message)
233 }
234}
235
236impl From<AppError> for std::io::Error {
253 fn from(err: AppError) -> Self {
254 if let Some(source) = err.source() {
256 if let Some(io_error) = source.downcast_ref::<std::io::Error>() {
258 return std::io::Error::new(
260 io_error.kind(),
261 io_error.to_string()
262 );
263 }
264 }
265
266 std::io::Error::other(
269 err.to_string()
270 )
271 }
272}