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 source migrations and the history are not synchronized in a way that
38    /// is expected.
39    #[error("inconsistent source: {msg}: {at_issue:?}")]
40    OutOfSync {
41        at_issue: Vec<MigrationId>,
42        msg: String,
43    },
44    /// The options passed are not valid.
45    #[error("invalid parameter for the operation requested: {0}")]
46    Invalid(String),
47    /// An error occurred, resulting in a partial migration run.
48    #[error("migration could not complete: {source}, partial report: {report}")]
49    Partial { source: BoxDynError, report: Report },
50}
51
52impl Error {
53    pub fn to_resolve_query_error<E>(e: E) -> Self
54    where
55        E: std::fmt::Display,
56    {
57        Self::ResolveQuery(e.to_string())
58    }
59}
60
61/// Converting a result with a generic `std::error::Error` to one with this
62/// crate's error type.
63///
64/// The `*_migration_result` methods allow attaching a migration to the error,
65/// such as the one being handled when the error occurred.  The `with_report`
66/// method allows attaching a slice of `MigrationResult` to the error to show
67/// what collection of the migration set did succeed in being applied before the
68/// error was encountered.
69pub trait DatabaseError<T, E> {
70    /// Convert `E` to an [`Error`].
71    fn tern_result(self) -> TernResult<T>;
72
73    /// Same as `tern_result` but discard the returned value.
74    fn void_tern_result(self) -> TernResult<()>;
75
76    /// Convert `E` to an [`Error`] that has a given migration in the error
77    /// type's source.
78    fn tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<T>;
79
80    /// Same as `tern_migration_result` but discard the returned value.
81    fn void_tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<()>;
82
83    /// Attach an array of `MigrationResult`, representing a partially successful
84    /// migration operation, to the error.
85    fn with_report(self, report: &[MigrationResult]) -> TernResult<T>;
86}
87
88impl<T, E> DatabaseError<T, E> for Result<T, E>
89where
90    E: StdError + Send + Sync + 'static,
91{
92    fn void_tern_result(self) -> TernResult<()> {
93        match self {
94            Err(e) => Err(Error::Execute(Box::new(e))),
95            _ => Ok(()),
96        }
97    }
98
99    fn void_tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<()> {
100        match self {
101            Err(e) => Err(Error::ExecuteMigration(
102                Box::new(e),
103                migration.migration_id(),
104                migration.no_tx(),
105            )),
106            _ => Ok(()),
107        }
108    }
109
110    fn tern_result(self) -> TernResult<T> {
111        match self {
112            Ok(v) => Ok(v),
113            Err(e) => Err(Error::Execute(Box::new(e))),
114        }
115    }
116
117    fn tern_migration_result<M: Migration + ?Sized>(self, migration: &M) -> TernResult<T> {
118        match self {
119            Ok(v) => Ok(v),
120            Err(e) => Err(Error::ExecuteMigration(
121                Box::new(e),
122                migration.migration_id(),
123                migration.no_tx(),
124            )),
125        }
126    }
127
128    fn with_report(self, migrations: &[MigrationResult]) -> TernResult<T> {
129        match self {
130            Ok(v) => Ok(v),
131            Err(e) => Err(Error::Partial {
132                source: Box::new(e),
133                report: Report::new(migrations.to_vec()),
134            }),
135        }
136    }
137}