knife_util/
error.rs

1use anyhow::Error as AnyhowError;
2use serde_json::Value;
3use std::fmt;
4
5/// 应用程序统一错误类型
6///
7/// 提供结构化的错误处理,支持错误链、上下文信息和序列化。
8/// 设计用于替代简单的字符串错误,提供更丰富的错误信息。
9///
10/// # 特性
11/// - **错误代码**: 机器可读的错误标识符
12/// - **错误消息**: 人类可读的错误描述
13/// - **详细信息**: 可选的详细错误信息
14/// - **上下文**: JSON 格式的上下文数据
15/// - **错误链**: 支持原始异常和堆栈追踪
16///
17/// # 示例
18/// ```rust
19/// use knife_util::AppError;
20///
21/// let error = AppError::new("FILE_NOT_FOUND", "配置文件不存在")
22///     .with_message_detail("路径: /etc/app/config.toml")
23///     .with_attribute("file_path", "/etc/app/config.toml");
24/// ```
25#[derive(Debug, serde::Serialize, serde::Deserialize)]
26pub struct AppError {
27    /// 错误代码 - 机器可读的错误标识符
28    pub code: String,
29    /// 错误消息 - 人类可读的错误描述
30    pub message: String,
31    /// 详细错误信息 - 可选的额外错误详情
32    pub message_detail: Option<String>,
33    /// 上下文信息 - JSON 格式的上下文数据
34    pub context: Value,
35    /// 原始异常和堆栈追踪 - 不参与序列化
36    #[serde(skip_serializing, skip_deserializing)]
37    pub source: Option<AnyhowError>,
38}
39
40impl AppError {
41    /// 创建新的错误实例
42    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    /// 获取错误代码
53    pub fn code(&self) -> &str {
54        &self.code
55    }
56
57    /// 获取错误消息
58    pub fn message(&self) -> &str {
59        &self.message
60    }
61
62    /// 获取详细错误信息
63    pub fn message_detail(&self) -> Option<&str> {
64        self.message_detail.as_deref()
65    }
66
67    /// 设置详细错误信息
68    pub fn with_message_detail(mut self, detail: &str) -> Self {
69        self.message_detail = Some(detail.to_string());
70        self
71    }
72
73    /// 添加上下文信息
74    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    /// 获取上下文信息
82    pub fn get_attribute(&self, key: &str) -> Option<&Value> {
83        self.context.get(key)
84    }
85
86    /// 设置完整的上下文信息
87    pub fn with_context(mut self, context: Value) -> Self {
88        self.context = context;
89        self
90    }
91
92    /// 获取原始异常信息
93    pub fn source(&self) -> Option<&AnyhowError> {
94        self.source.as_ref()
95    }
96
97    /// 设置原始异常信息
98    pub fn with_source(mut self, source: AnyhowError) -> Self {
99        self.source = Some(source);
100        self
101    }
102
103    /// 从其他错误类型创建AppError
104    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    /// 从anyhow::Error创建AppError
115    pub fn from_anyhow(code: &str, message: &str, err: AnyhowError) -> Self {
116        Self::new(code, message).with_source(err)
117    }
118
119    /// 从Display类型创建AppError
120    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    /// 从Display类型创建AppError并保存原始错误
125    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    /// 获取完整的错误堆栈信息
130    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}