refinery_core/
error.rs

1use crate::{Migration, Report};
2use std::fmt;
3use std::path::PathBuf;
4use thiserror::Error as TError;
5
6/// An Error occurred during a migration cycle
7#[derive(Debug)]
8pub struct Error {
9    kind: Box<Kind>,
10    report: Option<Report>,
11}
12
13impl Error {
14    /// Instantiate a new Error
15    pub(crate) fn new(kind: Kind, report: Option<Report>) -> Error {
16        Error {
17            kind: Box::new(kind),
18            report,
19        }
20    }
21
22    /// Return the Report of the migration cycle if any
23    pub fn report(&self) -> Option<&Report> {
24        self.report.as_ref()
25    }
26
27    /// Return the kind of error occurred
28    pub fn kind(&self) -> &Kind {
29        &self.kind
30    }
31}
32
33impl fmt::Display for Error {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        write!(f, "{}", self.kind)
36    }
37}
38
39impl std::error::Error for Error {
40    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
41        self.kind.source()
42    }
43}
44
45/// Enum listing possible errors from Refinery.
46#[derive(Debug, TError)]
47pub enum Kind {
48    /// An Error from an invalid file name migration
49    #[error("migration name must be in the format V{{number}}__{{name}}")]
50    InvalidName,
51    /// An Error from an invalid version on a file name migration
52    #[error("migration version must be a valid integer")]
53    InvalidVersion,
54    /// An Error from a repeated version, migration version numbers must be unique
55    #[error("migration {0} is repeated, migration versions must be unique")]
56    RepeatedVersion(Migration),
57    /// An Error from an divergent version, the applied version is different to the filesystem one
58    #[error("applied migration {0} is different than filesystem one {1}")]
59    DivergentVersion(Migration, Migration),
60    /// An Error from an divergent version, the applied version is missing on the filesystem
61    #[error("migration {0} is missing from the filesystem")]
62    MissingVersion(Migration),
63    /// An Error from an invalid migrations path location
64    #[error("invalid migrations path {0}, {1}")]
65    InvalidMigrationPath(PathBuf, std::io::Error),
66    /// An Error parsing refinery Config
67    #[error("Error parsing config: {0}")]
68    ConfigError(String),
69    /// An Error from an underlying database connection Error
70    #[error("`{0}`, `{1}`")]
71    Connection(String, #[source] Box<dyn std::error::Error + Sync + Send>),
72    /// An Error from an invalid migration file (not UTF-8 etc)
73    #[error("invalid migration file at path {0}, {1}")]
74    InvalidMigrationFile(PathBuf, std::io::Error),
75}
76
77// Helper trait for adding custom messages and applied migrations to Connection error's.
78pub trait WrapMigrationError<T, E> {
79    fn migration_err(self, msg: &str, report: Option<&[Migration]>) -> Result<T, Error>;
80}
81
82impl<T, E> WrapMigrationError<T, E> for Result<T, E>
83where
84    E: std::error::Error + Send + Sync + 'static,
85{
86    fn migration_err(
87        self,
88        msg: &str,
89        applied_migrations: Option<&[Migration]>,
90    ) -> Result<T, Error> {
91        match self {
92            Ok(report) => Ok(report),
93            Err(err) => Err(Error {
94                kind: Box::new(Kind::Connection(msg.into(), Box::new(err))),
95                report: applied_migrations.map(|am| Report::new(am.to_vec())),
96            }),
97        }
98    }
99}