1use anyhow::Error as AnyhowError;
2use serde_json::Value;
3use std::fmt;
4
5#[derive(Debug, serde::Serialize, serde::Deserialize)]
26pub struct AppError {
27 pub code: String,
29 pub message: String,
31 pub message_detail: Option<String>,
33 pub context: Value,
35 #[serde(skip_serializing, skip_deserializing)]
37 pub source: Option<AnyhowError>,
38}
39
40impl AppError {
41 pub fn new(code: &str, message: &str) -> Self {
43 Self {
44 code: code.to_string(),
45 message: message.to_string(),
46 message_detail: None,
47 context: Value::Object(serde_json::Map::new()),
48 source: None,
49 }
50 }
51
52 pub fn code(&self) -> &str {
54 &self.code
55 }
56
57 pub fn message(&self) -> &str {
59 &self.message
60 }
61
62 pub fn message_detail(&self) -> Option<&str> {
64 self.message_detail.as_deref()
65 }
66
67 pub fn with_message_detail(mut self, detail: &str) -> Self {
69 self.message_detail = Some(detail.to_string());
70 self
71 }
72
73 pub fn with_attribute(mut self, key: &str, value: impl Into<Value>) -> Self {
75 if let Value::Object(ref mut map) = self.context {
76 map.insert(key.to_string(), value.into());
77 }
78 self
79 }
80
81 pub fn get_attribute(&self, key: &str) -> Option<&Value> {
83 self.context.get(key)
84 }
85
86 pub fn with_context(mut self, context: Value) -> Self {
88 self.context = context;
89 self
90 }
91
92 pub fn source(&self) -> Option<&AnyhowError> {
94 self.source.as_ref()
95 }
96
97 pub fn with_source(mut self, source: AnyhowError) -> Self {
99 self.source = Some(source);
100 self
101 }
102
103 pub fn from_error<E: std::error::Error + Send + Sync + 'static>(code: &str, message: &str, err: E) -> Self {
105 Self {
106 code: code.to_string(),
107 message: message.to_string(),
108 message_detail: None,
109 context: Value::Object(serde_json::Map::new()),
110 source: Some(AnyhowError::new(err)),
111 }
112 }
113
114 pub fn from_anyhow(code: &str, message: &str, err: AnyhowError) -> Self {
116 Self::new(code, message).with_source(err)
117 }
118
119 pub fn from_display<E: std::fmt::Display>(code: &str, message: &str, err: E) -> Self {
121 Self::new(code, message).with_message_detail(&err.to_string())
122 }
123
124 pub fn from_display_with_source<E: std::fmt::Display + std::error::Error + Send + Sync + 'static>(code: &str, message: &str, err: E) -> Self {
126 Self::new(code, message).with_message_detail(&err.to_string()).with_source(AnyhowError::new(err))
127 }
128
129 pub fn get_cause(&self) -> Option<String> {
131 self.source.as_ref().map(|err| {
132 let mut trace = String::new();
133 trace.push_str(&format!("Error: {}\n", err));
134 let mut error: &dyn std::error::Error = err.as_ref();
135 let mut depth = 0;
136 while let Some(source) = error.source() {
137 depth += 1;
138 trace.push_str(&format!("CausedBy ({}): {}\n", depth, source));
139 error = source;
140 }
141 let backtrace = err.backtrace();
142 if backtrace.status() == std::backtrace::BacktraceStatus::Captured {
143 trace.push_str(&format!("Backtrace:\n{}", backtrace));
144 }
145 trace
146 })
147 }
148}
149
150impl fmt::Display for AppError {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(f, "AppError [{}] {}", self.code, self.message)?;
153
154 if let Some(detail) = &self.message_detail {
155 write!(f, "\n\tMessageDetail: {}", detail)?;
156 }
157
158 if let Value::Object(map) = &self.context
159 && !map.is_empty()
160 {
161 write!(f, "\n\tContextAttribute:")?;
162 for (key, value) in map {
163 write!(f, "\n\t\t{}: {}", key, value)?;
164 }
165 }
166
167 if let Some(source) = &self.source {
168 write!(f, "\n\tErrorSource: {}", source)?;
169 if let Some(cause) = self.get_cause() {
170 write!(f, "\n\tErrorCause:")?;
171 for line in cause.lines() {
172 write!(f, "\n\t\t{}", line)?;
173 }
174 }
175 }
176
177 Ok(())
178 }
179}
180
181impl std::error::Error for AppError {}
182
183impl Clone for AppError {
184 fn clone(&self) -> Self {
185 Self {
186 code: self.code.clone(),
187 message: self.message.clone(),
188 message_detail: self.message_detail.clone(),
189 context: self.context.clone(),
190 source: self.source.as_ref().map(|err| AnyhowError::msg(err.to_string())),
191 }
192 }
193}
194
195impl From<std::io::Error> for AppError {
196 fn from(err: std::io::Error) -> Self {
197 let kind_str = format!("{:?}", err.kind());
198 let message = err.to_string();
199 AppError::from_error("IO_ERROR", "IO异常", err)
200 .with_attribute("io_error_kind", kind_str)
201 .with_attribute("io_error_message", message)
202 }
203}
204
205impl From<tracing_subscriber::util::TryInitError> for AppError {
206 fn from(err: tracing_subscriber::util::TryInitError) -> Self {
207 let message = err.to_string();
208 AppError::from_error("LOGGING_INIT_ERROR", "日志系统初始化失败", err).with_attribute("tracing_init_error", message)
209 }
210}
211
212impl From<AppError> for std::io::Error {
213 fn from(err: AppError) -> Self {
214 if let Some(source) = err.source.as_ref()
215 && let Some(io_error) = source.downcast_ref::<std::io::Error>()
216 {
217 return std::io::Error::new(io_error.kind(), io_error.to_string());
218 }
219 std::io::Error::other(err.to_string())
220 }
221}