Skip to main content

max / balanced_breakfast

1.4 KB · 41 lines History Blame Raw
1 //! What a database call can fail with.
2 //!
3 //! Two distinct failures, kept separate rather than flattened: the statement
4 //! itself (rusqlite) and getting a connection to run it on (r2d2). A pool
5 //! timeout says the app is saturated, which is a different thing to diagnose
6 //! than a constraint violation.
7
8 /// Why a repository call failed.
9 #[derive(Debug, thiserror::Error)]
10 pub enum DbError {
11 #[error("database error: {0}")]
12 Sqlite(#[from] rusqlite::Error),
13
14 #[error("could not check out a database connection: {0}")]
15 Pool(#[from] r2d2::Error),
16 }
17
18 impl DbError {
19 /// Whether this is rusqlite's "no rows returned" for a single-row query.
20 ///
21 /// Repository methods that can legitimately find nothing return `Option`
22 /// instead, so this is for the callers that go around them.
23 #[must_use]
24 pub fn is_not_found(&self) -> bool {
25 matches!(self, Self::Sqlite(rusqlite::Error::QueryReturnedNoRows))
26 }
27 }
28
29 /// Why a database could not be opened.
30 ///
31 /// r2d2's error type is opaque and cannot carry a rusqlite failure, so opening
32 /// the anchor connection and building the pool around it stay separate.
33 #[derive(Debug, thiserror::Error)]
34 pub enum OpenError {
35 #[error("could not open the database: {0}")]
36 Sqlite(#[from] rusqlite::Error),
37
38 #[error("could not build the connection pool: {0}")]
39 Pool(#[from] r2d2::Error),
40 }
41