tern_core/
error.rs

1//! Error type for migration operations.
2use crate::migration::{Migration, MigrationId};
3use crate::runner::{MigrationResult, Report};
4
5use std::error::Error as StdError;
6
7/// Alias for a result whose error type is [`Error`].
8pub type TernResult<T> = Result<T, Error>;
9type BoxDynError = Box<dyn StdError + Send + Sync + 'static>;
10
11/// All the ways the lifecycle of applying migrations
12/// can end in failure.
13#[derive(Debug, thiserror::Error)]
14#[non_exhaustive]
15pub enum Error {
16    /// An error that came from applying migrations.
17    #[error("error applying migrations {0}")]
18    Execute(#[source] BoxDynError),
19    /// Error from one migration.
20    #[error("error applying migration: {{name: {1}, no_tx: {2}}}: {0}")]
21    ExecuteMigration(#[source] BoxDynError, MigrationId, bool),
22    /// An error resolving the query before applying.
23    /// Can be used as a fallthrough to map arbitrary error types to when
24    /// implementing `QueryBuilder`.
25    #[error("runtime could not resolve query: {0}")]
26    ResolveQuery(String),
27    /// Error processing a migration source.
28    #[error("could not parse migration query: {0}")]
29    Sql(#[from] std::fmt::Error),
30    /// Local migration source has fewer migrations than the history table.
31    #[error("missing source: {local} migrations found but {history} have been applied: {msg}")]
32    MissingSource {
33        local: i64,
34        history: i64,
35        msg: String,
36    },
37    /// The options passed are not valid.
38    #[error("invalid parameter for the operation requested: {0}")]
39    Invalid(String),
40    /// An error occurred, resulting in a partial migration run.
41    #[error("migration could not complete: {source}, partial report: {report}")]
42    Partial { source: BoxDynError, report: Report },
43}
44
45impl Error {
46    pub fn to_resolve_query_error<E>(e: E) -> Self
47    where
48        E: std::fmt::Display,
49    {
50        Self::ResolveQuery(e.to_string())
51    }
52}
53
54/// Converting a result with a generic `std::error::Error` to one with this
55/// crate's error type.
56///
57/// The `*_migration_result` methods allow attaching a migration to the error,
58/// such as the one being handled when the error occurred.  The `with_report`
59/// method allows attaching a slice of `MigrationResult` to the error to show
60/// what collection of the migration set did succeed in being applied before the
61/// error was encountered.
62pub trait DatabaseError<T, E> {
63    /// Convert `E` to an [`Error`].
64    fn tern_result(self) -> TernResult<T>;
65
66    /// Same as `tern_result` but discard the returned value.
67    fn void_tern_result(self) -> TernResult<()>;
68
69    /// Convert `E` to an [`Error`] that has a given migration in the error
70    /// type's source.
71    fn tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<T>;
72
73    /// Same as `tern_migration_result` but discard the returned value.
74    fn void_tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<()>;
75
76    /// Attach an array of `MigrationResult`, representing a partially successful
77    /// migration operation, to the error.
78    fn with_report(self, report: &[MigrationResult]) -> TernResult<T>;
79}
80
81impl<T, E> DatabaseError<T, E> for Result<T, E>
82where
83    E: StdError + Send + Sync + 'static,
84{
85    fn void_tern_result(self) -> TernResult<()> {
86        match self {
87            Err(e) => Err(Error::Execute(Box::new(e))),
88            _ => Ok(()),
89        }
90    }
91
92    fn void_tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<()> {
93        match self {
94            Err(e) => Err(Error::ExecuteMigration(
95                Box::new(e),
96                migration.migration_id(),
97                migration.no_tx(),
98            )),
99            _ => Ok(()),
100        }
101    }
102
103    fn tern_result(self) -> TernResult<T> {
104        match self {
105            Ok(v) => Ok(v),
106            Err(e) => Err(Error::Execute(Box::new(e))),
107        }
108    }
109
110    fn tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<T> {
111        match self {
112            Ok(v) => Ok(v),
113            Err(e) => Err(Error::ExecuteMigration(
114                Box::new(e),
115                migration.migration_id(),
116                migration.no_tx(),
117            )),
118        }
119    }
120
121    fn with_report(self, migrations: &[MigrationResult]) -> TernResult<T> {
122        match self {
123            Ok(v) => Ok(v),
124            Err(e) => Err(Error::Partial {
125                source: Box::new(e),
126                report: Report::new(migrations.to_vec()),
127            }),
128        }
129    }
130}