use color_eyre::{eyre::ContextCompat, Result}; use comfy_table::Cell; use heck::ToUpperCamelCase; use sea_schema::sea_query::{ColumnDef, ColumnSpec, ColumnType, IndexCreateStatement}; use crate::config::{sea_orm_config::DateTimeCrate, Config}; use super::{discover::DbType, ModelConfig}; #[derive(Clone, Debug)] pub struct Column { pub name: String, pub col_type: ColumnType, pub attrs: Vec, } impl Column { pub fn new(column: ColumnDef, index: Option) -> Result { let name = column.get_column_name(); let col_type = column .get_column_type() .context("Unable to get column type")? .clone(); let mut attrs = column.get_column_spec().clone(); if let Some(index) = index { if index.is_unique_key() { attrs.push(ColumnSpec::UniqueKey) } if index.is_primary_key() { attrs.push(ColumnSpec::PrimaryKey); } } Ok(Column { name: name.to_string(), col_type, attrs: attrs.to_vec(), }) } pub fn get_info_row(&self, config: &ModelConfig) -> Result> { let column_type_rust = self.get_rs_type(&config.comment.date_time_crate); let column_type = self.get_db_type(&config.db_type); let attrs = self.attrs_to_string(); let mut cols = Vec::new(); if config.comment.column_name { cols.push(Cell::new(self.name.clone())) } if config.comment.column_name { cols.push(Cell::new(column_type.clone())) } if config.comment.column_rust_type { cols.push(Cell::new(column_type_rust.clone())) } if config.comment.column_attributes { cols.push(Cell::new(attrs.clone())); } Ok(cols) } pub fn attrs_to_string(&self) -> String { self.attrs .iter() .filter_map(Self::get_addr_type) .map(|s| s.to_string()) .collect::>() .join(", ") } pub fn get_addr_type(attr: &ColumnSpec) -> Option { match attr { ColumnSpec::PrimaryKey => Some("primary key".to_owned()), ColumnSpec::Null => todo!(), ColumnSpec::NotNull => Some("not null".to_owned()), ColumnSpec::Default(simple_expr) => todo!(), ColumnSpec::AutoIncrement => Some("autoincrement".to_owned()), ColumnSpec::UniqueKey => Some("unique key".to_owned()), ColumnSpec::Check(simple_expr) => todo!(), ColumnSpec::Generated { expr, stored } => todo!(), ColumnSpec::Extra(_) => todo!(), ColumnSpec::Comment(_) => todo!(), } } pub fn get_db_type(&self, db_type: &DbType) -> String { fn write_db_type(col_type: &ColumnType, db_type: &DbType) -> String { #[allow(unreachable_patterns)] match (col_type, db_type) { (ColumnType::Char(_), _) => "char".to_owned(), (ColumnType::String(_), _) => "varchar".to_owned(), (ColumnType::Text, _) => "text".to_owned(), (ColumnType::TinyInteger, DbType::MySql | DbType::Sqlite) => "tinyint".to_owned(), (ColumnType::TinyInteger, DbType::Postgres) => "smallint".to_owned(), (ColumnType::SmallInteger, _) => "smallint".to_owned(), (ColumnType::Integer, DbType::MySql) => "int".to_owned(), (ColumnType::Integer, _) => "integer".to_owned(), (ColumnType::BigInteger, DbType::MySql | DbType::Postgres) => "bigint".to_owned(), (ColumnType::BigInteger, DbType::Sqlite) => "integer".to_owned(), (ColumnType::TinyUnsigned, DbType::MySql) => "tinyint unsigned".to_owned(), (ColumnType::TinyUnsigned, DbType::Postgres) => "smallint".to_owned(), (ColumnType::TinyUnsigned, DbType::Sqlite) => "tinyint".to_owned(), (ColumnType::SmallUnsigned, DbType::MySql) => "smallint unsigned".to_owned(), (ColumnType::SmallUnsigned, DbType::Postgres | DbType::Sqlite) => { "smallint".to_owned() } (ColumnType::Unsigned, DbType::MySql) => "int unsigned".to_owned(), (ColumnType::Unsigned, DbType::Postgres | DbType::Sqlite) => "integer".to_owned(), (ColumnType::BigUnsigned, DbType::MySql) => "bigint unsigned".to_owned(), (ColumnType::BigUnsigned, DbType::Postgres) => "bigint".to_owned(), (ColumnType::BigUnsigned, DbType::Sqlite) => "integer".to_owned(), (ColumnType::Float, DbType::MySql | DbType::Sqlite) => "float".to_owned(), (ColumnType::Float, DbType::Postgres) => "real".to_owned(), (ColumnType::Double, DbType::MySql | DbType::Sqlite) => "double".to_owned(), (ColumnType::Double, DbType::Postgres) => "double precision".to_owned(), (ColumnType::Decimal(_), DbType::MySql | DbType::Postgres) => "decimal".to_owned(), (ColumnType::Decimal(_), DbType::Sqlite) => "real".to_owned(), (ColumnType::DateTime, DbType::MySql) => "datetime".to_owned(), (ColumnType::DateTime, DbType::Postgres) => "timestamp w/o tz".to_owned(), (ColumnType::DateTime, DbType::Sqlite) => "datetime_text".to_owned(), (ColumnType::Timestamp, DbType::MySql | DbType::Postgres) => "timestamp".to_owned(), (ColumnType::Timestamp, DbType::Sqlite) => "timestamp_text".to_owned(), (ColumnType::TimestampWithTimeZone, DbType::MySql) => "timestamp".to_owned(), (ColumnType::TimestampWithTimeZone, DbType::Postgres) => { "timestamp w tz".to_owned() } (ColumnType::TimestampWithTimeZone, DbType::Sqlite) => { "timestamp_with_timezone_text".to_owned() } (ColumnType::Time, DbType::MySql | DbType::Postgres) => "time".to_owned(), (ColumnType::Time, DbType::Sqlite) => "time_text".to_owned(), (ColumnType::Date, DbType::MySql | DbType::Postgres) => "date".to_owned(), (ColumnType::Date, DbType::Sqlite) => "date_text".to_owned(), (ColumnType::Year, DbType::MySql) => "year".to_owned(), (ColumnType::Interval(_, _), DbType::Postgres) => "interval".to_owned(), (ColumnType::Blob, DbType::MySql | DbType::Sqlite) => "blob".to_owned(), (ColumnType::Blob, DbType::Postgres) => "bytea".to_owned(), (ColumnType::Binary(_), DbType::MySql) => "binary".to_owned(), (ColumnType::Binary(_), DbType::Postgres) => "bytea".to_owned(), (ColumnType::Binary(_), DbType::Sqlite) => "blob".to_owned(), (ColumnType::VarBinary(_), DbType::MySql) => "varbinary".to_owned(), (ColumnType::VarBinary(_), DbType::Postgres) => "bytea".to_owned(), (ColumnType::VarBinary(_), DbType::Sqlite) => "varbinary_blob".to_owned(), (ColumnType::Bit(_), DbType::MySql | DbType::Postgres) => "bit".to_owned(), (ColumnType::VarBit(_), DbType::MySql) => "bit".to_owned(), (ColumnType::VarBit(_), DbType::Postgres) => "varbit".to_owned(), (ColumnType::Boolean, DbType::MySql | DbType::Postgres) => "bool".to_owned(), (ColumnType::Boolean, DbType::Sqlite) => "boolean".to_owned(), (ColumnType::Money(_), DbType::MySql) => "decimal".to_owned(), (ColumnType::Money(_), DbType::Postgres) => "money".to_owned(), (ColumnType::Money(_), DbType::Sqlite) => "real_money".to_owned(), (ColumnType::Json, DbType::MySql | DbType::Postgres) => "json".to_owned(), (ColumnType::Json, DbType::Sqlite) => "json_text".to_owned(), (ColumnType::JsonBinary, DbType::MySql) => "json".to_owned(), (ColumnType::JsonBinary, DbType::Postgres) => "jsonb".to_owned(), (ColumnType::JsonBinary, DbType::Sqlite) => "jsonb_text".to_owned(), (ColumnType::Uuid, DbType::MySql) => "binary(16)".to_owned(), (ColumnType::Uuid, DbType::Postgres) => "uuid".to_owned(), (ColumnType::Uuid, DbType::Sqlite) => "uuid_text".to_owned(), (ColumnType::Enum { name, .. }, DbType::MySql) => { format!("ENUM({})", name.to_string().to_upper_camel_case()) } (ColumnType::Enum { name, .. }, DbType::Postgres) => { name.to_string().to_uppercase() } (ColumnType::Enum { .. }, DbType::Sqlite) => "enum_text".to_owned(), (ColumnType::Array(column_type), DbType::Postgres) => { format!("{}[]", write_db_type(column_type, db_type)).to_uppercase() } (ColumnType::Vector(_), DbType::Postgres) => "vector".to_owned(), (ColumnType::Cidr, DbType::Postgres) => "cidr".to_owned(), (ColumnType::Inet, DbType::Postgres) => "inet".to_owned(), (ColumnType::MacAddr, DbType::Postgres) => "macaddr".to_owned(), (ColumnType::LTree, DbType::Postgres) => "ltree".to_owned(), _ => unimplemented!(), } } write_db_type(&self.col_type, db_type) } pub fn get_rs_type(&self, date_time_crate: &DateTimeCrate) -> String { fn write_rs_type(col_type: &ColumnType, date_time_crate: &DateTimeCrate) -> String { #[allow(unreachable_patterns)] match col_type { ColumnType::Char(_) | ColumnType::String(_) | ColumnType::Text | ColumnType::Custom(_) => "String".to_owned(), ColumnType::TinyInteger => "i8".to_owned(), ColumnType::SmallInteger => "i16".to_owned(), ColumnType::Integer => "i32".to_owned(), ColumnType::BigInteger => "i64".to_owned(), ColumnType::TinyUnsigned => "u8".to_owned(), ColumnType::SmallUnsigned => "u16".to_owned(), ColumnType::Unsigned => "u32".to_owned(), ColumnType::BigUnsigned => "u64".to_owned(), ColumnType::Float => "f32".to_owned(), ColumnType::Double => "f64".to_owned(), ColumnType::Json | ColumnType::JsonBinary => "Json".to_owned(), ColumnType::Date => match date_time_crate { DateTimeCrate::Chrono => "Date".to_owned(), DateTimeCrate::Time => "TimeDate".to_owned(), }, ColumnType::Time => match date_time_crate { DateTimeCrate::Chrono => "Time".to_owned(), DateTimeCrate::Time => "TimeTime".to_owned(), }, ColumnType::DateTime => match date_time_crate { DateTimeCrate::Chrono => "DateTime".to_owned(), DateTimeCrate::Time => "TimeDateTime".to_owned(), }, ColumnType::Timestamp => match date_time_crate { DateTimeCrate::Chrono => "DateTimeUtc".to_owned(), DateTimeCrate::Time => "TimeDateTime".to_owned(), }, ColumnType::TimestampWithTimeZone => match date_time_crate { DateTimeCrate::Chrono => "DateTimeWithTimeZone".to_owned(), DateTimeCrate::Time => "TimeDateTimeWithTimeZone".to_owned(), }, ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal".to_owned(), ColumnType::Uuid => "Uuid".to_owned(), ColumnType::Binary(_) | ColumnType::VarBinary(_) | ColumnType::Blob => { "Vec".to_owned() } ColumnType::Boolean => "bool".to_owned(), ColumnType::Enum { name, .. } => name.to_string().to_upper_camel_case(), ColumnType::Array(column_type) => { format!("Vec<{}>", write_rs_type(column_type, date_time_crate)) } ColumnType::Vector(_) => "::pgvector::Vector".to_owned(), ColumnType::Bit(None | Some(1)) => "bool".to_owned(), ColumnType::Bit(_) | ColumnType::VarBit(_) => "Vec".to_owned(), ColumnType::Year => "i32".to_owned(), ColumnType::Cidr | ColumnType::Inet => "IpNetwork".to_owned(), ColumnType::Interval(_, _) | ColumnType::MacAddr | ColumnType::LTree => { "String".to_owned() } _ => unimplemented!(), } } write_rs_type(&self.col_type, date_time_crate) } }