use super::{
backend::SqlGenerator, ConstraintChange, ForeignKeyChange, IndexChange, PrimaryKeyChange,
TableChange,
};
use crate::types::Type;
use std::fmt::{Debug, Formatter, Result as FmtResult};
impl Debug for TableChange {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str("TableChange")
}
}
impl Debug for IndexChange {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str("IndexChange")
}
}
impl Debug for ConstraintChange {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str("ConstraintChange")
}
}
impl Debug for ForeignKeyChange {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("ForeignKeyChange")
}
}
impl Debug for PrimaryKeyChange {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("PrimaryKeyChange")
}
}
#[derive(Debug, Clone)]
pub struct Table {
pub meta: TableMeta,
columns: Vec<TableChange>,
indices: Vec<IndexChange>,
constraints: Vec<ConstraintChange>,
foreign_keys: Vec<ForeignKeyChange>,
primary_key: Option<PrimaryKeyChange>,
}
#[derive(Debug, Clone)]
pub struct SqlChanges {
pub(crate) columns: Vec<String>,
pub(crate) indices: Vec<String>,
pub(crate) constraints: Vec<String>,
pub(crate) foreign_keys: Vec<String>,
pub(crate) primary_key: Option<String>,
}
impl Table {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
meta: TableMeta::new(name.into()),
columns: vec![],
indices: vec![],
constraints: vec![],
foreign_keys: vec![],
primary_key: None,
}
}
pub fn add_column<S: Into<String>>(&mut self, name: S, _type: Type) -> &mut Type {
self.columns
.push(TableChange::AddColumn(name.into(), _type));
match self.columns.last_mut().unwrap() {
&mut TableChange::AddColumn(_, ref mut c) => c,
_ => unreachable!(),
}
}
pub fn drop_column<S: Into<String>>(&mut self, name: S) {
self.columns.push(TableChange::DropColumn(name.into()));
}
pub fn rename_column<S: Into<String>>(&mut self, old: S, new: S) {
self.columns
.push(TableChange::RenameColumn(old.into(), new.into()));
}
pub fn inject_custom<S: Into<String>>(&mut self, sql: S) {
self.columns.push(TableChange::CustomLine(sql.into()));
}
pub fn add_index<S: Into<String>>(&mut self, name: S, columns: Type) {
match columns.inner {
crate::types::BaseType::Index(_) => {}
_ => panic!("Calling `add_index` with a non-`Index` type is not allowed!"),
}
self.indices.push(IndexChange::AddIndex {
table: self.meta.name.clone(),
index: name.into(),
columns,
});
}
pub fn add_constraint<S: Into<String>>(&mut self, name: S, columns: Type) {
match columns.inner {
crate::types::BaseType::Constraint(_, _) => {}
_ => panic!("Calling `add_index` with a non-`Constraint` type is not allowed!"),
}
self.constraints.push(ConstraintChange::AddConstraint {
index: name.into(),
columns,
});
}
pub fn add_partial_index<S: Into<String>>(&mut self, name: S, columns: Type, conditions: S) {
match columns.inner {
crate::types::BaseType::Index(_) => {}
_ => panic!("Calling `add_index` with a non-`Index` type is not allowed!"),
}
self.indices.push(IndexChange::AddPartialIndex {
table: self.meta.name.clone(),
index: name.into(),
columns,
conditions: conditions.into(),
});
}
pub fn drop_index<S: Into<String>>(&mut self, name: S) {
self.indices.push(IndexChange::RemoveIndex(
self.meta.name.clone(),
name.into(),
));
}
pub fn add_foreign_key(
&mut self,
columns_on_this_side: &[&str],
related_table: &str,
columns_on_that_side: &[&str],
) {
let table = related_table.into();
let columns = columns_on_this_side
.into_iter()
.map(|c| String::from(*c))
.collect();
let relation_columns = columns_on_that_side
.into_iter()
.map(|c| String::from(*c))
.collect();
self.foreign_keys.push(ForeignKeyChange::AddForeignKey {
table,
columns,
relation_columns,
})
}
pub fn set_primary_key(&mut self, columns: &[&str]) {
let primary_key =
PrimaryKeyChange::AddPrimaryKey(columns.into_iter().map(|s| s.to_string()).collect());
self.primary_key = Some(primary_key);
}
pub fn make<T: SqlGenerator>(&mut self, ex: bool, schema: Option<&str>) -> SqlChanges {
use ConstraintChange as CC;
use ForeignKeyChange as KFC;
use IndexChange as IC;
use PrimaryKeyChange as PKC;
use TableChange as TC;
let columns = self
.columns
.iter_mut()
.map(|change| match change {
&mut TC::AddColumn(ref name, ref col) => T::add_column(ex, schema, name, &col),
&mut TC::DropColumn(ref name) => T::drop_column(name),
&mut TC::RenameColumn(ref old, ref new) => T::rename_column(old, new),
&mut TC::ChangeColumn(ref mut name, _, _) => T::alter_table(name, schema),
&mut TC::CustomLine(ref sql) => sql.clone(),
})
.collect();
let indices = self
.indices
.iter()
.map(|change| match change {
IC::AddIndex {
index,
table,
columns,
} => T::create_index(table, schema, index, columns),
IC::AddPartialIndex {
index,
table,
columns,
conditions,
} => T::create_partial_index(table, schema, index, columns, conditions),
IC::RemoveIndex(_, index) => T::drop_index(index),
})
.collect();
let primary_key = self.primary_key.as_ref().map(|pk| match pk {
PKC::AddPrimaryKey(ref cols) => T::add_primary_key(cols),
});
let constraints = self
.constraints
.iter()
.map(|c| match c {
CC::AddConstraint { index, columns } => T::create_constraint(index, columns, schema),
})
.collect();
let foreign_keys = self
.foreign_keys
.iter()
.map(|change| match change {
KFC::AddForeignKey {
columns,
table,
relation_columns,
} => T::add_foreign_key(
columns.as_slice(),
table,
relation_columns.as_slice(),
schema,
),
})
.collect();
SqlChanges {
columns,
indices,
constraints,
foreign_keys,
primary_key,
}
}
}
#[derive(Debug, Clone)]
pub struct TableMeta {
pub name: String,
pub encoding: String,
}
impl TableMeta {
pub fn new(name: String) -> Self {
Self {
name,
encoding: "utf-8".to_owned(),
}
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn encoding<S: Into<String>>(&mut self, enc: S) -> &mut TableMeta {
self.encoding = enc.into();
self
}
}