use crate::Syntax;
use crate::detect::TableDef;
use crate::migrations::MigrationWriter;
use crate::migrations::types::Type;
use crate::migrations::utils::find_column_or_unwrap;
use crate::migrations::writers::add_column;
use crate::migrations::writers::alter_column_type_down;
use crate::migrations::writers::alter_column_type_up;
use crate::migrations::writers::drop_column;
use crate::migrations::writers::rename_column;
pub struct Change {
pub(super) tabledef: Option<TableDef>,
pub(super) column_name: String,
new_name: Option<String>,
set_null: Option<bool>,
new_ty: Option<Type>,
}
impl Change {
pub(crate) fn new(tabledef: Option<TableDef>, column_name: String) -> Change {
Change {
tabledef,
column_name,
new_name: None,
set_null: None,
new_ty: None,
}
}
pub fn rename(mut self, newname: impl Into<String>) -> Change {
self.new_name = Some(newname.into());
self
}
pub fn to_type(mut self, ty: Type) -> Change {
self.new_ty = Some(ty);
self
}
pub fn null(mut self) -> Change {
self.set_null = Some(true);
self
}
pub fn not_null(mut self) -> Change {
self.set_null = Some(false);
self
}
pub fn drop_column(self) -> DropColumn {
DropColumn {
tabledef: self.tabledef,
column_name: self.column_name,
}
}
}
impl MigrationWriter for Change {
fn down_sql(&self, syntax: Syntax) -> Vec<String> {
let tabledef = match &self.tabledef {
Some(x) => x,
None => return vec![],
};
let mut commands = Vec::default();
let ident = tabledef.ident();
let col = find_column_or_unwrap(tabledef, &self.column_name);
let nullable = col.null();
let null_changed = self.set_null.is_some() && col.null() != nullable;
let type_changed = self.new_ty.is_some()
&& self
.new_ty
.as_ref()
.map(|t| t.db_type(syntax) != col.ty())
.unwrap_or_default();
let ty = Type::parse_db_type(syntax, col.ty()).db_type(syntax);
let columnname: &str = self
.new_name
.as_deref()
.unwrap_or(self.column_name.as_str());
if type_changed || null_changed {
let mut alters =
alter_column_type_down(syntax, tabledef, col, columnname, ty, nullable);
commands.append(&mut alters);
}
if let Some(new_name) = &self.new_name {
commands.push(rename_column(syntax, ident, new_name, &self.column_name));
}
commands
}
fn up_sql(&self, syntax: Syntax) -> Vec<String> {
let tabledef = match &self.tabledef {
Some(x) => x,
None => return vec![],
};
let mut commands = Vec::default();
let ident = tabledef.ident();
let col = find_column_or_unwrap(tabledef, &self.column_name);
let nullable = self.set_null.unwrap_or(col.null());
let null_changed = self.set_null.is_some() && col.null() != nullable;
if let Some(new_name) = &self.new_name {
commands.push(rename_column(syntax, ident, &self.column_name, new_name));
}
let columnname: &str = self
.new_name
.as_deref()
.unwrap_or(self.column_name.as_str());
let ty = self
.new_ty
.as_ref()
.map(|t| t.db_type(syntax))
.unwrap_or(col.ty().to_string());
let type_changed = self.new_ty.is_some()
&& self
.new_ty
.as_ref()
.map(|t| t.db_type(syntax) != col.ty())
.unwrap_or_default();
if type_changed || null_changed {
let mut alters = alter_column_type_up(syntax, tabledef, col, columnname, ty, nullable);
commands.append(&mut alters);
}
commands
}
}
pub struct DropColumn {
tabledef: Option<TableDef>,
column_name: String,
}
impl MigrationWriter for DropColumn {
fn up_sql(&self, syntax: Syntax) -> Vec<String> {
let tabledef = match &self.tabledef {
Some(x) => x,
None => return vec![],
};
vec![drop_column(syntax, tabledef, &self.column_name)]
}
fn down_sql(&self, syntax: Syntax) -> Vec<String> {
let tabledef = match &self.tabledef {
Some(x) => x,
None => return vec![],
};
let col = find_column_or_unwrap(tabledef, &self.column_name);
let ty = Type::parse_db_type(syntax, col.ty()).db_type(syntax);
let nullable = col.null();
vec![add_column(
syntax,
tabledef,
&self.column_name,
ty,
nullable,
)]
}
}