use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use crate::data_manager::GetTableDefinition;
use crate::object_management::insert::InsertStatement;
use crate::queries::Insertable;
use crate::{AsSql, BuildSql};
use crate::data_definition::table::{DatabaseTableDefinition, Identifier};
use super::{TableColumnConstraint, TableColumnConstraintDetail};
trait ForeignKeyObject
where
Self: GetTableDefinition + Insertable + Clone,
{
}
pub(crate) type ObjectRepr = HashMap<Identifier, ColumnValue>;
pub enum ColumnValue {
Boolean(bool), Int(i64), Float(f64), String(String), Timestamp(chrono::NaiveDateTime), Uuid(uuid::Uuid), Json(String), Child(Box<ObjectRepr>),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum DatabaseColumnType {
Boolean, Int, Float, String, Timestamp, Uuid, Json,
OneToMany(Identifier), ManyToMany(Identifier), OneToOne(Identifier), }
impl DatabaseColumnType {
pub fn as_str(&self) -> &str {
match self {
DatabaseColumnType::Boolean => "BOOL",
DatabaseColumnType::Int => "INT",
DatabaseColumnType::Float => "FLOAT",
DatabaseColumnType::String => "VARCHAR",
DatabaseColumnType::Timestamp => "TIMESTAMP",
DatabaseColumnType::Uuid => "UUID",
DatabaseColumnType::Json => "JSONB",
DatabaseColumnType::OneToMany(_) => todo!(),
DatabaseColumnType::ManyToMany {
..
} => todo!(),
DatabaseColumnType::OneToOne(_) => "UUID", }
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TableColumn {
data: Arc<TableColumnData>,
}
impl Deref for TableColumn {
type Target = TableColumnData;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl From<TableColumnData> for Identifier {
fn from(val: TableColumnData) -> Self {
val.column_name.clone()
}
}
impl From<TableColumn> for Identifier {
fn from(val: TableColumn) -> Self {
val.data.column_name.clone()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TableColumnData {
pub column_name: Identifier, pub column_type: DatabaseColumnType, pub constraints: Vec<TableColumnConstraint>,
}
impl From<TableColumnData> for TableColumn {
fn from(val: TableColumnData) -> Self {
TableColumn {
data: Arc::new(val),
}
}
}
impl TableColumnData {
pub fn is_nullable(&self) -> bool {
self.constraints
.iter()
.all(|constraint| TableColumnConstraintDetail::NotNull != *constraint.detail)
}
pub fn is_pk(&self) -> bool {
self.constraints.iter().any(|constraint| {
matches!(&*constraint.detail, TableColumnConstraintDetail::PrimaryKey(_))
})
}
}
impl TableColumnData {
pub fn non_null(mut self) -> Self {
self.constraints.push(TableColumnConstraint::non_null());
self
}
pub fn primary_key(mut self) -> Self {
self.constraints.push(TableColumnConstraint::primary_key());
self
}
pub fn pk(self) -> Self {
self.primary_key()
}
pub fn fk_to<T>(
self,
ref_table: DatabaseTableDefinition<T>,
ref_column: TableColumn,
) -> Self {
self.foreign_key_to(ref_table, ref_column)
}
pub fn foreign_key_to<T>(
mut self,
ref_table: DatabaseTableDefinition<T>,
ref_column: TableColumn,
) -> Self {
self.constraints.push(TableColumnConstraint::foreign_key(ref_table, ref_column));
self
}
}
impl TableColumn {
#[allow(clippy::new_ret_no_self)]
pub fn new(
column_name: &str,
column_type: DatabaseColumnType,
constraints: Vec<TableColumnConstraint>,
) -> Result<TableColumnData, String> {
Ok(TableColumnData {
column_name: Identifier::new(column_name)?,
column_type,
constraints,
})
}
pub fn int(column_name: &str) -> Result<TableColumnData, String> {
Self::new_int(column_name)
}
pub fn timestamp(column_name: &str) -> Result<TableColumnData, String> {
Self::new_timestamp(column_name)
}
pub fn string(column_name: &str) -> Result<TableColumnData, String> {
Self::new_string(column_name)
}
pub fn float(column_name: &str) -> Result<TableColumnData, String> {
Self::new_float(column_name)
}
pub fn uuid(column_name: &str) -> Result<TableColumnData, String> {
Self::new_uuid(column_name)
}
pub fn bool(column_name: &str) -> Result<TableColumnData, String> {
Self::new_bool(column_name)
}
pub fn new_int(column_name: &str) -> Result<TableColumnData, String> {
Ok(TableColumnData {
column_name: Identifier::new(column_name)?,
column_type: DatabaseColumnType::Int,
constraints: vec![],
})
}
pub fn new_string(column_name: &str) -> Result<TableColumnData, String> {
Ok(TableColumnData {
column_name: Identifier::new(column_name)?,
column_type: DatabaseColumnType::String,
constraints: vec![],
})
}
pub fn new_timestamp(column_name: &str) -> Result<TableColumnData, String> {
Ok(TableColumnData {
column_name: Identifier::new(column_name)?,
column_type: DatabaseColumnType::Timestamp,
constraints: vec![],
})
}
pub fn new_float(column_name: &str) -> Result<TableColumnData, String> {
Ok(TableColumnData {
column_name: Identifier::new(column_name)?,
column_type: DatabaseColumnType::Float,
constraints: vec![],
})
}
pub fn new_uuid(column_name: &str) -> Result<TableColumnData, String> {
Ok(TableColumnData {
column_name: Identifier::new(column_name)?,
column_type: DatabaseColumnType::Uuid,
constraints: vec![],
})
}
pub fn new_bool(column_name: &str) -> Result<TableColumnData, String> {
Ok(TableColumnData {
column_name: Identifier::new(column_name)?,
column_type: DatabaseColumnType::Boolean,
constraints: vec![],
})
}
}
impl BuildSql for TableColumn {
fn build_sql(
&self,
sql: &mut sqlx::QueryBuilder<'_, sqlx::Postgres>,
) {
sql.push(self.column_name.to_string()).push(" ").push(self.column_type.as_str());
let constraints_iter = self.constraints.iter();
for constraint in constraints_iter {
constraint.build_sql(sql);
}
}
}