use crate::{
expr::SimpleExpr,
types::{DynIden, IntoIden, TableRef},
};
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ColumnType {
Char(Option<u32>),
String(Option<u32>),
Text,
TinyInteger,
SmallInteger,
Integer,
BigInteger,
Float,
Double,
Decimal(Option<(u32, u32)>),
Boolean,
Date,
Time,
DateTime,
Timestamp,
TimestampWithTimeZone,
Binary(Option<u32>),
VarBinary(u32),
Blob,
Uuid,
Json,
JsonBinary,
Array(Box<ColumnType>),
Custom(String),
}
#[derive(Debug, Clone)]
pub struct ColumnDef {
pub(crate) name: DynIden,
pub(crate) column_type: Option<ColumnType>,
pub(crate) not_null: bool,
pub(crate) unique: bool,
pub(crate) primary_key: bool,
pub(crate) auto_increment: bool,
pub(crate) default: Option<SimpleExpr>,
pub(crate) check: Option<SimpleExpr>,
pub(crate) comment: Option<String>,
}
impl ColumnDef {
pub fn new<T>(name: T) -> Self
where
T: IntoIden,
{
Self {
name: name.into_iden(),
column_type: None,
not_null: false,
unique: false,
primary_key: false,
auto_increment: false,
default: None,
check: None,
comment: None,
}
}
pub fn column_type(mut self, column_type: ColumnType) -> Self {
self.column_type = Some(column_type);
self
}
pub fn not_null(mut self, not_null: bool) -> Self {
self.not_null = not_null;
self
}
pub fn unique(mut self, unique: bool) -> Self {
self.unique = unique;
self
}
pub fn primary_key(mut self, primary_key: bool) -> Self {
self.primary_key = primary_key;
self
}
pub fn auto_increment(mut self, auto_increment: bool) -> Self {
self.auto_increment = auto_increment;
self
}
pub fn default(mut self, value: SimpleExpr) -> Self {
self.default = Some(value);
self
}
pub fn check(mut self, expr: SimpleExpr) -> Self {
self.check = Some(expr);
self
}
pub fn comment<S: Into<String>>(mut self, comment: S) -> Self {
self.comment = Some(comment.into());
self
}
pub fn integer(self) -> Self {
self.column_type(ColumnType::Integer)
}
pub fn big_integer(self) -> Self {
self.column_type(ColumnType::BigInteger)
}
pub fn small_integer(self) -> Self {
self.column_type(ColumnType::SmallInteger)
}
pub fn tiny_integer(self) -> Self {
self.column_type(ColumnType::TinyInteger)
}
pub fn string(self) -> Self {
self.column_type(ColumnType::String(None))
}
pub fn string_len(self, len: u32) -> Self {
self.column_type(ColumnType::String(Some(len)))
}
pub fn char(self) -> Self {
self.column_type(ColumnType::Char(None))
}
pub fn char_len(self, len: u32) -> Self {
self.column_type(ColumnType::Char(Some(len)))
}
pub fn text(self) -> Self {
self.column_type(ColumnType::Text)
}
pub fn boolean(self) -> Self {
self.column_type(ColumnType::Boolean)
}
pub fn float(self) -> Self {
self.column_type(ColumnType::Float)
}
pub fn double(self) -> Self {
self.column_type(ColumnType::Double)
}
pub fn decimal(self, precision: u32, scale: u32) -> Self {
self.column_type(ColumnType::Decimal(Some((precision, scale))))
}
pub fn date(self) -> Self {
self.column_type(ColumnType::Date)
}
pub fn time(self) -> Self {
self.column_type(ColumnType::Time)
}
pub fn date_time(self) -> Self {
self.column_type(ColumnType::DateTime)
}
pub fn timestamp(self) -> Self {
self.column_type(ColumnType::Timestamp)
}
pub fn timestamp_with_time_zone(self) -> Self {
self.column_type(ColumnType::TimestampWithTimeZone)
}
pub fn uuid(self) -> Self {
self.column_type(ColumnType::Uuid)
}
pub fn json(self) -> Self {
self.column_type(ColumnType::Json)
}
pub fn json_binary(self) -> Self {
self.column_type(ColumnType::JsonBinary)
}
pub fn blob(self) -> Self {
self.column_type(ColumnType::Blob)
}
pub fn binary(self, len: u32) -> Self {
self.column_type(ColumnType::Binary(Some(len)))
}
pub fn binary_len(self, len: u32) -> Self {
self.column_type(ColumnType::Binary(Some(len)))
}
pub fn var_binary(self, len: u32) -> Self {
self.column_type(ColumnType::VarBinary(len))
}
pub fn custom<S: Into<String>>(self, name: S) -> Self {
self.column_type(ColumnType::Custom(name.into()))
}
pub fn array(self, element_type: ColumnType) -> Self {
self.column_type(ColumnType::Array(Box::new(element_type)))
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum TableConstraint {
PrimaryKey {
name: Option<DynIden>,
columns: Vec<DynIden>,
},
Unique {
name: Option<DynIden>,
columns: Vec<DynIden>,
},
ForeignKey {
name: Option<DynIden>,
columns: Vec<DynIden>,
ref_table: Box<TableRef>,
ref_columns: Vec<DynIden>,
on_delete: Option<ForeignKeyAction>,
on_update: Option<ForeignKeyAction>,
},
Check {
name: Option<DynIden>,
expr: SimpleExpr,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ForeignKeyAction {
Restrict,
Cascade,
SetNull,
SetDefault,
NoAction,
}
impl ForeignKeyAction {
pub fn as_str(&self) -> &'static str {
match self {
Self::Restrict => "RESTRICT",
Self::Cascade => "CASCADE",
Self::SetNull => "SET NULL",
Self::SetDefault => "SET DEFAULT",
Self::NoAction => "NO ACTION",
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct IndexDef {
pub(crate) name: DynIden,
pub(crate) table: TableRef,
pub(crate) columns: Vec<DynIden>,
pub(crate) unique: bool,
pub(crate) r#where: Option<SimpleExpr>,
}
impl IndexDef {
pub fn new<T, R>(name: T, table: R) -> Self
where
T: IntoIden,
R: Into<TableRef>,
{
Self {
name: name.into_iden(),
table: table.into(),
columns: Vec::new(),
unique: false,
r#where: None,
}
}
pub fn column<C>(mut self, col: C) -> Self
where
C: IntoIden,
{
self.columns.push(col.into_iden());
self
}
pub fn columns<I, C>(mut self, cols: I) -> Self
where
I: IntoIterator<Item = C>,
C: IntoIden,
{
for col in cols {
self.columns.push(col.into_iden());
}
self
}
pub fn unique(mut self, unique: bool) -> Self {
self.unique = unique;
self
}
pub fn r#where(mut self, expr: SimpleExpr) -> Self {
self.r#where = Some(expr);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
fn test_column_def_integer() {
let col = ColumnDef::new("age").integer().not_null(true);
assert_eq!(col.column_type, Some(ColumnType::Integer));
assert!(col.not_null);
}
#[rstest]
fn test_column_def_string_len() {
let col = ColumnDef::new("name").string_len(100);
assert_eq!(col.column_type, Some(ColumnType::String(Some(100))));
}
#[rstest]
fn test_column_def_text() {
let col = ColumnDef::new("bio").text();
assert_eq!(col.column_type, Some(ColumnType::Text));
}
#[rstest]
fn test_column_def_boolean() {
let col = ColumnDef::new("active").boolean();
assert_eq!(col.column_type, Some(ColumnType::Boolean));
}
#[rstest]
fn test_column_def_timestamp() {
let col = ColumnDef::new("created_at").timestamp();
assert_eq!(col.column_type, Some(ColumnType::Timestamp));
}
#[rstest]
fn test_column_def_uuid() {
let col = ColumnDef::new("id").uuid().primary_key(true);
assert_eq!(col.column_type, Some(ColumnType::Uuid));
assert!(col.primary_key);
}
#[rstest]
fn test_column_def_chaining() {
let col = ColumnDef::new("email")
.string_len(255)
.not_null(true)
.unique(true);
assert_eq!(col.column_type, Some(ColumnType::String(Some(255))));
assert!(col.not_null);
assert!(col.unique);
}
#[rstest]
fn test_column_def_json_binary() {
let col = ColumnDef::new("data").json_binary();
assert_eq!(col.column_type, Some(ColumnType::JsonBinary));
}
#[rstest]
fn test_column_def_decimal() {
let col = ColumnDef::new("price").decimal(10, 2);
assert_eq!(col.column_type, Some(ColumnType::Decimal(Some((10, 2)))));
}
#[rstest]
fn test_column_def_custom() {
let col = ColumnDef::new("data").custom("CITEXT");
assert_eq!(
col.column_type,
Some(ColumnType::Custom("CITEXT".to_string()))
);
}
}