1use std::fmt;
2
3/// What can go wrong with the database. What the adapters report to their ports is only a sentence for the
4/// log (the ports' own errors say whether to retry); this keeps the cause.
5#[derive(Debug)]
6pub enum DbError {
7    /// SQLite refused or failed.
8    Sqlite(rusqlite::Error),
9    /// The database was written by a newer build than this one.
10    Newer { found: u32, known: u32 },
11    /// Something another connection holds stopped a checkpoint.
12    Busy,
13    /// What is stored does not read back as what it should be.
14    Damaged(String),
15}
16
17impl fmt::Display for DbError {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            DbError::Sqlite(e) => write!(f, "database error: {e}"),
21            DbError::Newer { found, known } => write!(f, "the database is at version {found}, newer than this build's {known}"),
22            DbError::Busy => write!(f, "the database is busy"),
23            DbError::Damaged(what) => write!(f, "the database is damaged: {what}"),
24        }
25    }
26}
27
28impl DbError {
29    /// Whether what is stored is not a database at all, or is broken beyond reading: the file is damaged and
30    /// not merely busy, full or from a newer build.
31    pub fn is_unreadable(&self) -> bool {
32        match self {
33            DbError::Sqlite(rusqlite::Error::SqliteFailure(e, _)) => matches!(e.code, rusqlite::ErrorCode::NotADatabase | rusqlite::ErrorCode::DatabaseCorrupt),
34            DbError::Damaged(_) => true,
35            _ => false,
36        }
37    }
38
39    /// Whether the database was written by a newer build.
40    pub fn is_newer(&self) -> bool {
41        matches!(self, DbError::Newer { .. })
42    }
43}
44
45impl std::error::Error for DbError {}
46
47impl From<rusqlite::Error> for DbError {
48    fn from(e: rusqlite::Error) -> Self {
49        DbError::Sqlite(e)
50    }
51}