use crate::parser::error::ParseErrorKind;
use crate::parser::{
collect_versions_from_directory,
ParseError,
ReferenceToStaticDialect,
ResourceCollection,
StatementCollection
};
use std::collections::BTreeMap;
use std::fs;
use std::path::PathBuf;
#[derive(Clone, PartialEq)]
pub enum MigrationDirection {
Up,
Down,
}
impl MigrationDirection {
pub fn verb(&self) -> &'static str {
match self {
Self::Up => "Migrating",
Self::Down => "Rolling back",
}
}
pub fn noun(&self) -> &'static str {
match self {
Self::Up => "Migration",
Self::Down => "Rollback",
}
}
pub fn filename(&self) -> &'static str {
match self {
Self::Up => "up.sql",
Self::Down => "down.sql",
}
}
}
pub struct Migration {
pub path: PathBuf,
#[allow(dead_code)]
sql: String,
pub statements: StatementCollection,
}
impl Migration {
pub fn new(dialect: ReferenceToStaticDialect, path: PathBuf, sql: &str) -> Result<Migration, ParseError> {
let sql = sql.to_string();
let statements = StatementCollection::new(dialect).parse_sql(&sql);
Ok(Migration { path, sql, statements: statements? })
}
pub fn from_file(dialect: ReferenceToStaticDialect, path: PathBuf) -> Result<Self, ParseError> {
if !path.exists() {
return Err(ParseError { kind: ParseErrorKind::FileNotFound(path) })
}
let sql = fs::read_to_string(&path)
.map_err(|e| ParseError { kind: ParseErrorKind::Io { path: path.clone(), source: Box::new(e) } })?;
Ok(Migration::new(dialect, path, &sql)?)
}
pub fn resources(&self) -> ResourceCollection {
ResourceCollection::from_statement_collection(&self.statements)
}
}
pub struct MigrationCollection {
direction: MigrationDirection,
inner: BTreeMap<i64, Migration>,
}
impl MigrationCollection {
pub fn new(
direction: &MigrationDirection,
migrations: BTreeMap<i64, Migration>
) -> Self {
MigrationCollection {
direction: direction.clone(),
inner: migrations
}
}
pub fn from_directory(
dialect: ReferenceToStaticDialect,
directory: &str,
direction: &MigrationDirection,
from_version_id: i64,
to_version_id: i64,
) -> Result<Self, ParseError> {
let versions = collect_versions_from_directory(
directory, from_version_id, to_version_id, true
)?;
let migrations = versions
.iter()
.map(|(id, path)| {
Ok((*id, Migration::from_file(dialect, path.join(direction.filename()))?))
})
.collect::<Result<BTreeMap<i64, Migration>, ParseError>>()?;
Ok(MigrationCollection::new(direction, migrations))
}
pub fn iter(&self) -> Vec<(&i64, &Migration)> {
if self.direction == MigrationDirection::Up {
self.inner.iter().collect()
} else {
self.inner.iter().rev().collect()
}
}
}