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