diff --git a/src/entity/loader.rs b/src/entity/loader.rs new file mode 100644 index 00000000..e9af2cf9 --- /dev/null +++ b/src/entity/loader.rs @@ -0,0 +1,161 @@ +use crate::{DbErr, EntityTrait, ModelTrait, QueryFilter, Select, Related, RelationType, Identity, Condition, Value, ColumnTrait, ConnectionTrait}; +use std::{fmt::Debug, str::FromStr, collections::BTreeMap}; + +#[async_trait::async_trait] +pub trait LoaderTrait { + type Model: ModelTrait; + + async fn load_one(&self, db: &C) -> Result>, DbErr> + where + C: ConnectionTrait, + R: EntityTrait, + R::Model: Send + Sync, + <::Column as FromStr>::Err: Debug, + <::Model as ModelTrait>::Entity: Related, + <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::Err: Debug; + + async fn load_many(&self, db: &C) -> Result>, DbErr> + where + C: ConnectionTrait, + R: EntityTrait, + R::Model: Send + Sync, + <::Column as FromStr>::Err: Debug, + <::Model as ModelTrait>::Entity: Related, + <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::Err: Debug; +} + +#[async_trait::async_trait] +impl LoaderTrait for Vec +where + M: ModelTrait, + Vec: Sync, +{ + type Model = M; + + async fn load_one(&self, db: &C) -> Result>, DbErr> + where + C: ConnectionTrait, + R: EntityTrait, + R::Model: Send + Sync, + <::Column as FromStr>::Err: Debug, + <::Model as ModelTrait>::Entity: Related, + <<::Entity as EntityTrait>::Column as FromStr>::Err: Debug, + { + let rel_def = + <<::Model as ModelTrait>::Entity as Related>::to(); + + // we verify that is has_one relation + match (&rel_def).rel_type { + RelationType::HasOne => (), + RelationType::HasMany => { + return Err(DbErr::Type("Relation is HasMany instead of HasOne".into())) + } + } + + fn extract_key(target_col: &Identity, model: &Model) -> Vec + where + Model: ModelTrait, + <<::Entity as EntityTrait>::Column as FromStr>::Err: Debug, + { + match target_col { + Identity::Unary(a) => { + let column_a = <<::Entity as EntityTrait>::Column as FromStr>::from_str(&a.to_string()).unwrap(); + vec![model.get(column_a)] + }, + Identity::Binary(a, b) => { + let column_a = <<::Entity as EntityTrait>::Column as FromStr>::from_str(&a.to_string()).unwrap(); + let column_b = <<::Entity as EntityTrait>::Column as FromStr>::from_str(&b.to_string()).unwrap(); + vec![model.get(column_a), model.get(column_b)] + }, + Identity::Ternary(a, b, c) => { + let column_a = <<::Entity as EntityTrait>::Column as FromStr>::from_str(&a.to_string()).unwrap(); + let column_b = <<::Entity as EntityTrait>::Column as FromStr>::from_str(&b.to_string()).unwrap(); + let column_c = <<::Entity as EntityTrait>::Column as FromStr>::from_str(&c.to_string()).unwrap(); + vec![model.get(column_a), model.get(column_b), model.get(column_c)] + }, + } + } + + let keys: Vec> = self + .iter() + .map(|model: &M| { + extract_key(&rel_def.from_col, model) + }) + .collect(); + + let condition = match &rel_def.to_col { + Identity::Unary(a) => { + let column_a: ::Column = <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::from_str(&a.to_string()).unwrap(); + Condition::all().add(ColumnTrait::is_in( + &column_a, + keys.iter().map(|key| key[0].clone()).collect::>(), + )) + } + Identity::Binary(a, b) => { + let column_a: <<::Model as ModelTrait>::Entity as EntityTrait>::Column = <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::from_str(&a.to_string()).unwrap(); + let column_b: <<::Model as ModelTrait>::Entity as EntityTrait>::Column = <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::from_str(&b.to_string()).unwrap(); + // TODO + // Condition::all().add( + // sea_query::Expr::tuple([column_a.to_string(), column_b]).is_in(keys.iter().map(|key| (key[0].clone(), key[1].clone())).collect::>()) + // ) + // TODO + Condition::all().add(ColumnTrait::is_in( + &column_a, + keys.iter().map(|key| key[0].clone()).collect::>(), + )) + } + Identity::Ternary(a, b, c) => { + let column_a = <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::from_str(&a.to_string()).unwrap(); + let column_b = <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::from_str(&b.to_string()).unwrap(); + let column_c = <<<::Model as ModelTrait>::Entity as EntityTrait>::Column as FromStr>::from_str(&c.to_string()).unwrap(); + // TODO + Condition::all().add(ColumnTrait::is_in( + &column_a, + keys.iter().map(|key| key[0].clone()).collect::>(), + )) + } + }; + + let stmt = ::find(); + + let stmt = as QueryFilter>::filter(stmt, condition); + + let data = stmt.all(db).await?; + + let mut hashmap: BTreeMap::::Model> = data + .into_iter() + .fold(BTreeMap::::Model>::new(), |mut acc: BTreeMap::::Model>, value: ::Model| { + { + let key = extract_key(&rel_def.to_col, &value); + + acc.insert(format!("{:?}", key), value); + } + + acc + }); + + let result: Vec::Model>> = keys + .iter() + .map(|key| { + let model = hashmap.remove(&format!("{:?}", key)); + + model + }) + .collect(); + + Ok(result) + } + + async fn load_many(&self, db: &C) -> Result>, DbErr> + where + C: ConnectionTrait, + R: EntityTrait, + R::Model: Send + Sync, + <::Column as FromStr>::Err: Debug, + <::Model as ModelTrait>::Entity: Related, + <<::Entity as EntityTrait>::Column as FromStr>::Err: Debug, + { + // we should verify this is a has_many relation + Ok(vec![]) + } +} diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 6b413bdf..10387143 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -106,6 +106,7 @@ mod model; pub mod prelude; mod primary_key; mod relation; +mod loader; pub use active_enum::*; pub use active_model::*; @@ -117,3 +118,4 @@ pub use model::*; // pub use prelude::*; pub use primary_key::*; pub use relation::*; +pub use loader::*;