use crate::{Dialect, SqlDown, SqlUp, TableDefinition, TableName};
use log::{error, warn};
#[derive(Clone, Debug)]
pub struct Table {
pub name: TableName,
pub options: Option<TableOptions>,
pub definition: Vec<TableDefinition>,
}
impl Table {
pub fn new(name: &str, definition: Vec<TableDefinition>) -> Self {
Self {
name: TableName::new(name),
options: Some(TableOptions::default()),
definition,
}
}
pub fn no_timestamps(&mut self) -> &mut Self {
let options = self.options.clone().unwrap_or_default();
self.options = Some(TableOptions {
timestamps: None,
..options
});
self
}
pub fn with_options(
name: &str,
options: TableOptions,
definition: Vec<TableDefinition>,
) -> Self {
Self {
name: TableName::new(name),
options: Some(options),
definition,
}
}
}
impl SqlUp for Table {
fn sqlite_up(&self) -> Option<String> {
let mut sql = "CREATE TABLE".to_string();
let options = self.options.clone().unwrap_or_default();
if options.if_not_exists {
sql.push_str(" IF NOT EXISTS");
}
sql.push(' ');
sql.push_str(self.name.to_string().as_str());
let options = options.sqlite_up();
if let Some(options) = options {
sql.push_str(&options);
} else {
warn!("Failed to generate table options");
return None;
}
sql.push_str(", ");
for (i, def) in self.definition.iter().enumerate() {
if i > 0 {
sql.push_str(", ");
}
let def = if let TableDefinition::Index(index) = def {
let mut index = index.clone();
index.table_name = Some(self.name.clone());
TableDefinition::Index(index)
} else {
def.clone()
};
let def = def.sqlite_up();
if let Some(def) = def {
sql.push_str(&def);
} else {
warn!("Failed to generate table definition");
return None;
}
}
let options = self.options.clone().unwrap_or_default();
let timestamps = options.timestamps;
if timestamps.is_some() {
let timestamps = timestamps.unwrap();
match timestamps {
Timestamps::All => {
sql.push_str(", created_at DATETIME DEFAULT CURRENT_TIMESTAMP");
sql.push_str(", updated_at DATETIME DEFAULT CURRENT_TIMESTAMP");
}
Timestamps::CreatedAt => {
sql.push_str(", created_at DATETIME DEFAULT CURRENT_TIMESTAMP");
}
Timestamps::UpdatedAt => {
sql.push_str(", updated_at DATETIME DEFAULT CURRENT_TIMESTAMP");
}
}
}
sql.push(')');
Some(sql)
}
}
impl SqlDown for Table {
fn sql_down(&self, _dialect: Dialect) -> Option<String> {
let sql = format!("DROP TABLE {}", self.name);
Some(sql)
}
}
#[derive(Clone, Debug)]
pub struct TableOptions {
pub if_not_exists: bool,
pub primary_key: PrimaryKey,
pub timestamps: Option<Timestamps>,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub enum PrimaryKey {
#[default]
BigInt,
Int,
UUID,
Clustered(Vec<String>),
None,
}
impl Default for TableOptions {
fn default() -> Self {
TableOptions {
if_not_exists: true,
primary_key: PrimaryKey::BigInt,
timestamps: Some(Timestamps::default()),
}
}
}
#[derive(Clone, Debug, Default)]
pub enum Timestamps {
#[default]
All,
CreatedAt,
UpdatedAt,
}
impl SqlUp for TableOptions {
fn sqlite_up(&self) -> Option<String> {
let mut sql = String::new();
if self.primary_key == PrimaryKey::None {
return Some(sql);
}
sql.push_str(" (");
match self.primary_key {
PrimaryKey::Int => sql.push_str("id INTEGER PRIMARY KEY AUTOINCREMENT"),
PrimaryKey::BigInt => {
error!("AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY, not BIGINT, using INTEGER instead.");
sql.push_str("id INTEGER PRIMARY KEY AUTOINCREMENT")
},
PrimaryKey::UUID => sql.push_str("id UUID PRIMARY KEY"),
PrimaryKey::Clustered(ref columns) => {
sql.push_str("PRIMARY KEY (");
for (i, column) in columns.iter().enumerate() {
if i > 0 {
sql.push_str(", ");
}
sql.push_str(column);
}
sql.push(')');
}
PrimaryKey::None => {}
}
Some(sql)
}
}