use crate::{
ConnectionTrait, DbBackend, DbErr, EntityTrait, FromQueryResult, QueryResult, Select,
Statement, dynamic,
};
use itertools::Itertools;
use sea_query::{DynIden, Expr, IntoIden, SelectStatement};
use std::marker::PhantomData;
#[derive(Debug)]
pub struct SelectModelAndDynModel<M>
where
M: FromQueryResult,
{
model: PhantomData<M>,
dyn_model: dynamic::ModelType,
}
#[derive(Clone, Debug)]
pub struct DynSelector<S>
where
S: DynSelectorTrait,
{
pub(crate) query: SelectStatement,
selector: S,
}
pub trait DynSelectorTrait {
type Item: Sized;
#[allow(clippy::wrong_self_convention)]
fn from_raw_query_result(&self, res: QueryResult) -> Result<Self::Item, DbErr>;
}
impl<M> DynSelectorTrait for SelectModelAndDynModel<M>
where
M: FromQueryResult + Sized,
{
type Item = (M, dynamic::Model);
fn from_raw_query_result(&self, res: QueryResult) -> Result<Self::Item, DbErr> {
Ok((
M::from_query_result(&res, "")?,
self.dyn_model.from_query_result(&res, "")?,
))
}
}
impl<E> Select<E>
where
E: EntityTrait,
{
pub fn select_also_dyn_model(
mut self,
table: DynIden,
dyn_model: dynamic::ModelType,
) -> DynSelector<SelectModelAndDynModel<E::Model>> {
for field in dyn_model.fields.iter() {
self.query.expr(Expr::col((
table.clone(),
field.field().to_owned().into_iden(),
)));
}
DynSelector {
query: self.query,
selector: SelectModelAndDynModel {
model: PhantomData,
dyn_model,
},
}
}
}
impl<S> DynSelector<S>
where
S: DynSelectorTrait,
{
pub fn into_statement(self, builder: DbBackend) -> Statement {
builder.build(&self.query)
}
pub fn one<C>(mut self, db: &C) -> Result<Option<S::Item>, DbErr>
where
C: ConnectionTrait,
{
self.query.limit(1);
let row = db.query_one(&self.query)?;
match row {
Some(row) => Ok(Some(self.selector.from_raw_query_result(row)?)),
None => Ok(None),
}
}
pub fn all<C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
where
C: ConnectionTrait,
{
db.query_all(&self.query)?
.into_iter()
.map(|row| self.selector.from_raw_query_result(row))
.try_collect()
}
}