Archived
1
0
Fork 0

chore: improve error handling

This commit is contained in:
Dmitriy Pleshevskiy 2021-06-07 00:33:59 +03:00
parent 30482fbb79
commit a0ef76164f
12 changed files with 123 additions and 100 deletions

View file

@ -3,7 +3,7 @@
// #![allow(clippy::module_name_repetitions)] // #![allow(clippy::module_name_repetitions)]
// #![allow(clippy::missing_errors_doc)] // #![allow(clippy::missing_errors_doc)]
use crate::error::MigraResult; use crate::errors::MigraResult;
use crate::managers::{ManageMigrations, ManageTransaction}; use crate::managers::{ManageMigrations, ManageTransaction};
pub trait OpenDatabaseConnection pub trait OpenDatabaseConnection

View file

@ -1,5 +1,5 @@
use super::OpenDatabaseConnection; use super::OpenDatabaseConnection;
use crate::error::{Error, MigraResult, StdResult}; use crate::errors::{DbKind, Error, MigraResult, StdResult};
use crate::managers::{BatchExecute, ManageMigrations, ManageTransaction}; use crate::managers::{BatchExecute, ManageMigrations, ManageTransaction};
use crate::migration; use crate::migration;
use mysql::prelude::*; use mysql::prelude::*;
@ -15,7 +15,7 @@ impl OpenDatabaseConnection for Client {
fn manual(connection_string: &str, migrations_table_name: &str) -> MigraResult<Self> { fn manual(connection_string: &str, migrations_table_name: &str) -> MigraResult<Self> {
let conn = Pool::new_manual(1, 1, connection_string) let conn = Pool::new_manual(1, 1, connection_string)
.and_then(|pool| pool.get_conn()) .and_then(|pool| pool.get_conn())
.map_err(|_| Error::FailedDatabaseConnection)?; .map_err(|err| Error::db(err.into(), DbKind::DatabaseConnection))?;
Ok(Client { Ok(Client {
conn, conn,
@ -43,31 +43,28 @@ impl ManageMigrations for Client {
); );
self.batch_execute(&stmt) self.batch_execute(&stmt)
.map_err(|_| Error::FailedCreateMigrationsTable) .map_err(|err| Error::db(err, DbKind::CreateMigrationsTable))
} }
fn insert_migration(&mut self, name: &str) -> MigraResult<u64> { fn insert_migration(&mut self, name: &str) -> MigraResult<u64> {
let stmt = format!( let stmt = format!(
"INSERT INTO {} (name) VALUES ($1)", "INSERT INTO {} (name) VALUES (?)",
&self.migrations_table_name &self.migrations_table_name
); );
self.conn self.conn
.exec_first(&stmt, (name,)) .exec_first(&stmt, (name,))
.map(Option::unwrap_or_default) .map(Option::unwrap_or_default)
.map_err(|_| Error::FailedInsertMigration) .map_err(|err| Error::db(err.into(), DbKind::InsertMigration))
} }
fn delete_migration(&mut self, name: &str) -> MigraResult<u64> { fn delete_migration(&mut self, name: &str) -> MigraResult<u64> {
let stmt = format!( let stmt = format!("DELETE FROM {} WHERE name = ?", &self.migrations_table_name);
"DELETE FROM {} WHERE name = $1",
&self.migrations_table_name
);
self.conn self.conn
.exec_first(&stmt, (name,)) .exec_first(&stmt, (name,))
.map(Option::unwrap_or_default) .map(Option::unwrap_or_default)
.map_err(|_| Error::FailedDeleteMigration) .map_err(|err| Error::db(err.into(), DbKind::DeleteMigration))
} }
fn get_applied_migrations(&mut self) -> MigraResult<migration::List> { fn get_applied_migrations(&mut self) -> MigraResult<migration::List> {
@ -79,7 +76,7 @@ impl ManageMigrations for Client {
self.conn self.conn
.query::<String, _>(stmt) .query::<String, _>(stmt)
.map(From::from) .map(From::from)
.map_err(|_| Error::FailedGetAppliedMigrations) .map_err(|err| Error::db(err.into(), DbKind::GetAppliedMigrations))
} }
} }

View file

@ -1,5 +1,5 @@
use super::OpenDatabaseConnection; use super::OpenDatabaseConnection;
use crate::error::{Error, MigraResult, StdResult}; use crate::errors::{DbKind, Error, MigraResult, StdResult};
use crate::managers::{BatchExecute, ManageMigrations, ManageTransaction}; use crate::managers::{BatchExecute, ManageMigrations, ManageTransaction};
use crate::migration; use crate::migration;
use postgres::{Client as PostgresClient, NoTls}; use postgres::{Client as PostgresClient, NoTls};
@ -21,7 +21,7 @@ impl fmt::Debug for Client {
impl OpenDatabaseConnection for Client { impl OpenDatabaseConnection for Client {
fn manual(connection_string: &str, migrations_table_name: &str) -> MigraResult<Self> { fn manual(connection_string: &str, migrations_table_name: &str) -> MigraResult<Self> {
let client = PostgresClient::connect(connection_string, NoTls) let client = PostgresClient::connect(connection_string, NoTls)
.map_err(|_| Error::FailedDatabaseConnection)?; .map_err(|err| Error::db(err.into(), DbKind::DatabaseConnection))?;
Ok(Client { Ok(Client {
client, client,
migrations_table_name: migrations_table_name.to_owned(), migrations_table_name: migrations_table_name.to_owned(),
@ -48,7 +48,7 @@ impl ManageMigrations for Client {
); );
self.batch_execute(&stmt) self.batch_execute(&stmt)
.map_err(|_| Error::FailedCreateMigrationsTable) .map_err(|err| Error::db(err, DbKind::CreateMigrationsTable))
} }
fn insert_migration(&mut self, name: &str) -> MigraResult<u64> { fn insert_migration(&mut self, name: &str) -> MigraResult<u64> {
@ -59,7 +59,7 @@ impl ManageMigrations for Client {
self.client self.client
.execute(stmt.as_str(), &[&name]) .execute(stmt.as_str(), &[&name])
.map_err(|_| Error::FailedInsertMigration) .map_err(|err| Error::db(err.into(), DbKind::InsertMigration))
} }
fn delete_migration(&mut self, name: &str) -> MigraResult<u64> { fn delete_migration(&mut self, name: &str) -> MigraResult<u64> {
@ -70,7 +70,7 @@ impl ManageMigrations for Client {
self.client self.client
.execute(stmt.as_str(), &[&name]) .execute(stmt.as_str(), &[&name])
.map_err(|_| Error::FailedDeleteMigration) .map_err(|err| Error::db(err.into(), DbKind::DeleteMigration))
} }
fn get_applied_migrations(&mut self) -> MigraResult<migration::List> { fn get_applied_migrations(&mut self) -> MigraResult<migration::List> {
@ -87,7 +87,7 @@ impl ManageMigrations for Client {
.collect::<Result<Vec<String>, _>>() .collect::<Result<Vec<String>, _>>()
}) })
.map(From::from) .map(From::from)
.map_err(|_| Error::FailedGetAppliedMigrations) .map_err(|err| Error::db(err.into(), DbKind::GetAppliedMigrations))
} }
} }

View file

@ -1,5 +1,5 @@
use super::OpenDatabaseConnection; use super::OpenDatabaseConnection;
use crate::error::{Error, MigraResult, StdResult}; use crate::errors::{DbKind, Error, MigraResult, StdResult};
use crate::managers::{BatchExecute, ManageMigrations, ManageTransaction}; use crate::managers::{BatchExecute, ManageMigrations, ManageTransaction};
use crate::migration; use crate::migration;
use rusqlite::Connection; use rusqlite::Connection;
@ -12,8 +12,8 @@ pub struct Client {
impl OpenDatabaseConnection for Client { impl OpenDatabaseConnection for Client {
fn manual(connection_string: &str, migrations_table_name: &str) -> MigraResult<Self> { fn manual(connection_string: &str, migrations_table_name: &str) -> MigraResult<Self> {
let conn = let conn = Connection::open(connection_string)
Connection::open(connection_string).map_err(|_| Error::FailedDatabaseConnection)?; .map_err(|err| Error::db(err.into(), DbKind::DatabaseConnection))?;
Ok(Client { Ok(Client {
conn, conn,
migrations_table_name: migrations_table_name.to_owned(), migrations_table_name: migrations_table_name.to_owned(),
@ -40,7 +40,7 @@ impl ManageMigrations for Client {
); );
self.batch_execute(&stmt) self.batch_execute(&stmt)
.map_err(|_| Error::FailedCreateMigrationsTable) .map_err(|err| Error::db(err, DbKind::CreateMigrationsTable))
} }
fn insert_migration(&mut self, name: &str) -> MigraResult<u64> { fn insert_migration(&mut self, name: &str) -> MigraResult<u64> {
@ -52,7 +52,7 @@ impl ManageMigrations for Client {
self.conn self.conn
.execute(&stmt, [name]) .execute(&stmt, [name])
.map(|res| res as u64) .map(|res| res as u64)
.map_err(|_| Error::FailedInsertMigration) .map_err(|err| Error::db(err.into(), DbKind::InsertMigration))
} }
fn delete_migration(&mut self, name: &str) -> MigraResult<u64> { fn delete_migration(&mut self, name: &str) -> MigraResult<u64> {
@ -64,7 +64,7 @@ impl ManageMigrations for Client {
self.conn self.conn
.execute(&stmt, [name]) .execute(&stmt, [name])
.map(|res| res as u64) .map(|res| res as u64)
.map_err(|_| Error::FailedDeleteMigration) .map_err(|err| Error::db(err.into(), DbKind::DeleteMigration))
} }
fn get_applied_migrations(&mut self) -> MigraResult<migration::List> { fn get_applied_migrations(&mut self) -> MigraResult<migration::List> {
@ -80,7 +80,7 @@ impl ManageMigrations for Client {
.collect::<Result<Vec<String>, _>>() .collect::<Result<Vec<String>, _>>()
}) })
.map(From::from) .map(From::from)
.map_err(|_| Error::FailedGetAppliedMigrations) .map_err(|err| Error::db(err.into(), DbKind::GetAppliedMigrations))
} }
} }

View file

@ -1,56 +0,0 @@
use std::fmt;
use std::io;
pub type StdResult<T> = Result<T, Box<dyn std::error::Error + 'static + Sync + Send>>;
pub type MigraResult<T> = Result<T, Error>;
#[derive(Debug)]
pub enum Error {
FailedDatabaseConnection,
FailedOpenTransaction,
FailedCommitTransaction,
FailedRollbackTransaction,
FailedCreateMigrationsTable,
FailedApplySql,
FailedInsertMigration,
FailedDeleteMigration,
FailedGetAppliedMigrations,
Io(io::Error),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::FailedDatabaseConnection => fmt.write_str("Failed database connection"),
Error::FailedOpenTransaction => fmt.write_str("Failed to open a transaction"),
Error::FailedCommitTransaction => fmt.write_str("Failed to commit a transaction"),
Error::FailedRollbackTransaction => fmt.write_str("Failed to rollback a transaction"),
Error::FailedCreateMigrationsTable => {
fmt.write_str("Failed to create a migrations table")
}
Error::FailedApplySql => fmt.write_str("Failed to apply sql"),
Error::FailedInsertMigration => fmt.write_str("Failed to insert a migration"),
Error::FailedDeleteMigration => fmt.write_str("Failed to delete a migration"),
Error::FailedGetAppliedMigrations => fmt.write_str("Failed to get applied migrations"),
Error::Io(ref error) => write!(fmt, "{}", error),
}
}
}
impl std::error::Error for Error {}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other)
}
}
impl From<io::Error> for Error {
#[inline]
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}

86
migra/src/errors.rs Normal file
View file

@ -0,0 +1,86 @@
use std::fmt;
use std::io;
pub type StdError = Box<dyn std::error::Error + 'static + Sync + Send>;
pub type StdResult<T> = Result<T, StdError>;
pub type MigraResult<T> = Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Db(DbError),
Io(io::Error),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Db(ref error) => write!(fmt, "{}", error),
Error::Io(ref error) => write!(fmt, "{}", error),
}
}
}
impl std::error::Error for Error {}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other)
}
}
impl From<io::Error> for Error {
#[inline]
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl Error {
#[must_use]
pub fn db(origin: StdError, kind: DbKind) -> Self {
Error::Db(DbError { kind, origin })
}
}
#[derive(Debug)]
pub enum DbKind {
DatabaseConnection,
OpenTransaction,
CommitTransaction,
RollbackTransaction,
CreateMigrationsTable,
ApplySql,
InsertMigration,
DeleteMigration,
GetAppliedMigrations,
}
impl fmt::Display for DbKind {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DbKind::DatabaseConnection => fmt.write_str("Failed database connection"),
DbKind::OpenTransaction => fmt.write_str("Failed to open a transaction"),
DbKind::CommitTransaction => fmt.write_str("Failed to commit a transaction"),
DbKind::RollbackTransaction => fmt.write_str("Failed to rollback a transaction"),
DbKind::CreateMigrationsTable => fmt.write_str("Failed to create a migrations table"),
DbKind::ApplySql => fmt.write_str("Failed to apply sql"),
DbKind::InsertMigration => fmt.write_str("Failed to insert a migration"),
DbKind::DeleteMigration => fmt.write_str("Failed to delete a migration"),
DbKind::GetAppliedMigrations => fmt.write_str("Failed to get applied migrations"),
}
}
}
#[derive(Debug)]
pub struct DbError {
kind: DbKind,
origin: StdError,
}
impl fmt::Display for DbError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{} - {}", &self.kind, &self.origin)
}
}

