1use crate::{Migration, Report};
2use std::fmt;
3use std::path::PathBuf;
4use thiserror::Error as TError;
5
6#[derive(Debug)]
8pub struct Error {
9 kind: Box<Kind>,
10 report: Option<Report>,
11}
12
13impl Error {
14 pub(crate) fn new(kind: Kind, report: Option<Report>) -> Error {
16 Error {
17 kind: Box::new(kind),
18 report,
19 }
20 }
21
22 pub fn report(&self) -> Option<&Report> {
24 self.report.as_ref()
25 }
26
27 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#[derive(Debug, TError)]
47pub enum Kind {
48 #[error("migration name must be in the format V{{number}}__{{name}}")]
50 InvalidName,
51 #[error("migration version must be a valid integer")]
53 InvalidVersion,
54 #[error("migration {0} is repeated, migration versions must be unique")]
56 RepeatedVersion(Migration),
57 #[error("applied migration {0} is different than filesystem one {1}")]
59 DivergentVersion(Migration, Migration),
60 #[error("migration {0} is missing from the filesystem")]
62 MissingVersion(Migration),
63 #[error("invalid migrations path {0}, {1}")]
65 InvalidMigrationPath(PathBuf, std::io::Error),
66 #[error("Error parsing config: {0}")]
68 ConfigError(String),
69 #[error("`{0}`, `{1}`")]
71 Connection(String, #[source] Box<dyn std::error::Error + Sync + Send>),
72 #[error("invalid migration file at path {0}, {1}")]
74 InvalidMigrationFile(PathBuf, std::io::Error),
75}
76
77pub 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}