Skip to main content

anvilkit_core/error/
error.rs

1//! # 核心错误类型
2//! 
3//! 定义 AnvilKit 的主要错误类型和错误分类系统。
4
5use thiserror::Error;
6use std::fmt;
7
8/// AnvilKit 的主要错误类型
9/// 
10/// 这个枚举涵盖了 AnvilKit 生态系统中可能出现的所有错误类型。
11/// 每个变体都包含详细的错误信息和可选的上下文数据。
12/// 
13/// ## 设计特点
14/// 
15/// - **结构化**: 按功能模块分类错误
16/// - **信息丰富**: 包含详细的错误描述和上下文
17/// - **可序列化**: 支持错误的序列化和传输
18/// - **链式错误**: 支持错误链和根因分析
19#[derive(Error, Debug)]
20pub enum AnvilKitError {
21    /// 渲染系统错误
22    /// 
23    /// 包括 GPU 驱动错误、着色器编译错误、纹理加载错误等。
24    #[error("渲染错误: {message}")]
25    Render {
26        /// 错误消息
27        message: String,
28        /// 可选的底层错误
29        #[source]
30        source: Option<Box<dyn std::error::Error + Send + Sync>>,
31    },
32
33    /// 物理系统错误
34    /// 
35    /// 包括物理世界初始化错误、碰撞检测错误、约束求解错误等。
36    #[error("物理错误: {message}")]
37    Physics {
38        /// 错误消息
39        message: String,
40        /// 可选的底层错误
41        #[source]
42        source: Option<Box<dyn std::error::Error + Send + Sync>>,
43    },
44
45    /// 资源管理错误
46    /// 
47    /// 包括资源加载失败、格式不支持、资源不存在等。
48    #[error("资源错误: {message}")]
49    Asset {
50        /// 错误消息
51        message: String,
52        /// 资源路径(如果适用)
53        path: Option<String>,
54        /// 可选的底层错误
55        #[source]
56        source: Option<Box<dyn std::error::Error + Send + Sync>>,
57    },
58
59    /// 音频系统错误
60    /// 
61    /// 包括音频设备初始化错误、音频格式错误、播放错误等。
62    #[error("音频错误: {message}")]
63    Audio {
64        /// 错误消息
65        message: String,
66        /// 可选的底层错误
67        #[source]
68        source: Option<Box<dyn std::error::Error + Send + Sync>>,
69    },
70
71    /// 输入系统错误
72    /// 
73    /// 包括输入设备初始化错误、输入映射错误等。
74    #[error("输入错误: {message}")]
75    Input {
76        /// 错误消息
77        message: String,
78        /// 可选的底层错误
79        #[source]
80        source: Option<Box<dyn std::error::Error + Send + Sync>>,
81    },
82
83    /// ECS 系统错误
84    /// 
85    /// 包括组件注册错误、系统调度错误、世界状态错误等。
86    #[error("ECS 错误: {message}")]
87    Ecs {
88        /// 错误消息
89        message: String,
90        /// 可选的底层错误
91        #[source]
92        source: Option<Box<dyn std::error::Error + Send + Sync>>,
93    },
94
95    /// 窗口和平台错误
96    /// 
97    /// 包括窗口创建错误、平台特定错误、事件处理错误等。
98    #[error("窗口错误: {message}")]
99    Window {
100        /// 错误消息
101        message: String,
102        /// 可选的底层错误
103        #[source]
104        source: Option<Box<dyn std::error::Error + Send + Sync>>,
105    },
106
107    /// 配置和初始化错误
108    /// 
109    /// 包括配置文件解析错误、参数验证错误、初始化失败等。
110    #[error("配置错误: {message}")]
111    Config {
112        /// 错误消息
113        message: String,
114        /// 配置键或路径(如果适用)
115        key: Option<String>,
116        /// 可选的底层错误
117        #[source]
118        source: Option<Box<dyn std::error::Error + Send + Sync>>,
119    },
120
121    /// 网络和通信错误
122    /// 
123    /// 包括网络连接错误、协议错误、序列化错误等。
124    #[error("网络错误: {message}")]
125    Network {
126        /// 错误消息
127        message: String,
128        /// 可选的底层错误
129        #[source]
130        source: Option<Box<dyn std::error::Error + Send + Sync>>,
131    },
132
133    /// I/O 操作错误
134    /// 
135    /// 文件系统操作、网络 I/O 等底层 I/O 错误的包装。
136    #[error("I/O 错误: {0}")]
137    Io(#[from] std::io::Error),
138
139    /// 序列化和反序列化错误
140    #[error("序列化错误: {message}")]
141    Serialization {
142        /// 错误消息
143        message: String,
144        /// 可选的底层错误
145        #[source]
146        source: Option<Box<dyn std::error::Error + Send + Sync>>,
147    },
148
149    /// 通用错误
150    /// 
151    /// 用于不属于其他特定类别的错误。
152    #[error("AnvilKit 错误: {message}")]
153    Generic {
154        /// 错误消息
155        message: String,
156        /// 可选的底层错误
157        #[source]
158        source: Option<Box<dyn std::error::Error + Send + Sync>>,
159    },
160}
161
162/// 错误类别枚举
163/// 
164/// 用于对错误进行分类,便于错误处理和统计。
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
166pub enum ErrorCategory {
167    /// 渲染相关错误
168    Render,
169    /// 物理相关错误
170    Physics,
171    /// 资源相关错误
172    Asset,
173    /// 音频相关错误
174    Audio,
175    /// 输入相关错误
176    Input,
177    /// ECS 相关错误
178    Ecs,
179    /// 窗口相关错误
180    Window,
181    /// 配置相关错误
182    Config,
183    /// 网络相关错误
184    Network,
185    /// I/O 相关错误
186    Io,
187    /// 序列化相关错误
188    Serialization,
189    /// 通用错误
190    Generic,
191}
192
193impl fmt::Display for ErrorCategory {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        let name = match self {
196            ErrorCategory::Render => "渲染",
197            ErrorCategory::Physics => "物理",
198            ErrorCategory::Asset => "资源",
199            ErrorCategory::Audio => "音频",
200            ErrorCategory::Input => "输入",
201            ErrorCategory::Ecs => "ECS",
202            ErrorCategory::Window => "窗口",
203            ErrorCategory::Config => "配置",
204            ErrorCategory::Network => "网络",
205            ErrorCategory::Io => "I/O",
206            ErrorCategory::Serialization => "序列化",
207            ErrorCategory::Generic => "通用",
208        };
209        write!(f, "{}", name)
210    }
211}
212
213impl AnvilKitError {
214    /// 创建渲染错误
215    /// 
216    /// # 示例
217    /// 
218    /// ```rust
219    /// use anvilkit_core::error::AnvilKitError;
220    /// 
221    /// let error = AnvilKitError::render("着色器编译失败");
222    /// ```
223    pub fn render(message: impl Into<String>) -> Self {
224        Self::Render {
225            message: message.into(),
226            source: None,
227        }
228    }
229
230    /// 创建带源错误的渲染错误
231    pub fn render_with_source(
232        message: impl Into<String>,
233        source: impl std::error::Error + Send + Sync + 'static,
234    ) -> Self {
235        Self::Render {
236            message: message.into(),
237            source: Some(Box::new(source)),
238        }
239    }
240
241    /// 创建物理错误
242    pub fn physics(message: impl Into<String>) -> Self {
243        Self::Physics {
244            message: message.into(),
245            source: None,
246        }
247    }
248
249    /// 创建带源错误的物理错误
250    pub fn physics_with_source(
251        message: impl Into<String>,
252        source: impl std::error::Error + Send + Sync + 'static,
253    ) -> Self {
254        Self::Physics {
255            message: message.into(),
256            source: Some(Box::new(source)),
257        }
258    }
259
260    /// 创建资源错误
261    pub fn asset(message: impl Into<String>) -> Self {
262        Self::Asset {
263            message: message.into(),
264            path: None,
265            source: None,
266        }
267    }
268
269    /// 创建带路径的资源错误
270    pub fn asset_with_path(message: impl Into<String>, path: impl Into<String>) -> Self {
271        Self::Asset {
272            message: message.into(),
273            path: Some(path.into()),
274            source: None,
275        }
276    }
277
278    /// 创建音频错误
279    pub fn audio(message: impl Into<String>) -> Self {
280        Self::Audio {
281            message: message.into(),
282            source: None,
283        }
284    }
285
286    /// 创建输入错误
287    pub fn input(message: impl Into<String>) -> Self {
288        Self::Input {
289            message: message.into(),
290            source: None,
291        }
292    }
293
294    /// 创建 ECS 错误
295    pub fn ecs(message: impl Into<String>) -> Self {
296        Self::Ecs {
297            message: message.into(),
298            source: None,
299        }
300    }
301
302    /// 创建窗口错误
303    pub fn window(message: impl Into<String>) -> Self {
304        Self::Window {
305            message: message.into(),
306            source: None,
307        }
308    }
309
310    /// 创建配置错误
311    pub fn config(message: impl Into<String>) -> Self {
312        Self::Config {
313            message: message.into(),
314            key: None,
315            source: None,
316        }
317    }
318
319    /// 创建带键的配置错误
320    pub fn config_with_key(message: impl Into<String>, key: impl Into<String>) -> Self {
321        Self::Config {
322            message: message.into(),
323            key: Some(key.into()),
324            source: None,
325        }
326    }
327
328    /// 创建网络错误
329    pub fn network(message: impl Into<String>) -> Self {
330        Self::Network {
331            message: message.into(),
332            source: None,
333        }
334    }
335
336    /// 创建序列化错误
337    pub fn serialization(message: impl Into<String>) -> Self {
338        Self::Serialization {
339            message: message.into(),
340            source: None,
341        }
342    }
343
344    /// 创建通用错误
345    pub fn generic(message: impl Into<String>) -> Self {
346        Self::Generic {
347            message: message.into(),
348            source: None,
349        }
350    }
351
352    /// 获取错误类别
353    /// 
354    /// # 示例
355    /// 
356    /// ```rust
357    /// use anvilkit_core::error::{AnvilKitError, ErrorCategory};
358    /// 
359    /// let error = AnvilKitError::render("测试错误");
360    /// assert_eq!(error.category(), ErrorCategory::Render);
361    /// ```
362    pub fn category(&self) -> ErrorCategory {
363        match self {
364            Self::Render { .. } => ErrorCategory::Render,
365            Self::Physics { .. } => ErrorCategory::Physics,
366            Self::Asset { .. } => ErrorCategory::Asset,
367            Self::Audio { .. } => ErrorCategory::Audio,
368            Self::Input { .. } => ErrorCategory::Input,
369            Self::Ecs { .. } => ErrorCategory::Ecs,
370            Self::Window { .. } => ErrorCategory::Window,
371            Self::Config { .. } => ErrorCategory::Config,
372            Self::Network { .. } => ErrorCategory::Network,
373            Self::Io(_) => ErrorCategory::Io,
374            Self::Serialization { .. } => ErrorCategory::Serialization,
375            Self::Generic { .. } => ErrorCategory::Generic,
376        }
377    }
378
379    /// 获取错误消息
380    ///
381    /// 返回不包含错误类型前缀的纯错误消息。
382    pub fn message(&self) -> String {
383        match self {
384            Self::Render { message, .. } => message.clone(),
385            Self::Physics { message, .. } => message.clone(),
386            Self::Asset { message, .. } => message.clone(),
387            Self::Audio { message, .. } => message.clone(),
388            Self::Input { message, .. } => message.clone(),
389            Self::Ecs { message, .. } => message.clone(),
390            Self::Window { message, .. } => message.clone(),
391            Self::Config { message, .. } => message.clone(),
392            Self::Network { message, .. } => message.clone(),
393            Self::Io(err) => err.to_string(),
394            Self::Serialization { message, .. } => message.clone(),
395            Self::Generic { message, .. } => message.clone(),
396        }
397    }
398
399    /// 检查是否为特定类别的错误
400    /// 
401    /// # 示例
402    /// 
403    /// ```rust
404    /// use anvilkit_core::error::{AnvilKitError, ErrorCategory};
405    /// 
406    /// let error = AnvilKitError::render("测试错误");
407    /// assert!(error.is_category(ErrorCategory::Render));
408    /// assert!(!error.is_category(ErrorCategory::Physics));
409    /// ```
410    pub fn is_category(&self, category: ErrorCategory) -> bool {
411        self.category() == category
412    }
413
414    /// 添加上下文信息
415    /// 
416    /// 返回一个包含额外上下文信息的新错误。
417    pub fn with_context(self, context: impl Into<String>) -> Self {
418        let context = context.into();
419        match self {
420            Self::Generic { message, source } => Self::Generic {
421                message: format!("{}: {}", context, message),
422                source,
423            },
424            _ => Self::Generic {
425                message: format!("{}: {}", context, self),
426                source: Some(Box::new(self)),
427            },
428        }
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use super::*;
435
436    #[test]
437    fn test_error_creation() {
438        let error = AnvilKitError::render("测试渲染错误");
439        assert_eq!(error.category(), ErrorCategory::Render);
440        assert_eq!(error.message(), "测试渲染错误");
441        assert!(error.is_category(ErrorCategory::Render));
442    }
443
444    #[test]
445    fn test_error_with_path() {
446        let error = AnvilKitError::asset_with_path("加载失败", "texture.png");
447        if let AnvilKitError::Asset { path, .. } = &error {
448            assert_eq!(path.as_ref().unwrap(), "texture.png");
449        } else {
450            panic!("Expected Asset error");
451        }
452    }
453
454    #[test]
455    fn test_error_with_context() {
456        let original = AnvilKitError::render("着色器编译失败");
457        let with_context = original.with_context("初始化渲染器时");
458        
459        assert!(with_context.to_string().contains("初始化渲染器时"));
460        assert!(with_context.to_string().contains("着色器编译失败"));
461    }
462
463    #[test]
464    fn test_io_error_conversion() {
465        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "文件未找到");
466        let anvilkit_error: AnvilKitError = io_error.into();
467        
468        assert_eq!(anvilkit_error.category(), ErrorCategory::Io);
469    }
470
471    #[test]
472    fn test_error_category_display() {
473        assert_eq!(ErrorCategory::Render.to_string(), "渲染");
474        assert_eq!(ErrorCategory::Physics.to_string(), "物理");
475        assert_eq!(ErrorCategory::Asset.to_string(), "资源");
476    }
477
478    #[test]
479    fn test_error_with_source() {
480        let source_error = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "权限不足");
481        let error = AnvilKitError::render_with_source("渲染初始化失败", source_error);
482
483        assert!(std::error::Error::source(&error).is_some());
484        assert_eq!(error.category(), ErrorCategory::Render);
485    }
486
487    #[test]
488    fn test_all_error_variants() {
489        // 测试每种错误变体的创建和类别
490        let errors = vec![
491            (AnvilKitError::render("test"), ErrorCategory::Render),
492            (AnvilKitError::physics("test"), ErrorCategory::Physics),
493            (AnvilKitError::asset("test"), ErrorCategory::Asset),
494            (AnvilKitError::audio("test"), ErrorCategory::Audio),
495            (AnvilKitError::input("test"), ErrorCategory::Input),
496            (AnvilKitError::ecs("test"), ErrorCategory::Ecs),
497            (AnvilKitError::window("test"), ErrorCategory::Window),
498            (AnvilKitError::config("test"), ErrorCategory::Config),
499            (AnvilKitError::network("test"), ErrorCategory::Network),
500            (AnvilKitError::serialization("test"), ErrorCategory::Serialization),
501            (AnvilKitError::generic("test"), ErrorCategory::Generic),
502        ];
503
504        for (error, expected_category) in errors {
505            assert_eq!(error.category(), expected_category);
506            assert_eq!(error.message(), "test");
507            assert!(error.is_category(expected_category));
508        }
509    }
510
511    #[test]
512    fn test_error_display_format() {
513        let error = AnvilKitError::render("GPU 初始化失败");
514        let display = format!("{}", error);
515        assert!(display.contains("渲染错误"));
516        assert!(display.contains("GPU 初始化失败"));
517    }
518
519    #[test]
520    fn test_error_debug_format() {
521        let error = AnvilKitError::physics("碰撞检测溢出");
522        let debug = format!("{:?}", error);
523        assert!(debug.contains("Physics"));
524    }
525
526    #[test]
527    fn test_config_error_with_key() {
528        let error = AnvilKitError::config_with_key("无效的分辨率", "window.resolution");
529        if let AnvilKitError::Config { key, message, .. } = &error {
530            assert_eq!(key.as_ref().unwrap(), "window.resolution");
531            assert_eq!(message, "无效的分辨率");
532        } else {
533            panic!("Expected Config error");
534        }
535    }
536
537    #[test]
538    fn test_error_context_chaining() {
539        let original = AnvilKitError::asset("纹理加载失败");
540        let with_ctx = original.with_context("初始化场景时");
541
542        // with_context 在非 Generic 错误上会创建一个新的 Generic 错误包裹源错误
543        assert_eq!(with_ctx.category(), ErrorCategory::Generic);
544        assert!(std::error::Error::source(&with_ctx).is_some());
545    }
546
547    #[test]
548    fn test_error_generic_context_chaining() {
549        // Generic 错误的 with_context 不会嵌套,而是拼接消息
550        let original = AnvilKitError::generic("底层错误");
551        let with_ctx = original.with_context("上层调用");
552
553        assert_eq!(with_ctx.category(), ErrorCategory::Generic);
554        assert!(with_ctx.message().contains("上层调用"));
555        assert!(with_ctx.message().contains("底层错误"));
556    }
557
558    #[test]
559    fn test_all_category_display() {
560        let categories = vec![
561            (ErrorCategory::Render, "渲染"),
562            (ErrorCategory::Physics, "物理"),
563            (ErrorCategory::Asset, "资源"),
564            (ErrorCategory::Audio, "音频"),
565            (ErrorCategory::Input, "输入"),
566            (ErrorCategory::Ecs, "ECS"),
567            (ErrorCategory::Window, "窗口"),
568            (ErrorCategory::Config, "配置"),
569            (ErrorCategory::Network, "网络"),
570            (ErrorCategory::Io, "I/O"),
571            (ErrorCategory::Serialization, "序列化"),
572            (ErrorCategory::Generic, "通用"),
573        ];
574
575        for (category, expected_display) in categories {
576            assert_eq!(category.to_string(), expected_display);
577        }
578    }
579
580    #[test]
581    fn test_error_is_send_sync() {
582        fn assert_send_sync<T: Send + Sync>() {}
583        assert_send_sync::<AnvilKitError>();
584    }
585}