use rusqlite::Error as RusqliteError;
use thiserror::Error;
use std::path::PathBuf;
use std::path::Path;
use super::id::CrawlLogEntryId;
#[derive(Error, Debug)]
pub enum DatabaseOpeningError {
#[error("Unable to open Database at {path:?}: {source}")]
UnableToOpenDatabase {
source: RusqliteError,
path: PathBuf,
},
#[error("Database file not found at {path:?}")]
DatabaseFileNotFound {
path: PathBuf,
},
#[error("Database at {path:?} required to be writable, but it isn't. Are the (file) permissions correct?")]
DatabaseIsNotWritable {
path: PathBuf,
},
#[error("Error while initalizing Database at {path:?}: {source}")]
ErrorInitalizingDatabase {
source: DatabaseError,
path: PathBuf,
},
#[error("Database at {path:?} either isn't an unobtanium database or an unversioned one (before 2025-08-09).")]
DatabaseUnversioned {
path: PathBuf,
},
#[error("Database at {path:?} has {schema} schema version of {got_version:?}, expected {expected_version:?}")]
WrongSchemaVersion {
path: PathBuf,
schema: String,
got_version: String,
expected_version: String,
},
#[error("Database at {path:?} isof he wrong kind. Found {got_kind:?}, Expected {expected_kind:?}")]
WrongDatabaseKind {
path: PathBuf,
got_kind: String,
expected_kind: String
}
}
#[derive(Error, Debug)]
pub enum DatabaseError {
#[error("Problem wile interacting with database: {source}")]
RusqliteError {
source: RusqliteError,
},
#[error("Attempted to write to read only database using {function}")]
AttemptToWriteToReadOnlyDatabse {
function: String,
},
#[error("Out of range crawl exit code {code} on entry {crawl_log_id}")]
OutOfRangeCrawlerExitCode {
crawl_log_id: CrawlLogEntryId,
code: i64,
},
}
impl DatabaseError {
pub(super) fn smuggle_through_rusqlite(self) -> RusqliteError {
match self {
Self::RusqliteError { source } => source,
Self::AttemptToWriteToReadOnlyDatabse { function } =>
RusqliteError::InvalidColumnName(
format!("SmuggledUnobtaniumDatabaseError: Attempt to write to readonly database in {function}")),
Self::OutOfRangeCrawlerExitCode { crawl_log_id, code } =>
RusqliteError::InvalidColumnName(
format!("SmuggledUnobtaniumDatabaseError: Out of range crawl exit code {code} on entry {crawl_log_id}")),
}
}
pub(super) fn while_initlizing(self, path: impl AsRef<Path>) -> DatabaseOpeningError {
DatabaseOpeningError::ErrorInitalizingDatabase {
source: self,
path: path.as_ref().into()
}
}
pub fn is_not_found(&self) -> bool {
matches!(self,Self::RusqliteError { source: RusqliteError::QueryReturnedNoRows })
}
}
impl From<RusqliteError> for DatabaseError {
fn from(value: RusqliteError) -> Self {
Self::RusqliteError { source: value }
}
}
pub trait SmuggleDatabaseErrorExtension<T> {
fn smuggle_through_rusqlite(self) -> Result<T, RusqliteError>;
}
impl<T> SmuggleDatabaseErrorExtension<T> for Result<T, DatabaseError> {
fn smuggle_through_rusqlite(self) -> Result<T, RusqliteError> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(e.smuggle_through_rusqlite()),
}
}
}
pub trait IntoDatabaseErrorExtension<T> {
fn error_while_opening(self, path: impl AsRef<Path>) -> Result<T,DatabaseOpeningError>;
}
impl<T> IntoDatabaseErrorExtension<T> for Result<T,RusqliteError> {
fn error_while_opening(self, path: impl AsRef<Path>) -> Result<T,DatabaseOpeningError> {
self.map_err(|e| DatabaseOpeningError::UnableToOpenDatabase {
source: e,
path: path.as_ref().into()
})
}
}