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