1#[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 #[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 #[error("不支持的操作符: {0}")]
55 UnsupportedOperator(String),
56
57 #[error("未知错误: {0}")]
58 Unknown(String),
59}
60
61impl 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 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
103impl From<redis::RedisError> for DbError {
105 fn from(err: redis::RedisError) -> Self {
106 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
121impl 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 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 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 let err = DbError::QueryError("测试".to_string());
241 let _: &dyn std::error::Error = &err;
242 }
243
244 #[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 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 #[test]
285 fn test_unsupported_operator_error_message() {
286 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 let err = DbError::UnsupportedOperator(String::new());
296 assert_eq!(format!("{}", err), "不支持的操作符: ");
297 }
298
299 #[test]
300 fn test_unsupported_operator_special_chars() {
301 let err = DbError::UnsupportedOperator("<>".to_string());
303 assert_eq!(format!("{}", err), "不支持的操作符: <>");
304 }
305
306 #[test]
307 fn test_unsupported_operator_implements_std_error() {
308 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 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}