gel_errors/
lib.rs

1/*!
2# Error Handling for Gel
3
4All errors that Gel Rust bindings produce are encapsulated into the
5[`Error`] structure. The structure is a bit like `Box<dyn Error>` or
6[`anyhow::Error`], except it can only contain Gel error types. Or
7[`UserError`] can be used to encapsulate custom errors (commonly used
8to return an error from a transaction).
9
10A full list of Gel error types on a single page can be found on the [website documentation](https://www.edgedb.com/docs/reference/protocol/errors#error-codes).
11
12Each error kind is represented as a separate type that implements the
13[`ErrorKind`] trait. But error kinds are used like marker structs; you can
14use [`Error::is`] for error kinds and use them to create instances of the
15error:
16
17```rust
18# use std::io;
19# use gel_errors::{UserError, ErrorKind};
20let err = UserError::with_source(io::Error::from(io::ErrorKind::NotFound));
21assert!(err.is::<UserError>());
22```
23
24Since errors are hirarchical, [`Error::is`] works with any ancestor:
25
26```rust
27# use gel_errors::*;
28# let err = MissingArgumentError::with_message("test error");
29assert!(err.is::<MissingArgumentError>());
30assert!(err.is::<QueryArgumentError>());  // implied by the assertion above
31assert!(err.is::<InterfaceError>());  // and this one
32assert!(err.is::<ClientError>());  // and this one
33```
34
35Error hierarchy doesn't have multiple inheritance (i.e. every error has only
36single parent). When we match across different parents we use error tags:
37
38```rust
39# use gel_errors::*;
40# let err1 = ClientConnectionTimeoutError::with_message("test error");
41# let err2 = TransactionConflictError::with_message("test error");
42
43assert!(err1.is::<ClientConnectionTimeoutError>());
44assert!(err2.is::<TransactionConflictError>());
45// Both of these are retried
46assert!(err1.has_tag(SHOULD_RETRY));
47assert!(err2.has_tag(SHOULD_RETRY));
48
49// But they aren't a part of common hierarchy
50assert!(err1.is::<ClientError>());
51assert!(!err1.is::<ExecutionError>());
52assert!(err2.is::<ExecutionError>());
53assert!(!err2.is::<ClientError>());
54```
55
56[`anyhow::Error`]: https://docs.rs/anyhow/latest/anyhow/struct.Error.html
57
58# Errors in Transactions
59
60Special care for errors must be taken in transactions. Generally:
61
621. Errors from queries should not be ignored, and should be propagagated
63   up to the transaction function.
642. User errors can be encapsulated into [`UserError`] via one of the
65   methods:
66    * [`ErrorKind::with_source`] (for any [`std::error::Error`])
67    * [`ErrorKind::with_source_box`] already boxed error
68    * [`ErrorKind::with_source_ref`] for smart wrappers such as
69      [`anyhow::Error`]
703. Original query error must be propagated via error chain. It can be in the
71   `.source()` chain but must not be swallowed, otherwise retrying
72   transaction may work incorrectly.
73
74# Nice Error Reporting
75
76Refer to documentation in the [gel-tokio](https://docs.rs/gel-tokio) crate.
77*/
78mod error;
79mod traits;
80
81pub mod display;
82pub mod fields;
83pub mod kinds;
84
85#[cfg(feature = "miette")]
86pub mod miette;
87
88pub use error::{Error, Tag};
89pub use kinds::*;
90pub use traits::{ErrorKind, Field, ResultExt};