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