//! What a database call can fail with. //! //! Two distinct failures, kept separate rather than flattened: the statement //! itself (rusqlite) and getting a connection to run it on (r2d2). A pool //! timeout says the app is saturated, which is a different thing to diagnose //! than a constraint violation. /// Why a repository call failed. #[derive(Debug, thiserror::Error)] pub enum DbError { #[error("database error: {0}")] Sqlite(#[from] rusqlite::Error), #[error("could not check out a database connection: {0}")] Pool(#[from] r2d2::Error), } impl DbError { /// Whether this is rusqlite's "no rows returned" for a single-row query. /// /// Repository methods that can legitimately find nothing return `Option` /// instead, so this is for the callers that go around them. #[must_use] pub fn is_not_found(&self) -> bool { matches!(self, Self::Sqlite(rusqlite::Error::QueryReturnedNoRows)) } } /// Why a database could not be opened. /// /// r2d2's error type is opaque and cannot carry a rusqlite failure, so opening /// the anchor connection and building the pool around it stay separate. #[derive(Debug, thiserror::Error)] pub enum OpenError { #[error("could not open the database: {0}")] Sqlite(#[from] rusqlite::Error), #[error("could not build the connection pool: {0}")] Pool(#[from] r2d2::Error), }