Centralise error handling in SQLite manager

This commit is contained in:
Reinout Meliesie 2026-01-22 14:09:17 +01:00
commit 5a615ad3a3
Signed by: zedfrigg
GPG key ID: 3AFCC06481308BC6

View file

@ -24,7 +24,7 @@ impl SqliteManager {
// The order of the items is undefined. // The order of the items is undefined.
pub async fn films_overview(&self) -> Result<Vec<FilmOverview>, DataManagerError> { pub async fn films_overview(&self) -> Result<Vec<FilmOverview>, DataManagerError> {
let overview = self self
.client .client
.conn(|connection| { .conn(|connection| {
connection connection
@ -33,38 +33,18 @@ impl SqliteManager {
select uuid, name, original_name, release_date, runtime_minutes select uuid, name, original_name, release_date, runtime_minutes
from films from films
", ",
) )?
.expect("films overview statement should be valid SQL") .query(())?
.query(())
.expect("parameters in films overview query should match those in its statement")
.map(row_to_film_overview) .map(row_to_film_overview)
.collect() .collect()
}) })
.await; .await
.map_err(convert_error)
overview.map_err(|async_sqlite_error| match async_sqlite_error {
async_sqlite::Error::Closed => {
panic!("database connection should remain open as long as the application is running")
}
async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error {
rusqlite::Error::InvalidColumnIndex(_) => {
panic!("column indices obtained from films overview query should exist")
}
rusqlite::Error::InvalidColumnName(_) => {
panic!("column names obtained from films overview query should exist")
}
rusqlite::Error::InvalidColumnType(..) => panic!(
"values obtained from films overview query should have a type matching their column"
),
_ => DataManagerError::UnknownDBError,
},
_ => DataManagerError::UnknownDBError,
})
} }
// The order of the items is undefined. // The order of the items is undefined.
pub async fn series_overview(&self) -> Result<Vec<SeriesOverview>, DataManagerError> { pub async fn series_overview(&self) -> Result<Vec<SeriesOverview>, DataManagerError> {
let overview = self self
.client .client
.conn(|connection| { .conn(|connection| {
connection connection
@ -76,33 +56,13 @@ impl SqliteManager {
where series.uuid = seasons.series and seasons.uuid = episodes.season where series.uuid = seasons.series and seasons.uuid = episodes.season
group by series.uuid group by series.uuid
", ",
) )?
.expect("series overview statement should be valid SQL") .query(())?
.query(())
.expect("parameters in series overview query should match those in its statement")
.map(row_to_series_overview) .map(row_to_series_overview)
.collect() .collect()
}) })
.await; .await
.map_err(convert_error)
overview.map_err(|async_sqlite_error| match async_sqlite_error {
async_sqlite::Error::Closed => {
panic!("database connection should remain open as long as the application is running")
}
async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error {
rusqlite::Error::InvalidColumnIndex(_) => {
panic!("column indices obtained from series overview query should exist")
}
rusqlite::Error::InvalidColumnName(_) => {
panic!("column names obtained from series overview query should exist")
}
rusqlite::Error::InvalidColumnType(..) => panic!(
"values obtained from series overview query should have a type matching their column"
),
_ => DataManagerError::UnknownDBError,
},
_ => DataManagerError::UnknownDBError,
})
} }
} }
@ -128,21 +88,13 @@ async fn create_client(data_dir: &OsStr) -> Result<Client, DataManagerError> {
let shared_path = shared_path_os_str let shared_path = shared_path_os_str
.to_str() .to_str()
.expect("shared database path should be valid Unicode"); .expect("shared database path should be valid Unicode");
connection connection.execute("attach database :path as shared", &[(":path", shared_path)])?;
.execute("attach database :path as shared", &[(":path", shared_path)])
.expect(
"shared database attaching statement should be valid SQL with matching parameters",
);
let local_path_os_str = concat_os_str!(&data_dir, "/local.sqlite"); let local_path_os_str = concat_os_str!(&data_dir, "/local.sqlite");
let local_path = local_path_os_str let local_path = local_path_os_str
.to_str() .to_str()
.expect("local database path should be valid Unicode"); .expect("local database path should be valid Unicode");
connection connection.execute("attach database :path as local", &[(":path", local_path)])?;
.execute("attach database :path as local", &[(":path", local_path)])
.expect(
"local database attaching statement should be valid SQL with matching parameters",
);
Ok(()) Ok(())
}) })
@ -151,19 +103,7 @@ async fn create_client(data_dir: &OsStr) -> Result<Client, DataManagerError> {
}) })
.await; .await;
client.map_err(|async_sqlite_error| match async_sqlite_error { client.map_err(convert_error)
async_sqlite::Error::Closed => {
panic!("database connection should remain open as long as the application is running")
}
async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error {
rusqlite::Error::SqliteFailure(sqlite_error, _) => match sqlite_error.code {
rusqlite::ffi::ErrorCode::CannotOpen => DataManagerError::CannotOpenDB,
_ => DataManagerError::UnknownDBError,
},
_ => DataManagerError::UnknownDBError,
},
_ => DataManagerError::UnknownDBError,
})
} }
fn row_to_film_overview(row: &Row) -> rusqlite::Result<FilmOverview> { fn row_to_film_overview(row: &Row) -> rusqlite::Result<FilmOverview> {
@ -196,8 +136,36 @@ fn row_to_series_overview(row: &Row) -> rusqlite::Result<SeriesOverview> {
}) })
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq)] fn convert_error(async_sqlite_error: async_sqlite::Error) -> DataManagerError {
enum DBType { match async_sqlite_error {
Shared, async_sqlite::Error::Closed => {
Local, panic!("database connection should remain open as long as the application is running")
}
async_sqlite::Error::Rusqlite(rusqlite_error) => match rusqlite_error {
rusqlite::Error::SqliteFailure(sqlite_error, _) => match sqlite_error.code {
rusqlite::ffi::ErrorCode::CannotOpen => DataManagerError::CannotOpenDB,
_ => DataManagerError::UnknownDBError,
},
rusqlite::Error::InvalidColumnIndex(_) => {
panic!("column indices obtained from query should exist")
}
rusqlite::Error::InvalidColumnName(_) => {
panic!("column names obtained from query should exist")
}
rusqlite::Error::InvalidColumnType(..) => {
panic!("values obtained from query should have a type matching their column")
}
rusqlite::Error::InvalidParameterCount(..) => {
panic!("number of bound parameters should match that in the statement")
}
rusqlite::Error::ExecuteReturnedResults => {
panic!("execution of statement returned data when it shouldn't")
}
rusqlite::Error::MultipleStatement => {
panic!("multiple statements present when there should be one")
}
_ => DataManagerError::UnknownDBError,
},
_ => DataManagerError::UnknownDBError,
}
} }