View file

@ -1,4 +1,4 @@
use crate::error::MigraResult; use crate::errors::MigraResult;
use crate::migration; use crate::migration;
use crate::migration::{DOWNGRADE_MIGRATION_FILE_NAME, UPGRADE_MIGRATION_FILE_NAME}; use crate::migration::{DOWNGRADE_MIGRATION_FILE_NAME, UPGRADE_MIGRATION_FILE_NAME};
use std::io; use std::io;

View file

@ -8,7 +8,7 @@ pub mod fs;
pub mod managers; pub mod managers;
pub mod migration; pub mod migration;
mod error; mod errors;
pub use error::{Error, MigraResult as Result, StdResult}; pub use errors::{Error, MigraResult as Result, StdResult};
pub use migration::Migration; pub use migration::Migration;

View file

@ -1,4 +1,4 @@
use crate::error::{Error, MigraResult, StdResult}; use crate::errors::{DbKind, Error, MigraResult, StdResult};
use crate::migration::{self, Migration}; use crate::migration::{self, Migration};
use std::path::Path; use std::path::Path;
@ -9,26 +9,24 @@ pub trait BatchExecute {
pub trait ManageTransaction: BatchExecute { pub trait ManageTransaction: BatchExecute {
fn begin_transaction(&mut self) -> MigraResult<()> { fn begin_transaction(&mut self) -> MigraResult<()> {
self.batch_execute("BEGIN") self.batch_execute("BEGIN")
.map_err(|_| Error::FailedOpenTransaction) .map_err(|err| Error::db(err, DbKind::OpenTransaction))
} }
fn rollback_transaction(&mut self) -> MigraResult<()> { fn rollback_transaction(&mut self) -> MigraResult<()> {
self.batch_execute("ROLLBACK") self.batch_execute("ROLLBACK")
.map_err(|_| Error::FailedRollbackTransaction) .map_err(|err| Error::db(err, DbKind::RollbackTransaction))
} }
fn commit_transaction(&mut self) -> MigraResult<()> { fn commit_transaction(&mut self) -> MigraResult<()> {
self.batch_execute("COMMIT") self.batch_execute("COMMIT")
.map_err(|_| Error::FailedCommitTransaction) .map_err(|err| Error::db(err, DbKind::CommitTransaction))
} }
} }
pub trait ManageMigrations: BatchExecute { pub trait ManageMigrations: BatchExecute {
fn apply_sql(&mut self, sql: &str) -> MigraResult<()> { fn apply_sql(&mut self, sql: &str) -> MigraResult<()> {
self.batch_execute(sql).map_err(|err| { self.batch_execute(sql)
dbg!(err); .map_err(|err| Error::db(err, DbKind::ApplySql))
Error::FailedApplySql
})
} }
fn create_migrations_table(&mut self) -> MigraResult<()>; fn create_migrations_table(&mut self) -> MigraResult<()>;

View file

@ -4,11 +4,7 @@ use crate::opts::ApplyCommandOpt;
pub(crate) fn apply_sql(app: &App, cmd_opts: &ApplyCommandOpt) -> migra::StdResult<()> { pub(crate) fn apply_sql(app: &App, cmd_opts: &ApplyCommandOpt) -> migra::StdResult<()> {
let config = app.config()?; let config = app.config()?;
let mut client = crate::client::create( let mut client = crate::client::create_from_config(&config)?;
&config.database.client(),
&config.database.connection_string()?,
&config.migrations.table_name(),
)?;
let file_contents = cmd_opts let file_contents = cmd_opts
.file_paths .file_paths

View file

@ -1,5 +1,5 @@
use super::client_rusqlite::Connection::AnyConnection; use super::client_rusqlite::Connection::AnyConnection;
use crate::error::StdResult; use crate::errors::StdResult;
pub trait ManageTransaction { pub trait ManageTransaction {
fn begin_transaction(&self, conn: &mut AnyConnection) -> migra::StdResult<()>; fn begin_transaction(&self, conn: &mut AnyConnection) -> migra::StdResult<()>;

View file

@ -20,9 +20,11 @@ use app::App;
use config::Config; use config::Config;
use opts::{AppOpt, StructOpt}; use opts::{AppOpt, StructOpt};
fn main() -> migra::StdResult<()> { fn main() {
#[cfg(feature = "dotenv")] #[cfg(feature = "dotenv")]
dotenv::dotenv().ok(); dotenv::dotenv().ok();
App::new(AppOpt::from_args()).run_command() if let Err(err) = App::new(AppOpt::from_args()).run_command() {
eprintln!("Error: {}", err);
}
} }