use crate::{ query::combine, DatabaseConnection, EntityTrait, FromQueryResult, Iterable, JsonValue, ModelTrait, Paginator, PrimaryKeyToColumn, QueryErr, QueryResult, Select, SelectTwo, TypeErr, }; use sea_query::SelectStatement; use std::marker::PhantomData; #[derive(Clone, Debug)] pub struct Selector where S: SelectorTrait, { query: SelectStatement, selector: S, } pub trait SelectorTrait { type Item: Sized; fn from_raw_query_result(res: QueryResult) -> Result; } pub struct SelectModel where M: FromQueryResult, { model: PhantomData, } #[derive(Clone, Debug)] pub struct SelectTwoModel where M: FromQueryResult, N: FromQueryResult, { model: PhantomData<(M, N)>, } impl SelectorTrait for SelectModel where M: FromQueryResult + Sized, { type Item = M; fn from_raw_query_result(res: QueryResult) -> Result { M::from_query_result(&res, "") } } impl SelectorTrait for SelectTwoModel where M: FromQueryResult + Sized, N: FromQueryResult + Sized, { type Item = (M, Option); fn from_raw_query_result(res: QueryResult) -> Result { Ok(( M::from_query_result(&res, combine::SELECT_A)?, N::from_query_result_opt(&res, combine::SELECT_B)?, )) } } impl Select where E: EntityTrait, { pub fn into_model(self) -> Selector> where M: FromQueryResult, { Selector { query: self.query, selector: SelectModel { model: PhantomData }, } } #[cfg(feature = "with-json")] pub fn into_json(self) -> Selector> { Selector { query: self.query, selector: SelectModel { model: PhantomData }, } } pub async fn one(self, db: &DatabaseConnection) -> Result, QueryErr> { self.into_model::().one(db).await } pub async fn all(self, db: &DatabaseConnection) -> Result, QueryErr> { self.into_model::().all(db).await } pub fn paginate( self, db: &DatabaseConnection, page_size: usize, ) -> Paginator<'_, SelectModel> { self.into_model::().paginate(db, page_size) } } impl SelectTwo where E: EntityTrait, F: EntityTrait, { fn into_model(self) -> Selector> where M: FromQueryResult, N: FromQueryResult, { Selector { query: self.query, selector: SelectTwoModel { model: PhantomData }, } } #[cfg(feature = "with-json")] pub fn into_json(self) -> Selector> { Selector { query: self.query, selector: SelectTwoModel { model: PhantomData }, } } pub async fn one( self, db: &DatabaseConnection, ) -> Result)>, QueryErr> { self.into_model::().one(db).await } pub async fn all(self, db: &DatabaseConnection) -> Result)>, QueryErr> { self.into_model::().all(db).await } } impl Selector where S: SelectorTrait, { pub async fn one(mut self, db: &DatabaseConnection) -> Result, QueryErr> { let builder = db.get_query_builder_backend(); self.query.limit(1); let row = db.query_one(builder.build(&self.query)).await?; match row { Some(row) => Ok(Some(S::from_raw_query_result(row)?)), None => Ok(None), } } pub async fn all(self, db: &DatabaseConnection) -> Result, QueryErr> { let builder = db.get_query_builder_backend(); let rows = db.query_all(builder.build(&self.query)).await?; let mut models = Vec::new(); for row in rows.into_iter() { models.push(S::from_raw_query_result(row)?); } Ok(models) } pub fn paginate(self, db: &DatabaseConnection, page_size: usize) -> Paginator<'_, S> { Paginator { query: self.query, page: 0, page_size, db, selector: PhantomData, } } } fn parse_query_result(rows: Vec<(L::Model, Option)>) -> Vec<(L::Model, Vec)> where L: EntityTrait, { let mut acc: Vec<(L::Model, Vec)> = Vec::new(); for (l, r) in rows { if let Some((last_l, last_r)) = acc.last_mut() { let mut same_l = true; for pk_col in ::iter() { let col = pk_col.into_column(); let val = l.get(col); let last_val = last_l.get(col); if !val.eq(&last_val) { same_l = false; break; } } if same_l { if let Some(r) = r { last_r.push(r); continue; } } } if r.is_some() { acc.push((l, vec![r.unwrap()])); } else { acc.push((l, vec![])); } } acc }