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