1mod adhoc;
10mod condition_failed;
11mod connection_lost;
12mod connection_pool;
13mod driver_operation_failed;
14mod expression_evaluation_failed;
15mod invalid_connection_url;
16mod invalid_driver_configuration;
17mod invalid_record_count;
18mod invalid_result;
19mod invalid_schema;
20mod invalid_statement;
21mod invalid_type_conversion;
22mod read_only_transaction;
23mod record_not_found;
24mod serialization_failure;
25mod transaction_timeout;
26mod unsupported_feature;
27mod validation;
28
29use adhoc::Adhoc;
30use condition_failed::ConditionFailed;
31use connection_lost::ConnectionLost;
32use connection_pool::ConnectionPool;
33use driver_operation_failed::DriverOperationFailed;
34use expression_evaluation_failed::ExpressionEvaluationFailed;
35use invalid_connection_url::InvalidConnectionUrl;
36use invalid_driver_configuration::InvalidDriverConfiguration;
37use invalid_record_count::InvalidRecordCount;
38use invalid_result::InvalidResult;
39use invalid_schema::InvalidSchema;
40use invalid_statement::InvalidStatement;
41use invalid_type_conversion::InvalidTypeConversion;
42use read_only_transaction::ReadOnlyTransaction;
43use record_not_found::RecordNotFound;
44use serialization_failure::SerializationFailure;
45use std::sync::Arc;
46use transaction_timeout::TransactionTimeout;
47use unsupported_feature::UnsupportedFeature;
48use validation::ValidationFailed;
49
50#[derive(Clone)]
74#[non_exhaustive]
75pub struct Error {
76 inner: Arc<ErrorInner>,
77}
78
79pub trait IntoError {
96 fn into_error(self) -> Error;
98}
99
100#[derive(Debug)]
101struct ErrorInner {
102 kind: ErrorKind,
103 cause: Option<Error>,
104}
105
106#[derive(Debug)]
107enum ErrorKind {
108 Adhoc(Adhoc),
109 DriverOperationFailed(DriverOperationFailed),
110 ConnectionLost(ConnectionLost),
111 ConnectionPool(ConnectionPool),
112 ExpressionEvaluationFailed(ExpressionEvaluationFailed),
113 InvalidConnectionUrl(InvalidConnectionUrl),
114 InvalidDriverConfiguration(InvalidDriverConfiguration),
115 InvalidTypeConversion(InvalidTypeConversion),
116 InvalidRecordCount(InvalidRecordCount),
117 RecordNotFound(RecordNotFound),
118 InvalidResult(InvalidResult),
119 InvalidSchema(InvalidSchema),
120 InvalidStatement(InvalidStatement),
121 ReadOnlyTransaction(ReadOnlyTransaction),
122 SerializationFailure(SerializationFailure),
123 TransactionTimeout(TransactionTimeout),
124 UnsupportedFeature(UnsupportedFeature),
125 ValidationFailed(ValidationFailed),
126 ConditionFailed(ConditionFailed),
127}
128
129impl Error {
130 pub fn context(self, consequent: impl IntoError) -> Error {
153 self.context_impl(consequent.into_error())
154 }
155
156 fn context_impl(self, consequent: Error) -> Error {
157 let mut err = consequent;
158 let inner = Arc::get_mut(&mut err.inner).unwrap();
159 assert!(
160 inner.cause.is_none(),
161 "consequent error must not already have a cause"
162 );
163 inner.cause = Some(self);
164 err
165 }
166
167 fn chain(&self) -> impl Iterator<Item = &Error> {
168 let mut err = self;
169 core::iter::once(err).chain(core::iter::from_fn(move || {
170 err = err.inner.cause.as_ref()?;
171 Some(err)
172 }))
173 }
174
175 fn kind(&self) -> &ErrorKind {
176 &self.inner.kind
177 }
178}
179
180impl std::error::Error for Error {
181 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
182 match self.kind() {
183 ErrorKind::DriverOperationFailed(err) => Some(err),
184 ErrorKind::ConnectionLost(err) => Some(err),
185 ErrorKind::ConnectionPool(err) => Some(err),
186 _ => None,
187 }
188 }
189}
190
191impl core::fmt::Display for Error {
192 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
193 let mut it = self.chain().peekable();
194 while let Some(err) = it.next() {
195 core::fmt::Display::fmt(err.kind(), f)?;
196 if it.peek().is_some() {
197 f.write_str(": ")?;
198 }
199 }
200 Ok(())
201 }
202}
203
204impl core::fmt::Debug for Error {
205 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
206 if !f.alternate() {
207 core::fmt::Display::fmt(self, f)
208 } else {
209 f.debug_struct("Error")
210 .field("kind", &self.inner.kind)
211 .field("cause", &self.inner.cause)
212 .finish()
213 }
214 }
215}
216
217impl core::fmt::Display for ErrorKind {
218 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
219 use self::ErrorKind::*;
220
221 match self {
222 Adhoc(err) => core::fmt::Display::fmt(err, f),
223 DriverOperationFailed(err) => core::fmt::Display::fmt(err, f),
224 ConnectionLost(err) => core::fmt::Display::fmt(err, f),
225 ConnectionPool(err) => core::fmt::Display::fmt(err, f),
226 ExpressionEvaluationFailed(err) => core::fmt::Display::fmt(err, f),
227 InvalidConnectionUrl(err) => core::fmt::Display::fmt(err, f),
228 InvalidDriverConfiguration(err) => core::fmt::Display::fmt(err, f),
229 InvalidTypeConversion(err) => core::fmt::Display::fmt(err, f),
230 InvalidRecordCount(err) => core::fmt::Display::fmt(err, f),
231 RecordNotFound(err) => core::fmt::Display::fmt(err, f),
232 InvalidResult(err) => core::fmt::Display::fmt(err, f),
233 InvalidSchema(err) => core::fmt::Display::fmt(err, f),
234 InvalidStatement(err) => core::fmt::Display::fmt(err, f),
235 ReadOnlyTransaction(err) => core::fmt::Display::fmt(err, f),
236 SerializationFailure(err) => core::fmt::Display::fmt(err, f),
237 TransactionTimeout(err) => core::fmt::Display::fmt(err, f),
238 UnsupportedFeature(err) => core::fmt::Display::fmt(err, f),
239 ValidationFailed(err) => core::fmt::Display::fmt(err, f),
240 ConditionFailed(err) => core::fmt::Display::fmt(err, f),
241 }
242 }
243}
244
245impl From<ErrorKind> for Error {
246 fn from(kind: ErrorKind) -> Error {
247 Error {
248 inner: Arc::new(ErrorInner { kind, cause: None }),
249 }
250 }
251}
252
253impl IntoError for Error {
254 fn into_error(self) -> Error {
255 self
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn error_size() {
265 let expected_size = core::mem::size_of::<usize>();
267 assert_eq!(expected_size, core::mem::size_of::<Error>());
268 }
269
270 #[test]
271 fn error_from_args() {
272 let err = Error::from_args(format_args!("test error: {}", 42));
273 assert_eq!(err.to_string(), "test error: 42");
274 }
275
276 #[test]
277 fn error_chain_display() {
278 let root = Error::from_args(format_args!("root cause"));
279 let mid = Error::from_args(format_args!("middle context"));
280 let top = Error::from_args(format_args!("top context"));
281
282 let chained = root.context(mid).context(top);
283 assert_eq!(
284 chained.to_string(),
285 "top context: middle context: root cause"
286 );
287 }
288
289 #[test]
290 fn type_conversion_error() {
291 let value = crate::stmt::Value::I64(42);
292 let err = Error::type_conversion(value, "String");
293 assert_eq!(err.to_string(), "cannot convert I64 to String");
294 }
295
296 #[test]
297 fn type_conversion_error_range() {
298 let value = crate::stmt::Value::U64(u64::MAX);
300 let err = Error::type_conversion(value, "usize");
301 assert_eq!(err.to_string(), "cannot convert U64 to usize");
302 }
303
304 #[test]
305 fn record_not_found_with_immediate_context() {
306 let err = Error::record_not_found("table=users key={id: 123}");
307 assert_eq!(
308 err.to_string(),
309 "record not found: table=users key={id: 123}"
310 );
311 }
312
313 #[test]
314 fn record_not_found_with_context_chain() {
315 let err = Error::record_not_found("table=users key={id: 123}")
316 .context(Error::from_args(format_args!("update query failed")))
317 .context(Error::from_args(format_args!("User.update() operation")));
318
319 assert_eq!(
320 err.to_string(),
321 "User.update() operation: update query failed: record not found: table=users key={id: 123}"
322 );
323 }
324
325 #[test]
326 fn invalid_record_count_with_context() {
327 let err = Error::invalid_record_count("expected 1 record, found multiple");
328 assert_eq!(
329 err.to_string(),
330 "invalid record count: expected 1 record, found multiple"
331 );
332 }
333
334 #[test]
335 fn invalid_result_error() {
336 let err = Error::invalid_result("expected Stream, got Count");
337 assert_eq!(
338 err.to_string(),
339 "invalid result: expected Stream, got Count"
340 );
341 }
342
343 #[test]
344 fn validation_length_too_short() {
345 let err = Error::validation_length(3, Some(5), Some(10));
346 assert_eq!(err.to_string(), "value length 3 is too short (minimum: 5)");
347 }
348
349 #[test]
350 fn validation_length_too_long() {
351 let err = Error::validation_length(15, Some(5), Some(10));
352 assert_eq!(err.to_string(), "value length 15 is too long (maximum: 10)");
353 }
354
355 #[test]
356 fn validation_length_exact_mismatch() {
357 let err = Error::validation_length(3, Some(5), Some(5));
358 assert_eq!(
359 err.to_string(),
360 "value length 3 does not match required length 5"
361 );
362 }
363
364 #[test]
365 fn validation_length_min_only() {
366 let err = Error::validation_length(3, Some(5), None);
367 assert_eq!(err.to_string(), "value length 3 is too short (minimum: 5)");
368 }
369
370 #[test]
371 fn validation_length_max_only() {
372 let err = Error::validation_length(15, None, Some(10));
373 assert_eq!(err.to_string(), "value length 15 is too long (maximum: 10)");
374 }
375
376 #[test]
377 fn condition_failed_with_context() {
378 let err = Error::condition_failed("optimistic lock version mismatch");
379 assert_eq!(
380 err.to_string(),
381 "condition failed: optimistic lock version mismatch"
382 );
383 }
384
385 #[test]
386 fn condition_failed_with_format() {
387 let expected = 1;
388 let actual = 0;
389 let err = Error::condition_failed(format!(
390 "expected {} row affected, got {}",
391 expected, actual
392 ));
393 assert_eq!(
394 err.to_string(),
395 "condition failed: expected 1 row affected, got 0"
396 );
397 }
398
399 #[test]
400 fn invalid_schema_error() {
401 let err = Error::invalid_schema("duplicate index name `idx_users`");
402 assert_eq!(
403 err.to_string(),
404 "invalid schema: duplicate index name `idx_users`"
405 );
406 }
407
408 #[test]
409 fn invalid_schema_with_context() {
410 let err = Error::invalid_schema(
411 "auto_increment column `id` in table `users` must have a numeric type, found String",
412 )
413 .context(Error::from_args(format_args!("schema verification failed")));
414 assert_eq!(
415 err.to_string(),
416 "schema verification failed: invalid schema: auto_increment column `id` in table `users` must have a numeric type, found String"
417 );
418 }
419
420 #[test]
421 fn expression_evaluation_failed() {
422 let err = Error::expression_evaluation_failed("failed to resolve argument");
423 assert_eq!(
424 err.to_string(),
425 "expression evaluation failed: failed to resolve argument"
426 );
427 }
428
429 #[test]
430 fn expression_evaluation_failed_with_context() {
431 let err = Error::expression_evaluation_failed("expected boolean value")
432 .context(Error::from_args(format_args!("query execution failed")));
433 assert_eq!(
434 err.to_string(),
435 "query execution failed: expression evaluation failed: expected boolean value"
436 );
437 }
438
439 #[test]
440 fn unsupported_feature() {
441 let err = Error::unsupported_feature("VARCHAR type is not supported by this database");
442 assert_eq!(
443 err.to_string(),
444 "unsupported feature: VARCHAR type is not supported by this database"
445 );
446 }
447
448 #[test]
449 fn unsupported_feature_with_context() {
450 let err = Error::unsupported_feature("type List is not supported by this database")
451 .context(Error::from_args(format_args!("schema creation failed")));
452 assert_eq!(
453 err.to_string(),
454 "schema creation failed: unsupported feature: type List is not supported by this database"
455 );
456 }
457
458 #[test]
459 fn invalid_driver_configuration() {
460 let err = Error::invalid_driver_configuration(
461 "native_varchar is true but storage_types.varchar is None",
462 );
463 assert_eq!(
464 err.to_string(),
465 "invalid driver configuration: native_varchar is true but storage_types.varchar is None"
466 );
467 }
468
469 #[test]
470 fn invalid_driver_configuration_with_context() {
471 let err = Error::invalid_driver_configuration("inconsistent capability flags").context(
472 Error::from_args(format_args!("driver initialization failed")),
473 );
474 assert_eq!(
475 err.to_string(),
476 "driver initialization failed: invalid driver configuration: inconsistent capability flags"
477 );
478 }
479
480 #[test]
481 fn invalid_statement_error() {
482 let err = Error::invalid_statement("field `unknown_field` does not exist on model `User`");
483 assert_eq!(
484 err.to_string(),
485 "invalid statement: field `unknown_field` does not exist on model `User`"
486 );
487 }
488
489 #[test]
490 fn invalid_statement_with_context() {
491 let err = Error::invalid_statement("cannot update primary key field `id`")
492 .context(Error::from_args(format_args!("statement lowering failed")));
493 assert_eq!(
494 err.to_string(),
495 "statement lowering failed: invalid statement: cannot update primary key field `id`"
496 );
497 }
498
499 #[test]
500 fn read_only_transaction_display() {
501 let err = Error::read_only_transaction("cannot execute UPDATE in a read-only transaction");
502 assert_eq!(
503 err.to_string(),
504 "read-only transaction: cannot execute UPDATE in a read-only transaction"
505 );
506 }
507
508 #[test]
509 fn read_only_transaction_is_predicate() {
510 let err = Error::read_only_transaction("write not allowed");
511 assert!(err.is_read_only_transaction());
512 }
513
514 #[test]
515 fn read_only_transaction_predicate_false_for_other_errors() {
516 let err = Error::serialization_failure("concurrent update conflict");
517 assert!(!err.is_read_only_transaction());
518 }
519
520 #[test]
521 fn connection_lost_predicate_and_display() {
522 let io_err = std::io::Error::new(std::io::ErrorKind::ConnectionReset, "broken pipe");
523 let err = Error::connection_lost(io_err);
524 assert!(err.is_connection_lost());
525 assert!(!err.is_driver_operation_failed());
526 assert_eq!(err.to_string(), "connection lost: broken pipe");
527 }
528
529 #[test]
530 fn read_only_transaction_with_context() {
531 let err = Error::read_only_transaction("INSERT not allowed")
532 .context(Error::from_args(format_args!("create user failed")));
533 assert_eq!(
534 err.to_string(),
535 "create user failed: read-only transaction: INSERT not allowed"
536 );
537 }
538}