Skip to main content

yang_db/
error.rs

1/// 数据库错误类型
2#[derive(Debug, thiserror::Error)]
3pub enum DbError {
4    #[error("连接错误: {0}")]
5    ConnectionError(String),
6
7    #[error("查询错误: {0}")]
8    QueryError(String),
9
10    #[error("SQL 语法错误: {0}")]
11    SqlSyntaxError(String),
12
13    #[error("约束错误: {0}")]
14    ConstraintError(String),
15
16    #[error("类型转换错误: {0}")]
17    TypeConversionError(String),
18
19    #[error("序列化错误: {0}")]
20    SerializationError(String),
21
22    #[error("反序列化错误: {0}")]
23    DeserializationError(String),
24
25    #[error("事务错误: {0}")]
26    TransactionError(String),
27
28    #[error("表不存在: {0}")]
29    TableNotFound(String),
30
31    #[error("缺少 WHERE 条件,禁止全表操作")]
32    MissingWhereClause,
33
34    // Redis 相关错误
35    #[error("Redis 连接错误: {0}")]
36    RedisConnectionError(String),
37
38    #[error("Redis 命令错误: {0}")]
39    RedisCommandError(String),
40
41    #[error("Redis 连接池错误: {0}")]
42    RedisPoolError(String),
43
44    #[error("Redis 类型转换错误: {0}")]
45    RedisTypeConversionError(String),
46
47    #[error("Redis 超时错误: {0}")]
48    RedisTimeoutError(String),
49
50    #[error("HAVING 子句需要 GROUP BY 子句")]
51    MissingGroupByClause,
52
53    /// 不支持的操作符错误,当传入的操作符不在支持集合中时返回
54    #[error("不支持的操作符: {0}")]
55    UnsupportedOperator(String),
56
57    #[error("未知错误: {0}")]
58    Unknown(String),
59}
60
61/// 从 sqlx::Error 转换为 DbError
62impl From<sqlx::Error> for DbError {
63    fn from(err: sqlx::Error) -> Self {
64        match err {
65            sqlx::Error::Configuration(_) => DbError::ConnectionError(format!("配置错误: {}", err)),
66            sqlx::Error::Database(db_err) => {
67                let code = db_err.code().unwrap_or_default();
68                let message = db_err.message();
69
70                // MySQL 错误码映射
71                match code.as_ref() {
72                    "23000" => DbError::ConstraintError(message.to_string()),
73                    "42S02" => DbError::TableNotFound(message.to_string()),
74                    "42000" => DbError::SqlSyntaxError(message.to_string()),
75                    _ => DbError::QueryError(format!("数据库错误 [{}]: {}", code, message)),
76                }
77            }
78            sqlx::Error::Io(_) => DbError::ConnectionError(format!("IO 错误: {}", err)),
79            sqlx::Error::Tls(_) => DbError::ConnectionError(format!("TLS 错误: {}", err)),
80            sqlx::Error::Protocol(_) => DbError::QueryError(format!("协议错误: {}", err)),
81            sqlx::Error::RowNotFound => DbError::QueryError("未找到记录".to_string()),
82            sqlx::Error::TypeNotFound { type_name } => {
83                DbError::TypeConversionError(format!("类型未找到: {}", type_name))
84            }
85            sqlx::Error::ColumnIndexOutOfBounds { index, len } => {
86                DbError::QueryError(format!("列索引越界: {} (总列数: {})", index, len))
87            }
88            sqlx::Error::ColumnNotFound(col) => DbError::QueryError(format!("列不存在: {}", col)),
89            sqlx::Error::ColumnDecode { index, source } => {
90                DbError::TypeConversionError(format!("列 {} 解码失败: {}", index, source))
91            }
92            sqlx::Error::Decode(source) => {
93                DbError::DeserializationError(format!("解码失败: {}", source))
94            }
95            sqlx::Error::PoolTimedOut => DbError::ConnectionError("连接池超时".to_string()),
96            sqlx::Error::PoolClosed => DbError::ConnectionError("连接池已关闭".to_string()),
97            sqlx::Error::WorkerCrashed => DbError::ConnectionError("工作线程崩溃".to_string()),
98            _ => DbError::Unknown(format!("未知错误: {}", err)),
99        }
100    }
101}
102
103/// 从 redis::RedisError 转换为 DbError
104impl From<redis::RedisError> for DbError {
105    fn from(err: redis::RedisError) -> Self {
106        // redis 1.1.0 中的 ErrorKind 是不同的,我们直接根据错误信息判断
107        let err_str = format!("{}", err);
108
109        if err_str.contains("IO error") || err_str.contains("Connection") {
110            DbError::RedisConnectionError(format!("IO 错误: {}", err))
111        } else if err_str.contains("type") || err_str.contains("Type") {
112            DbError::RedisTypeConversionError(format!("类型错误: {}", err))
113        } else if err_str.contains("timeout") || err_str.contains("Timeout") {
114            DbError::RedisTimeoutError(format!("超时错误: {}", err))
115        } else {
116            DbError::RedisCommandError(format!("Redis 错误: {}", err))
117        }
118    }
119}
120
121/// 从 deadpool_redis::PoolError 转换为 DbError
122impl From<deadpool_redis::PoolError> for DbError {
123    fn from(err: deadpool_redis::PoolError) -> Self {
124        match err {
125            deadpool_redis::PoolError::Timeout(_) => {
126                DbError::RedisTimeoutError("连接池获取连接超时".to_string())
127            }
128            deadpool_redis::PoolError::Closed => {
129                DbError::RedisPoolError("连接池已关闭".to_string())
130            }
131            deadpool_redis::PoolError::NoRuntimeSpecified => {
132                DbError::RedisPoolError("未指定运行时".to_string())
133            }
134            deadpool_redis::PoolError::PostCreateHook(source) => {
135                DbError::RedisPoolError(format!("连接创建后钩子失败: {:?}", source))
136            }
137            deadpool_redis::PoolError::Backend(err) => DbError::from(err),
138        }
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_error_display_chinese() {
148        // 测试所有错误类型的中文消息
149        let errors = vec![
150            DbError::ConnectionError("测试".to_string()),
151            DbError::QueryError("测试".to_string()),
152            DbError::SqlSyntaxError("测试".to_string()),
153            DbError::ConstraintError("测试".to_string()),
154            DbError::TypeConversionError("测试".to_string()),
155            DbError::SerializationError("测试".to_string()),
156            DbError::DeserializationError("测试".to_string()),
157            DbError::TransactionError("测试".to_string()),
158            DbError::TableNotFound("测试".to_string()),
159            DbError::MissingWhereClause,
160            DbError::Unknown("测试".to_string()),
161        ];
162
163        for error in errors {
164            let msg = format!("{}", error);
165            // 验证错误消息包含中文字符
166            let has_chinese = msg.chars().any(|c| matches!(c, '\u{4e00}'..='\u{9fff}'));
167            assert!(has_chinese, "错误消息应该包含中文: {}", msg);
168        }
169    }
170
171    #[test]
172    fn test_connection_error() {
173        let err = DbError::ConnectionError("无法连接到数据库".to_string());
174        assert_eq!(format!("{}", err), "连接错误: 无法连接到数据库");
175    }
176
177    #[test]
178    fn test_query_error() {
179        let err = DbError::QueryError("查询超时".to_string());
180        assert_eq!(format!("{}", err), "查询错误: 查询超时");
181    }
182
183    #[test]
184    fn test_sql_syntax_error() {
185        let err = DbError::SqlSyntaxError("语法错误".to_string());
186        assert_eq!(format!("{}", err), "SQL 语法错误: 语法错误");
187    }
188
189    #[test]
190    fn test_constraint_error() {
191        let err = DbError::ConstraintError("主键冲突".to_string());
192        assert_eq!(format!("{}", err), "约束错误: 主键冲突");
193    }
194
195    #[test]
196    fn test_type_conversion_error() {
197        let err = DbError::TypeConversionError("无法转换类型".to_string());
198        assert_eq!(format!("{}", err), "类型转换错误: 无法转换类型");
199    }
200
201    #[test]
202    fn test_serialization_error() {
203        let err = DbError::SerializationError("序列化失败".to_string());
204        assert_eq!(format!("{}", err), "序列化错误: 序列化失败");
205    }
206
207    #[test]
208    fn test_deserialization_error() {
209        let err = DbError::DeserializationError("反序列化失败".to_string());
210        assert_eq!(format!("{}", err), "反序列化错误: 反序列化失败");
211    }
212
213    #[test]
214    fn test_transaction_error() {
215        let err = DbError::TransactionError("事务已提交".to_string());
216        assert_eq!(format!("{}", err), "事务错误: 事务已提交");
217    }
218
219    #[test]
220    fn test_table_not_found() {
221        let err = DbError::TableNotFound("users".to_string());
222        assert_eq!(format!("{}", err), "表不存在: users");
223    }
224
225    #[test]
226    fn test_missing_where_clause() {
227        let err = DbError::MissingWhereClause;
228        assert_eq!(format!("{}", err), "缺少 WHERE 条件,禁止全表操作");
229    }
230
231    #[test]
232    fn test_unknown_error() {
233        let err = DbError::Unknown("未知问题".to_string());
234        assert_eq!(format!("{}", err), "未知错误: 未知问题");
235    }
236
237    #[test]
238    fn test_error_implements_std_error() {
239        // 验证 DbError 实现了 std::error::Error trait
240        let err = DbError::QueryError("测试".to_string());
241        let _: &dyn std::error::Error = &err;
242    }
243
244    // Redis 错误测试
245    #[test]
246    fn test_redis_connection_error() {
247        let err = DbError::RedisConnectionError("连接失败".to_string());
248        assert_eq!(format!("{}", err), "Redis 连接错误: 连接失败");
249    }
250
251    #[test]
252    fn test_redis_command_error() {
253        let err = DbError::RedisCommandError("命令执行失败".to_string());
254        assert_eq!(format!("{}", err), "Redis 命令错误: 命令执行失败");
255    }
256
257    #[test]
258    fn test_redis_pool_error() {
259        let err = DbError::RedisPoolError("连接池错误".to_string());
260        assert_eq!(format!("{}", err), "Redis 连接池错误: 连接池错误");
261    }
262
263    #[test]
264    fn test_redis_type_conversion_error() {
265        let err = DbError::RedisTypeConversionError("类型转换失败".to_string());
266        assert_eq!(format!("{}", err), "Redis 类型转换错误: 类型转换失败");
267    }
268
269    #[test]
270    fn test_redis_timeout_error() {
271        let err = DbError::RedisTimeoutError("操作超时".to_string());
272        assert_eq!(format!("{}", err), "Redis 超时错误: 操作超时");
273    }
274
275    #[test]
276    fn test_pool_error_conversion() {
277        // 测试 deadpool_redis::PoolError 转换
278        let pool_err = deadpool_redis::PoolError::Closed;
279        let db_err: DbError = pool_err.into();
280        assert!(matches!(db_err, DbError::RedisPoolError(_)));
281    }
282
283    // 验证需求: 4.4 — UnsupportedOperator 变体错误消息格式测试
284    #[test]
285    fn test_unsupported_operator_error_message() {
286        // 验证错误消息格式为 "不支持的操作符: {op}"
287        let op = "BETWEEN";
288        let err = DbError::UnsupportedOperator(op.to_string());
289        assert_eq!(format!("{}", err), "不支持的操作符: BETWEEN");
290    }
291
292    #[test]
293    fn test_unsupported_operator_empty_string() {
294        // 验证空字符串操作符的错误消息格式
295        let err = DbError::UnsupportedOperator(String::new());
296        assert_eq!(format!("{}", err), "不支持的操作符: ");
297    }
298
299    #[test]
300    fn test_unsupported_operator_special_chars() {
301        // 验证包含特殊字符的操作符错误消息格式
302        let err = DbError::UnsupportedOperator("<>".to_string());
303        assert_eq!(format!("{}", err), "不支持的操作符: <>");
304    }
305
306    #[test]
307    fn test_unsupported_operator_implements_std_error() {
308        // 验证 UnsupportedOperator 变体实现了 std::error::Error trait
309        let err = DbError::UnsupportedOperator("IN".to_string());
310        let _: &dyn std::error::Error = &err;
311    }
312
313    #[test]
314    fn test_unsupported_operator_message_contains_chinese() {
315        // 验证错误消息包含中文字符("不支持的操作符")
316        let err = DbError::UnsupportedOperator("XOR".to_string());
317        let msg = format!("{}", err);
318        let has_chinese = msg.chars().any(|c| matches!(c, '\u{4e00}'..='\u{9fff}'));
319        assert!(has_chinese, "错误消息应该包含中文: {}", msg);
320    }
321}