knife_util/
error.rs

1use anyhow::Error as AnyhowError;
2use serde_json::Value;
3use std::fmt;
4
5/// 应用程序统一错误类型
6#[derive(Debug, serde::Serialize, serde::Deserialize)]
7pub struct AppError {
8    /// 错误代码
9    pub code: String,
10    /// 错误消息
11    pub message: String,
12    /// 详细错误信息
13    pub message_detail: Option<String>,
14    /// 上下文信息
15    pub context: Value,
16    /// 原始异常和堆栈追踪
17    #[serde(skip_serializing, skip_deserializing)]
18    pub source: Option<AnyhowError>,
19}
20
21impl AppError {
22    /// 创建新的错误实例
23    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    /// 获取错误代码
34    pub fn code(&self) -> &str {
35        &self.code
36    }
37
38    /// 获取错误消息
39    pub fn message(&self) -> &str {
40        &self.message
41    }
42
43    /// 获取详细错误信息
44    pub fn message_detail(&self) -> Option<&str> {
45        self.message_detail.as_deref()
46    }
47
48    /// 设置详细错误信息
49    pub fn with_message_detail(mut self, detail: &str) -> Self {
50        self.message_detail = Some(detail.to_string());
51        self
52    }
53
54    /// 添加上下文信息
55    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    /// 获取上下文信息
63    pub fn get_attribute(&self, key: &str) -> Option<&Value> {
64        self.context.get(key)
65    }
66
67    /// 获取原始异常信息
68    pub fn source(&self) -> Option<&AnyhowError> {
69        self.source.as_ref()
70    }
71
72    /// 设置完整的上下文信息
73    pub fn with_context(mut self, context: Value) -> Self {
74        self.context = context;
75        self
76    }
77
78    /// 设置原始异常信息
79    pub fn with_source(mut self, source: AnyhowError) -> Self {
80        self.source = Some(source);
81        self
82    }
83
84    /// 从其他错误类型创建AppError
85    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    /// 从anyhow::Error创建AppError
100    pub fn from_anyhow(code: &str, message: &str, err: AnyhowError) -> Self {
101        Self::new(code, message).with_source(err)
102    }
103
104    /// 从Display类型创建AppError
105    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    /// 从Display类型创建AppError并保存原始错误
110    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    /// 获取完整的错误堆栈信息
121    pub fn get_cause(&self) -> Option<String> {
122        self.source.as_ref().map(|err| {
123            let mut trace = String::new();
124
125            // 添加主要错误信息
126            trace.push_str(&format!("Error: {}\n", err));
127
128            // 添加错误链
129            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            // 添加 backtrace(如果可用)
138            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        // 主行:AppError [$code, $message]
151        write!(f, "AppError [{}] {}", self.code, self.message)?;
152
153        // 如果有详细错误信息,添加缩进的 MessageDetail 行
154        if let Some(detail) = &self.message_detail {
155            write!(f, "\n\tMessageDetail: {}", detail)?;
156        }
157
158        // 如果有上下文信息,添加缩进的 Context 行
159        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        // 如果有原始异常信息,添加缩进的 Source 行
167        if let Some(source) = &self.source {
168            write!(f, "\n\tErrorSource: {}", source)?;
169
170            // 显示完整的错误堆栈追踪和 backtrace
171            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 转换为字符串再重新创建
194                AnyhowError::msg(err.to_string())
195            }),
196        }
197    }
198}
199
200/// 从std::io::Error转换为AppError
201/// 
202/// 支持通过 ? 操作符自动处理 IOError
203/// 
204/// # 示例
205/// 
206/// ```rust
207/// use knife_util::AppError;
208/// use std::fs::File;
209/// 
210/// fn read_file() -> Result<String, AppError> {
211///     let mut file = File::open("nonexistent.txt")?; // 自动转换为 AppError
212///     // ... 其他操作
213///     Ok("content".to_string())
214/// }
215/// ```
216impl 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
227/// 从TryInitError转换为AppError
228impl 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
236/// 从AppError转换为std::io::Error
237/// 
238/// 支持在返回 std::io::Result 的函数中使用 ? 操作符
239/// 
240/// # 示例
241/// 
242/// ```rust
243/// use knife_util::AppError;
244/// use std::fs::File;
245/// 
246/// fn read_file() -> std::io::Result<String> {
247///     let mut file = File::open("nonexistent.txt")?; // 自动转换为 AppError
248///     // ... 其他操作
249///     Ok("content".to_string())
250/// }
251/// ```
252impl From<AppError> for std::io::Error {
253    fn from(err: AppError) -> Self {
254        // 尝试从 AppError 中提取原始的 std::io::Error
255        if let Some(source) = err.source() {
256            // 检查原始错误是否是 std::io::Error
257            if let Some(io_error) = source.downcast_ref::<std::io::Error>() {
258                // 如果是 IO 错误,创建一个新的 std::io::Error 保持相同的类型和消息
259                return std::io::Error::new(
260                    io_error.kind(),
261                    io_error.to_string()
262                );
263            }
264        }
265        
266        // 如果不是 IO 错误,创建一个新的 std::io::Error
267        // 使用 AppError 的显示格式作为错误消息
268        std::io::Error::other(
269            err.to_string()
270        )
271    }
272}