use crate::table::{Table, TableMeta};
use crate::DatabaseChange;
use crate::backend::{SqlGenerator, SqlVariant};
use crate::connectors::SqlRunner;
use std::rc::Rc;
pub struct Migration {
#[doc(hidden)]
pub schema: Option<String>,
#[doc(hidden)]
pub changes: Vec<DatabaseChange>,
}
impl Migration {
pub fn new() -> Migration {
Migration {
schema: None,
changes: Vec::new(),
}
}
pub fn schema<S: Into<String>>(self, schema: S) -> Migration {
Self {
schema: Some(schema.into()),
..self
}
}
pub fn make<T: SqlGenerator>(&self) -> String {
use DatabaseChange::*;
let mut changes = self.changes.clone();
let schema = self.schema.as_ref().map(|s| s.as_str());
changes.iter_mut().fold(String::new(), |mut sql, change| {
match change {
&mut CreateTable(ref mut t, ref mut cb)
| &mut CreateTableIfNotExists(ref mut t, ref mut cb) => {
cb(t); let sql_changes = t.make::<T>(false, schema);
let name = t.meta.name().clone();
sql.push_str(&match change {
CreateTable(_, _) => T::create_table(&name, schema),
CreateTableIfNotExists(_, _) => {
T::create_table_if_not_exists(&name, schema)
}
_ => unreachable!(),
});
sql.push_str(" (");
let l = sql_changes.columns.len();
for (i, slice) in sql_changes.columns.iter().enumerate() {
sql.push_str(slice);
if i < l - 1 {
sql.push_str(", ");
}
}
let l = sql_changes.constraints.len();
for (i, slice) in sql_changes.constraints.iter().enumerate() {
if sql_changes.columns.len() > 0 && i == 0 {
sql.push_str(", ")
}
sql.push_str(slice);
if i < l - 1 {
sql.push_str(", ")
}
}
if let Some(ref primary_key) = sql_changes.primary_key {
sql.push_str(", ");
sql.push_str(primary_key);
};
let l = sql_changes.foreign_keys.len();
for (i, slice) in sql_changes.foreign_keys.iter().enumerate() {
if sql_changes.columns.len() > 0 && i == 0 {
sql.push_str(", ")
}
sql.push_str(slice);
if i < l - 1 {
sql.push_str(", ")
}
}
sql.push_str(")");
if sql_changes.indices.len() > 0 {
sql.push_str(";");
sql.push_str(&sql_changes.indices.join(";"));
}
}
&mut DropTable(ref name) => sql.push_str(&T::drop_table(name, schema)),
&mut DropTableIfExists(ref name) => {
sql.push_str(&T::drop_table_if_exists(name, schema))
}
&mut RenameTable(ref old, ref new) => {
sql.push_str(&T::rename_table(old, new, schema))
}
&mut ChangeTable(ref mut t, ref mut cb) => {
cb(t);
let sql_changes = t.make::<T>(true, schema);
sql.push_str(&T::alter_table(&t.meta.name(), schema));
sql.push_str(" ");
let l = sql_changes.columns.len();
for (i, slice) in sql_changes.columns.iter().enumerate() {
sql.push_str(slice);
if i < l - 1 {
sql.push_str(", ");
}
}
let l = sql_changes.foreign_keys.len();
for (i, slice) in sql_changes.foreign_keys.iter().enumerate() {
if sql_changes.columns.len() > 0 && i == 0 {
sql.push_str(", ")
}
sql.push_str("ADD ");
sql.push_str(slice);
if i < l - 1 {
sql.push_str(", ")
}
}
if let Some(ref primary_key) = sql_changes.primary_key {
sql.push_str(", ");
sql.push_str("ADD ");
sql.push_str(primary_key);
};
if sql_changes.indices.len() > 0 {
sql.push_str(";");
sql.push_str(&sql_changes.indices.join(";"));
}
}
&mut CustomLine(ref line) => sql.push_str(line.as_str()),
}
sql.push_str(";");
sql
})
}
pub fn make_from(&self, variant: SqlVariant) -> String {
variant.run_for(self)
}
pub fn inject_custom<S: Into<String>>(&mut self, sql: S) {
self.changes.push(DatabaseChange::CustomLine(sql.into()));
}
pub fn revert<T: SqlGenerator>(&self) -> String {
unimplemented!()
}
pub fn execute<S: SqlGenerator, T: SqlRunner>(&self, runner: &mut T) {
runner.execute(self.make::<S>());
}
pub fn create_table<S: Into<String>, F: 'static>(&mut self, name: S, cb: F) -> &mut TableMeta
where
F: Fn(&mut Table),
{
self.changes
.push(DatabaseChange::CreateTable(Table::new(name), Rc::new(cb)));
match self.changes.last_mut().unwrap() {
&mut DatabaseChange::CreateTable(ref mut t, _) => &mut t.meta,
_ => unreachable!(),
}
}
pub fn create_table_if_not_exists<S: Into<String>, F: 'static>(
&mut self,
name: S,
cb: F,
) -> &mut TableMeta
where
F: Fn(&mut Table),
{
self.changes.push(DatabaseChange::CreateTableIfNotExists(
Table::new(name),
Rc::new(cb),
));
match self.changes.last_mut().unwrap() {
&mut DatabaseChange::CreateTableIfNotExists(ref mut t, _) => &mut t.meta,
_ => unreachable!(),
}
}
pub fn change_table<S: Into<String>, F: 'static>(&mut self, name: S, cb: F)
where
F: Fn(&mut Table),
{
let t = Table::new(name);
let c = DatabaseChange::ChangeTable(t, Rc::new(cb));
self.changes.push(c);
}
pub fn rename_table<S: Into<String>>(&mut self, old: S, new: S) {
self.changes
.push(DatabaseChange::RenameTable(old.into(), new.into()));
}
pub fn drop_table<S: Into<String>>(&mut self, name: S) {
self.changes.push(DatabaseChange::DropTable(name.into()));
}
pub fn drop_table_if_exists<S: Into<String>>(&mut self, name: S) {
self.changes
.push(DatabaseChange::DropTableIfExists(name.into()));
}
}