Skip to main content

dbnexus/foundation/error/
mod.rs

1// Copyright (c) 2026 Kirky.X
2//
3// Licensed under MIT License
4// See LICENSE file in project root for full license information.
5
6//! 子错误类型模块
7//!
8//! 定义 DBNexus 项目中各功能模块的子错误类型。
9//! 顶层统一错误类型 `DbNexusError` 定义在 [`crate::common::error`] 模块。
10//!
11//! # 错误类型
12//!
13//! - [`DbError`] - 数据库操作错误
14//! - [`PoolError`] - 连接池错误
15//! - [`PermissionError`] - 权限错误
16//! - [`MigrationError`] - 迁移错误
17//! - [`AuditError`] - 审计错误
18//!
19//! 每个子错误类型都有对应的结果类型别名(如 [`DbResult`]、[`PoolResult`] 等)。
20
21// ============================================================================
22// 子错误类型定义
23// ============================================================================
24
25/// 数据库操作错误
26#[derive(Debug, thiserror::Error)]
27pub enum DbError {
28    /// 数据库连接错误
29    #[error(transparent)]
30    Connection(#[from] sea_orm::DbErr),
31
32    /// 配置错误
33    #[error("Configuration error: {0}")]
34    Config(String),
35
36    /// 权限错误
37    #[error("Permission denied: {0}")]
38    Permission(String),
39
40    /// 事务错误
41    #[error("Transaction error: {0}")]
42    Transaction(String),
43
44    /// 迁移错误
45    #[error("Migration error: {0}")]
46    Migration(String),
47
48    /// 数据验证错误(feature-gated: validation)
49    #[cfg(feature = "validation")]
50    #[error("Validation error: {0}")]
51    Validation(String),
52}
53
54impl DbError {
55    /// 从 sea_orm::DbErr 创建数据库连接错误
56    pub fn new(error: sea_orm::DbErr) -> Self {
57        Self::Connection(error)
58    }
59
60    /// 获取错误消息
61    pub fn message(&self) -> String {
62        match self {
63            DbError::Connection(e) => e.to_string(),
64            DbError::Config(msg) => msg.clone(),
65            DbError::Permission(msg) => msg.clone(),
66            DbError::Transaction(msg) => msg.clone(),
67            DbError::Migration(msg) => msg.clone(),
68            #[cfg(feature = "validation")]
69            DbError::Validation(msg) => msg.clone(),
70        }
71    }
72}
73
74/// 从字符串创建 DbError::Config
75impl From<String> for DbError {
76    fn from(msg: String) -> Self {
77        Self::Config(msg)
78    }
79}
80
81/// 从 &str 创建 DbError::Config
82impl From<&str> for DbError {
83    fn from(msg: &str) -> Self {
84        Self::Config(msg.to_string())
85    }
86}
87
88/// 连接池错误
89#[derive(Debug, thiserror::Error)]
90pub enum PoolError {
91    /// 连接获取超时
92    #[error("Failed to acquire connection within timeout")]
93    AcquireTimeout,
94
95    /// 连接池已耗尽
96    #[error("Connection pool exhausted")]
97    PoolExhausted,
98
99    /// 连接创建失败
100    #[error("Failed to create connection: {0}")]
101    ConnectionFailed(String),
102
103    /// 健康检查失败
104    #[error("Health check failed: {0}")]
105    HealthCheckFailed(String),
106}
107
108// 权限错误类型已统一到领域层。
109// 此处 re-export `domain::permission::error::PermissionError`,保持
110// `foundation::error::PermissionError` 路径可用,避免破坏既有引用路径。
111//
112// BREAKING(Task 13):旧版 foundation 定义被删除,统一到 domain 版本:
113// - `InvalidConfig` 变体重命名为 `InvalidPolicy`
114// - 新增独有变体 `ParseError`
115// - 错误消息大小写变化(如 "Role not found" → "role not found")
116// access::permission::types::PermissionError 保留不动,待 Phase 4 Task 18 整体删除。
117pub use crate::domain::permission::PermissionError;
118
119/// 迁移错误
120#[derive(Debug, thiserror::Error)]
121pub enum MigrationError {
122    /// 迁移文件未找到
123    #[error("Migration file not found: {0}")]
124    FileNotFound(String),
125
126    /// 迁移文件解析错误
127    #[error("Failed to parse migration file: {0}")]
128    ParseError(String),
129
130    /// 迁移执行失败
131    #[error("Migration execution failed: {0}")]
132    ExecutionError(String),
133
134    /// 迁移版本冲突
135    #[error("Migration version conflict: {0}")]
136    VersionConflict(String),
137
138    /// 迁移回滚失败
139    #[error("Migration rollback failed: {0}")]
140    RollbackError(String),
141}
142
143/// 审计错误
144#[derive(Debug, thiserror::Error)]
145pub enum AuditError {
146    /// 审计日志写入失败
147    #[error("Failed to write audit log: {0}")]
148    WriteError(String),
149
150    /// 审计日志序列化失败
151    #[error("Failed to serialize audit data: {0}")]
152    SerializationError(String),
153
154    /// 审计配置错误
155    #[error("Invalid audit configuration: {0}")]
156    ConfigError(String),
157}
158
159// ============================================================================
160// 结果类型别名
161// ============================================================================
162
163/// 数据库操作结果
164pub type DbResult<T> = Result<T, DbError>;
165/// 权限检查结果
166pub type PermissionResult<T> = Result<T, PermissionError>;
167/// 连接池操作结果
168pub type PoolResult<T> = Result<T, PoolError>;
169/// 配置操作结果
170pub type ConfigResult<T> = Result<T, crate::foundation::config::ConfigError>;
171/// 迁移操作结果
172pub type MigrationResult<T> = Result<T, MigrationError>;
173/// 审计操作结果
174pub type AuditResult<T> = Result<T, AuditError>;
175
176// ============================================================================
177// 单元测试
178// ============================================================================
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    /// 测试 DbError 创建和转换
185    #[test]
186    fn test_db_error_creation() {
187        let db_err = sea_orm::DbErr::Custom("test error".to_string());
188        let error = DbError::new(db_err);
189        assert!(matches!(error, DbError::Connection(_)));
190    }
191
192    /// 测试从 String 创建 DbError
193    #[test]
194    fn test_db_error_from_string() {
195        let error: DbError = "custom error message".into();
196        assert!(matches!(error, DbError::Config(msg) if msg == "custom error message"));
197    }
198
199    /// 测试从 &str 创建 DbError
200    #[test]
201    fn test_db_error_from_str() {
202        let error: DbError = "str error".into();
203        assert!(matches!(error, DbError::Config(msg) if msg == "str error"));
204    }
205
206    /// 测试 DbError 各变体
207    #[test]
208    fn test_db_error_variants() {
209        let config_err = DbError::Config("config issue".to_string());
210        assert!(matches!(config_err, DbError::Config(_)));
211
212        let perm_err = DbError::Permission("access denied".to_string());
213        assert!(matches!(perm_err, DbError::Permission(_)));
214
215        let tx_err = DbError::Transaction("tx failed".to_string());
216        assert!(matches!(tx_err, DbError::Transaction(_)));
217
218        let mig_err = DbError::Migration("migration failed".to_string());
219        assert!(matches!(mig_err, DbError::Migration(_)));
220    }
221
222    /// 测试 PoolError 显示
223    #[test]
224    fn test_pool_error_display() {
225        let error = PoolError::AcquireTimeout;
226        assert_eq!(error.to_string(), "Failed to acquire connection within timeout");
227
228        let error = PoolError::ConnectionFailed("network issue".to_string());
229        assert!(error.to_string().contains("network issue"));
230    }
231
232    /// 测试 PermissionError 显示
233    #[test]
234    fn test_permission_error_display() {
235        let error = PermissionError::Denied {
236            resource: "users".to_string(),
237            operation: "delete".to_string(),
238        };
239        let msg = error.to_string();
240        assert!(msg.contains("users"));
241        assert!(msg.contains("delete"));
242
243        let error = PermissionError::RoleNotFound("admin".to_string());
244        assert!(error.to_string().contains("admin"));
245    }
246
247    /// 测试 MigrationError 显示
248    #[test]
249    fn test_migration_error_display() {
250        let error = MigrationError::FileNotFound("v001.sql".to_string());
251        assert!(error.to_string().contains("v001.sql"));
252
253        let error = MigrationError::VersionConflict("v002".to_string());
254        assert!(error.to_string().contains("v002"));
255    }
256
257    /// 测试 AuditError 显示
258    #[test]
259    fn test_audit_error_display() {
260        let error = AuditError::WriteError("disk full".to_string());
261        assert!(error.to_string().contains("disk full"));
262
263        let error = AuditError::SerializationError("invalid JSON".to_string());
264        assert!(error.to_string().contains("invalid JSON"));
265    }
266}