Crate edgedb_errors
source ·Expand description
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 error from the transaction).
Each error kind is represented as a separate type that implements
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:
let err = UserError::with_source(io::Error::from(io::ErrorKind::NotFound));
assert!(err.is::<UserError>());
Since errors are hirarhical Error::is
works with any ancestor:
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:
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>());
Errors in Transactions
Special care for errors must be taken in transactions. Generally:
- Errors from queries should not be ignored, and should be propagagated up to the transaction function.
- User errors can be encapsulated into
UserError
via one of the methods:ErrorKind::with_source
(for anystd::error::Error
)ErrorKind::with_source_box
already boxed errorErrorKind::with_source_ref
for smart wrappers such asanyhow::Error
- 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
We use miette crate for including snippets in your error reporting code.
To make it work, first you need enable fancy
feature in your top-level
crate’s Cargo.toml
:
[dependencies]
miette = { version="5.3.0", features=["fancy"] }
edgedb-tokio = { version="*", features=["miette-errors"] }
Then if you use miette
all the way through your application, it just
works:
#[tokio::main]
async fn main() -> miette::Result<()> {
let conn = edgedb_tokio::create_client().await?;
conn.query::<String, _>("SELECT 1+2)", &()).await?;
Ok(())
}
However, if you use some boxed error container (e.g. anyhow), you might need to downcast error for printing:
async fn do_something() -> anyhow::Result<()> {
let conn = edgedb_tokio::create_client().await?;
conn.query::<String, _>("SELECT 1+2)", &()).await?;
Ok(())
}
#[tokio::main]
async fn main() {
match do_something().await {
Ok(res) => res,
Err(e) => {
e.downcast::<edgedb_tokio::Error>()
.map(|e| eprintln!("{:?}", miette::Report::new(e)))
.unwrap_or_else(|e| eprintln!("{:#}", e));
std::process::exit(1);
}
}
}
In some cases, where parts of your code use miette::Result
or
miette::Report
before converting to the boxed (anyhow) container, you
might want a little bit more complex downcasting:
#[tokio::main]
async fn main() {
match do_something().await {
Ok(res) => res,
Err(e) => {
e.downcast::<edgedb_tokio::Error>()
.map(|e| eprintln!("{:?}", miette::Report::new(e)))
.or_else(|e| e.downcast::<miette::Report>()
.map(|e| eprintln!("{:?}", e)))
.unwrap_or_else(|e| eprintln!("{:#}", e));
std::process::exit(1);
}
}
}
Note that last two examples do hide error contexts from anyhow and do not
pretty print if source()
of the error is edgedb_errors::Error
but not
the top-level one. We leave those more complex cases as an excersize to the
reader.
Re-exports
pub use kinds::*;
Modules
Structs
- Error type returned from any EdgeDB call.
- Tag that is used to group similar errors.
Traits
- Trait that marks EdgeDB errors.