use std::{collections::HashMap, sync::Arc};
use itertools::Itertools;
use tracing::trace;
use crate::{
MigrationKind, ModelMigration,
column::{ColumnType, ModifyColumnOptionsValues, create::CreateOptions},
search::SearchQuery,
traits::{DatabaseStrategy, DatabaseStrategyError, from_iter::FromIter, save_data::SaveData},
};
pub trait Model {
const TABLE_NAME: &'static str;
fn get_migration() -> &'static Vec<ModelMigration>;
fn get_id(&self) -> Option<i64>;
fn get_id_column_name(&self) -> &'static str;
fn set_id(&mut self, id: i64);
fn get_latest_column_name(initial_name: &str) -> Option<String> {
let mut past_names = vec![initial_name.to_string()];
let mut name = Some(String::from(initial_name));
for migration in Self::get_migration()
.iter()
.sorted_by_key(|item| item.ordering)
{
match &migration.kind {
MigrationKind::Create(columns) => {
let existed_at_creation = columns.iter().any(|elem| elem.key == initial_name);
if !existed_at_creation {
name = None;
}
}
MigrationKind::Modify(modifiers) => {
for modification in modifiers {
if !past_names.contains(&modification.key) {
continue;
}
match &modification.options {
ModifyColumnOptionsValues::Rename { to } => {
if Some(&modification.key) == name.as_ref() {
name = Some(to.to_string());
past_names.push(modification.key.clone());
}
}
ModifyColumnOptionsValues::Drop => name = None,
ModifyColumnOptionsValues::Add {
new_type: _,
new_options: _,
} => {
if modification.key == initial_name {
name = Some(initial_name.to_string())
}
}
}
}
}
}
}
name
}
fn get_columns() -> Vec<(String, ColumnType, CreateOptions)> {
let migration = Self::get_migration();
let mut columns: HashMap<String, (ColumnType, CreateOptions)> = HashMap::new();
for (idx, column) in migration
.iter()
.sorted_by_key(|item| item.ordering)
.enumerate()
{
match &column.kind {
MigrationKind::Create(values) => {
if idx != 0 {
panic!("The first iteration must be a creation.")
}
for item in values.iter().map(|col| {
(
Self::get_latest_column_name(&col.key).unwrap(),
col.value,
col.options.clone(),
)
}) {
columns.insert(item.0, (item.1, item.2));
}
}
MigrationKind::Modify(values) => {
for value in values {
match &value.options {
ModifyColumnOptionsValues::Rename { to: _ } => {}
ModifyColumnOptionsValues::Drop => {}
ModifyColumnOptionsValues::Add {
new_type,
new_options: options,
} => {
columns.insert(
Self::get_latest_column_name(&value.key).unwrap(),
(*new_type, options.clone()),
);
}
}
}
}
}
}
if let MigrationKind::Create(value) = &migration[0].kind {
for item in value.iter().map(|e| {
(
Self::get_latest_column_name(&e.key).unwrap(),
e.value,
e.options.clone(),
)
}) {
columns.insert(item.0, (item.1, item.2));
}
}
columns
.into_iter()
.map(|(key, (col_value, col_options))| (key, col_value, col_options))
.collect_vec()
}
fn self_get_migration(&self) -> &'static Vec<ModelMigration> {
Self::get_migration()
}
fn self_get_table_name(&self) -> &'static str {
Self::TABLE_NAME
}
fn self_get_columns(&self) -> Vec<(String, ColumnType, CreateOptions)> {
Self::get_columns()
}
fn save<D>(&mut self, db: Arc<D>) -> Result<(), DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.save_model(&db.get_connection(), self)
}
fn save_conn<D>(
&mut self,
db: Arc<D>,
conn: &D::FunctionConnType<'_>,
) -> Result<(), DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.save_model(conn, self)
}
fn search_single<D>(
db: Arc<D>,
query: SearchQuery,
) -> Result<Option<Self>, DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.search_single_model(&db.get_connection(), query)
}
fn search_single_conn<D>(
db: Arc<D>,
conn: &D::FunctionConnType<'_>,
query: SearchQuery,
) -> Result<Option<Self>, DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.search_single_model(conn, query)
}
fn search_multiple<D>(
db: Arc<D>,
query: SearchQuery,
) -> Result<Vec<Self>, DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.search_multiple_model(&db.get_connection(), query)
}
fn search_multiple_conn<D>(
db: Arc<D>,
conn: &D::FunctionConnType<'_>,
query: SearchQuery,
) -> Result<Vec<Self>, DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.search_multiple_model(conn, query)
}
fn remove<D>(db: Arc<D>, query: SearchQuery) -> Result<(), DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.remove_model::<Self>(&db.get_connection(), query)
}
fn remove_conn<D>(
db: Arc<D>,
conn: &D::FunctionConnType<'_>,
query: SearchQuery,
) -> Result<(), DatabaseStrategyError>
where
D: DatabaseStrategy,
Self: Sized + FromIter + SaveData,
{
db.remove_model::<Self>(conn, query)
}
}