use crate::migration::{Migration, MigrationId};
use crate::runner::{MigrationResult, Report};
use std::error::Error as StdError;
pub type TernResult<T> = Result<T, Error>;
type BoxDynError = Box<dyn StdError + Send + Sync + 'static>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("error applying migrations {0}")]
Execute(#[source] BoxDynError),
#[error("error applying migration: {{name: {1}, no_tx: {2}}}: {0}")]
ExecuteMigration(#[source] BoxDynError, MigrationId, bool),
#[error("runtime could not resolve query: {0}")]
ResolveQuery(String),
#[error("could not parse migration query: {0}")]
Sql(#[from] std::fmt::Error),
#[error("error splitting statement {1}: {0}")]
Split(std::io::Error, usize),
#[error(
"missing source: {local} migrations found but {history} have been applied: {msg}"
)]
MissingSource { local: i64, history: i64, msg: String },
#[error("inconsistent source: {msg}: {at_issue:?}")]
OutOfSync { at_issue: Vec<MigrationId>, msg: String },
#[error("invalid parameter for the operation requested: {0}")]
Invalid(String),
#[error("migration could not complete: {source}, partial report: {report}")]
Partial { source: BoxDynError, report: Report },
}
impl Error {
pub fn to_resolve_query_error<E>(e: E) -> Self
where
E: std::fmt::Display,
{
Self::ResolveQuery(e.to_string())
}
pub(crate) fn split_err(idx: usize) -> impl FnMut(std::io::Error) -> Self {
move |e| Self::Split(e, idx)
}
}
pub trait DatabaseError<T, E> {
fn tern_result(self) -> TernResult<T>;
fn void_tern_result(self) -> TernResult<()>;
fn tern_migration_result<M: Migration + ?Sized>(
self,
migration: &M,
) -> TernResult<T>;
fn void_tern_migration_result<M: Migration + ?Sized>(
self,
migration: &M,
) -> TernResult<()>;
fn with_report(self, report: &[MigrationResult]) -> TernResult<T>;
}
impl<T, E> DatabaseError<T, E> for Result<T, E>
where
E: StdError + Send + Sync + 'static,
{
fn void_tern_result(self) -> TernResult<()> {
match self {
Err(e) => Err(Error::Execute(Box::new(e))),
_ => Ok(()),
}
}
fn void_tern_migration_result<M: Migration + ?Sized>(
self,
migration: &M,
) -> TernResult<()> {
match self {
Err(e) => Err(Error::ExecuteMigration(
Box::new(e),
migration.migration_id(),
migration.no_tx(),
)),
_ => Ok(()),
}
}
fn tern_result(self) -> TernResult<T> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(Error::Execute(Box::new(e))),
}
}
fn tern_migration_result<M: Migration + ?Sized>(
self,
migration: &M,
) -> TernResult<T> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(Error::ExecuteMigration(
Box::new(e),
migration.migration_id(),
migration.no_tx(),
)),
}
}
fn with_report(self, migrations: &[MigrationResult]) -> TernResult<T> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(Error::Partial {
source: Box::new(e),
report: Report::new(migrations.to_vec()),
}),
}
}
}