#[derive(Debug, thiserror::Error)]
pub enum DbError {
#[error("record not found")]
NotFound,
#[error("record already exists")]
AlreadyExists,
#[error("constraint violation: {0}")]
ConstraintViolation(String),
#[error("I/O error: {0}")]
IoError(String),
#[error("wrong master key or corrupted vault")]
EncryptionError,
#[error("schema migration failed: {0}")]
MigrationError(String),
#[error("internal database error: {0}")]
Internal(String),
}
pub(super) fn not_found_or(e: rusqlite::Error) -> DbError {
if matches!(e, rusqlite::Error::QueryReturnedNoRows) {
DbError::NotFound
} else {
map_rusqlite_error(e)
}
}
pub(super) fn is_encryption_error(e: &rusqlite::Error) -> bool {
matches!(e, rusqlite::Error::SqliteFailure(err, _) if err.extended_code == 26)
}
pub(super) fn map_rusqlite_error(e: rusqlite::Error) -> DbError {
if is_encryption_error(&e) {
return DbError::EncryptionError;
}
if let rusqlite::Error::SqliteFailure(ref err, ref msg) = e {
if err.extended_code == 2067 || err.extended_code == 1555 {
return DbError::AlreadyExists;
}
if err.code == rusqlite::ErrorCode::ConstraintViolation {
return DbError::ConstraintViolation(msg.clone().unwrap_or_else(|| e.to_string()));
}
}
DbError::Internal(e.to_string())
}