diff --git a/migra/src/fs.rs b/migra/src/fs.rs index 4787963..82d4e56 100644 --- a/migra/src/fs.rs +++ b/migra/src/fs.rs @@ -1,11 +1,13 @@ use crate::error::MigraResult; use crate::migration; +use crate::migration::{DOWNGRADE_MIGRATION_FILE_NAME, UPGRADE_MIGRATION_FILE_NAME}; use std::io; use std::path::Path; #[must_use] pub fn is_migration_dir(path: &Path) -> bool { - path.join("up.sql").exists() && path.join("down.sql").exists() + path.join(UPGRADE_MIGRATION_FILE_NAME).exists() + && path.join(DOWNGRADE_MIGRATION_FILE_NAME).exists() } pub fn get_all_migrations(dir_path: &Path) -> MigraResult { diff --git a/migra/src/lib.rs b/migra/src/lib.rs index 75f3b2b..dba2c1c 100644 --- a/migra/src/lib.rs +++ b/migra/src/lib.rs @@ -2,12 +2,15 @@ #![deny(clippy::all, clippy::pedantic)] #![allow(clippy::missing_errors_doc)] -mod error; pub mod fs; +pub mod managers; pub mod migration; +mod error; pub use error::{Error, MigraResult as Result}; +pub use migration::Migration; + /* # list diff --git a/migra/src/managers.rs b/migra/src/managers.rs new file mode 100644 index 0000000..347862d --- /dev/null +++ b/migra/src/managers.rs @@ -0,0 +1,41 @@ +use crate::error::MigraResult; +use crate::migration::{self, Migration}; + +pub trait ManageTransaction { + fn begin_transaction(&mut self) -> MigraResult<()>; + + fn rollback_transaction(&mut self) -> MigraResult<()>; + + fn commit_transaction(&mut self) -> MigraResult<()>; +} + +pub trait ManageMigrations { + fn apply_sql(&mut self, sql_content: &str) -> MigraResult<()>; + + fn create_migrations_table(&mut self) -> MigraResult<()>; + + fn insert_migration(&mut self, name: &str) -> MigraResult; + + fn delete_migration(&mut self, name: &str) -> MigraResult; + + fn applied_migrations(&mut self) -> MigraResult; + + fn apply_upgrade_migration(&mut self, migration: &Migration) -> MigraResult<()> { + let content = migration.read_upgrade_migration_sql()?; + + self.create_migrations_table()?; + self.apply_sql(&content)?; + self.insert_migration(migration.name())?; + + Ok(()) + } + + fn apply_downgrade_migration(&mut self, migration: &Migration) -> MigraResult<()> { + let content = migration.read_downgrade_migration_sql()?; + + self.apply_sql(&content)?; + self.delete_migration(migration.name())?; + + Ok(()) + } +} diff --git a/migra/src/migration.rs b/migra/src/migration.rs index a7a82a7..ed1dc2e 100644 --- a/migra/src/migration.rs +++ b/migra/src/migration.rs @@ -1,15 +1,27 @@ +use std::fs; +use std::io; use std::iter::FromIterator; +use std::path::{Path, PathBuf}; + +pub(crate) const UPGRADE_MIGRATION_FILE_NAME: &str = "up.sql"; +pub(crate) const DOWNGRADE_MIGRATION_FILE_NAME: &str = "down.sql"; #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Migration { + path: PathBuf, name: String, } impl Migration { #[must_use] - pub fn new(name: &str) -> Self { + pub fn new(path: &Path) -> Self { Migration { - name: name.to_owned(), + path: PathBuf::from(path), + name: path + .file_name() + .and_then(std::ffi::OsStr::to_str) + .expect("Cannot read migration name") + .to_string(), } } @@ -17,6 +29,14 @@ impl Migration { pub fn name(&self) -> &String { &self.name } + + pub fn read_upgrade_migration_sql(&self) -> io::Result { + fs::read_to_string(self.path.join(UPGRADE_MIGRATION_FILE_NAME)) + } + + pub fn read_downgrade_migration_sql(&self) -> io::Result { + fs::read_to_string(self.path.join(DOWNGRADE_MIGRATION_FILE_NAME)) + } } #[derive(Debug, Clone, Default, PartialEq, Eq)] @@ -24,7 +44,7 @@ pub struct List { inner: Vec, } -impl> From> for List { +impl> From> for List { fn from(list: Vec) -> Self { List { inner: list.iter().map(AsRef::as_ref).map(Migration::new).collect(), @@ -50,8 +70,8 @@ impl List { self.inner.push(migration) } - pub fn push_name(&mut self, name: &str) { - self.inner.push(Migration::new(name)) + pub fn push_name>(&mut self, path: P) { + self.inner.push(Migration::new(path.as_ref())) } #[must_use] @@ -67,7 +87,16 @@ impl List { } #[must_use] - pub fn maybe_next<'a>(&self, name: &'a str) -> Option<&'a str> { + pub fn maybe_contains<'a>(&self, name: &'a str) -> Option<&'a str> { + if self.contains_name(name) { + Some(name) + } else { + None + } + } + + #[must_use] + pub fn maybe_missed<'a>(&self, name: &'a str) -> Option<&'a str> { if self.contains_name(name) { None } else { @@ -118,10 +147,10 @@ mod tests { fn push_migration_to_list() { let mut list = List::new(); - list.push(Migration::new(FIRST_MIGRATION)); + list.push(Migration::new(&PathBuf::from(FIRST_MIGRATION))); assert_eq!(list, List::from(vec![FIRST_MIGRATION])); - list.push(Migration::new(&String::from(SECOND_MIGRATION))); + list.push(Migration::new(&PathBuf::from(SECOND_MIGRATION))); assert_eq!(list, List::from(vec![FIRST_MIGRATION, SECOND_MIGRATION])) } @@ -140,8 +169,14 @@ mod tests { fn contains_migration() { let list = List::from(vec![FIRST_MIGRATION]); - assert_eq!(list.contains(&Migration::new(FIRST_MIGRATION)), true); - assert_eq!(list.contains(&Migration::new(SECOND_MIGRATION)), false); + assert_eq!( + list.contains(&Migration::new(&PathBuf::from(FIRST_MIGRATION))), + true + ); + assert_eq!( + list.contains(&Migration::new(&PathBuf::from(SECOND_MIGRATION))), + false + ); } #[test] @@ -156,8 +191,8 @@ mod tests { fn maybe_next_migration_name() { let list = List::from(vec![FIRST_MIGRATION]); - assert_eq!(list.maybe_next(FIRST_MIGRATION), None); - assert_eq!(list.maybe_next(SECOND_MIGRATION), Some(SECOND_MIGRATION)); + assert_eq!(list.maybe_missed(FIRST_MIGRATION), None); + assert_eq!(list.maybe_missed(SECOND_MIGRATION), Some(SECOND_MIGRATION)); } #[test]