use super::{
consolidate_query_result, consolidate_query_result_chain, consolidate_query_result_tee,
};
use crate::{
ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, IdenStatic, PartialModelTrait,
QueryResult, QuerySelect, Select, SelectA, SelectB, SelectTwo, SelectTwoMany,
SelectTwoRequired, Statement, StreamTrait, TryGetableMany, error::*,
};
use itertools::Itertools;
use sea_query::SelectStatement;
use std::marker::PhantomData;
mod five;
mod four;
mod six;
mod three;
#[cfg(feature = "with-json")]
use crate::JsonValue;
#[cfg(not(feature = "sync"))]
type PinBoxStream<'b, S> = Pin<Box<dyn Stream<Item = Result<S, DbErr>> + 'b>>;
#[cfg(feature = "sync")]
type PinBoxStream<'b, S> = Box<dyn Iterator<Item = Result<S, DbErr>> + 'b>;
#[derive(Clone, Debug)]
pub struct Selector<S>
where
S: SelectorTrait,
{
pub(crate) query: SelectStatement,
selector: PhantomData<S>,
}
#[derive(Clone, Debug)]
pub struct SelectorRaw<S>
where
S: SelectorTrait,
{
pub(crate) stmt: Statement,
pub(super) selector: PhantomData<S>,
}
pub trait SelectorTrait {
#[allow(missing_docs)]
type Item: Sized;
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>;
}
#[derive(Debug)]
pub struct SelectGetableValue<T, C>
where
T: TryGetableMany,
C: strum::IntoEnumIterator + sea_query::Iden,
{
columns: PhantomData<C>,
model: PhantomData<T>,
}
#[derive(Debug)]
pub struct SelectGetableTuple<T>
where
T: TryGetableMany,
{
model: PhantomData<T>,
}
#[derive(Debug)]
pub struct SelectModel<M>
where
M: FromQueryResult,
{
model: PhantomData<M>,
}
#[derive(Clone, Debug)]
pub struct SelectTwoModel<M, N>
where
M: FromQueryResult,
N: FromQueryResult,
{
model: PhantomData<(M, N)>,
}
#[derive(Clone, Debug)]
pub struct SelectTwoRequiredModel<M, N>
where
M: FromQueryResult,
N: FromQueryResult,
{
model: PhantomData<(M, N)>,
}
#[derive(Clone, Debug)]
pub struct SelectThreeModel<M, N, O>
where
M: FromQueryResult,
N: FromQueryResult,
O: FromQueryResult,
{
model: PhantomData<(M, N, O)>,
}
#[derive(Clone, Debug)]
pub struct SelectFourModel<M, N, O, P>
where
M: FromQueryResult,
N: FromQueryResult,
O: FromQueryResult,
P: FromQueryResult,
{
model: PhantomData<(M, N, O, P)>,
}
#[derive(Clone, Debug)]
pub struct SelectFiveModel<M, N, O, P, Q>
where
M: FromQueryResult,
N: FromQueryResult,
O: FromQueryResult,
P: FromQueryResult,
Q: FromQueryResult,
{
model: PhantomData<(M, N, O, P, Q)>,
}
#[derive(Clone, Debug)]
pub struct SelectSixModel<M, N, O, P, Q, R>
where
M: FromQueryResult,
N: FromQueryResult,
O: FromQueryResult,
P: FromQueryResult,
Q: FromQueryResult,
R: FromQueryResult,
{
model: PhantomData<(M, N, O, P, Q, R)>,
}
impl<T, C> Default for SelectGetableValue<T, C>
where
T: TryGetableMany,
C: strum::IntoEnumIterator + sea_query::Iden,
{
fn default() -> Self {
Self {
columns: PhantomData,
model: PhantomData,
}
}
}
impl<T, C> SelectorTrait for SelectGetableValue<T, C>
where
T: TryGetableMany,
C: strum::IntoEnumIterator + sea_query::Iden,
{
type Item = T;
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
let cols: Vec<String> = C::iter().map(|col| col.to_string()).collect();
T::try_get_many(&res, "", &cols).map_err(Into::into)
}
}
impl<T> SelectorTrait for SelectGetableTuple<T>
where
T: TryGetableMany,
{
type Item = T;
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
T::try_get_many_by_index(&res).map_err(Into::into)
}
}
impl<M> SelectorTrait for SelectModel<M>
where
M: FromQueryResult + Sized,
{
type Item = M;
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
M::from_query_result(&res, "")
}
}
impl<M, N> SelectorTrait for SelectTwoModel<M, N>
where
M: FromQueryResult + Sized,
N: FromQueryResult + Sized,
{
type Item = (M, Option<N>);
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
Ok((
M::from_query_result(&res, SelectA.as_str())?,
N::from_query_result_optional(&res, SelectB.as_str())?,
))
}
}
impl<M, N> SelectorTrait for SelectTwoRequiredModel<M, N>
where
M: FromQueryResult + Sized,
N: FromQueryResult + Sized,
{
type Item = (M, N);
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
Ok((
M::from_query_result(&res, SelectA.as_str())?,
N::from_query_result(&res, SelectB.as_str())?,
))
}
}
impl<E> Select<E>
where
E: EntityTrait,
{
#[allow(clippy::wrong_self_convention)]
pub fn from_raw_sql(self, stmt: Statement) -> SelectorRaw<SelectModel<E::Model>> {
SelectorRaw {
stmt,
selector: PhantomData,
}
}
pub fn into_model<M>(self) -> Selector<SelectModel<M>>
where
M: FromQueryResult,
{
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn into_partial_model<M>(self) -> Selector<SelectModel<M>>
where
M: PartialModelTrait,
{
M::select_cols(QuerySelect::select_only(self)).into_model::<M>()
}
#[cfg(feature = "with-json")]
pub fn into_json(self) -> Selector<SelectModel<JsonValue>> {
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn into_values<T, C>(self) -> Selector<SelectGetableValue<T, C>>
where
T: TryGetableMany,
C: strum::IntoEnumIterator + sea_query::Iden,
{
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn into_tuple<T>(self) -> Selector<SelectGetableTuple<T>>
where
T: TryGetableMany,
{
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn one<C>(self, db: &C) -> Result<Option<E::Model>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().one(db)
}
pub fn all<C>(self, db: &C) -> Result<Vec<E::Model>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().all(db)
}
pub fn stream<'a: 'b, 'b, C>(
self,
db: &'a C,
) -> Result<impl Iterator<Item = Result<E::Model, DbErr>> + 'b, DbErr>
where
C: ConnectionTrait + StreamTrait,
{
self.into_model().stream(db)
}
pub fn stream_partial_model<'a: 'b, 'b, C, M>(
self,
db: &'a C,
) -> Result<impl Iterator<Item = Result<M, DbErr>> + 'b, DbErr>
where
C: ConnectionTrait + StreamTrait,
M: PartialModelTrait + 'b,
{
self.into_partial_model().stream(db)
}
}
impl<E, F> SelectTwo<E, F>
where
E: EntityTrait,
F: EntityTrait,
{
pub fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
where
M: FromQueryResult,
N: FromQueryResult,
{
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn into_partial_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
where
M: PartialModelTrait,
N: PartialModelTrait,
{
let select = QuerySelect::select_only(self);
let select = M::select_cols(select);
let select = N::select_cols(select);
select.into_model::<M, N>()
}
#[cfg(feature = "with-json")]
pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn one<C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().one(db)
}
pub fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().all(db)
}
pub fn stream<'a: 'b, 'b, C>(
self,
db: &'a C,
) -> Result<impl Iterator<Item = Result<(E::Model, Option<F::Model>), DbErr>> + 'b, DbErr>
where
C: ConnectionTrait + StreamTrait,
{
self.into_model().stream(db)
}
pub fn stream_partial_model<'a: 'b, 'b, C, M, N>(
self,
db: &'a C,
) -> Result<impl Iterator<Item = Result<(M, Option<N>), DbErr>> + 'b, DbErr>
where
C: ConnectionTrait + StreamTrait,
M: PartialModelTrait + 'b,
N: PartialModelTrait + 'b,
{
self.into_partial_model().stream(db)
}
}
impl<E, F> SelectTwoMany<E, F>
where
E: EntityTrait,
F: EntityTrait,
{
fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
where
M: FromQueryResult,
N: FromQueryResult,
{
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr>
where
C: ConnectionTrait,
{
let rows = self.into_model().all(db)?;
Ok(consolidate_query_result::<E, F>(rows))
}
}
impl<E, F> SelectTwoRequired<E, F>
where
E: EntityTrait,
F: EntityTrait,
{
pub fn into_model<M, N>(self) -> Selector<SelectTwoRequiredModel<M, N>>
where
M: FromQueryResult,
N: FromQueryResult,
{
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn into_partial_model<M, N>(self) -> Selector<SelectTwoRequiredModel<M, N>>
where
M: PartialModelTrait,
N: PartialModelTrait,
{
let select = QuerySelect::select_only(self);
let select = M::select_cols(select);
let select = N::select_cols(select);
select.into_model::<M, N>()
}
#[cfg(feature = "with-json")]
pub fn into_json(self) -> Selector<SelectTwoRequiredModel<JsonValue, JsonValue>> {
Selector {
query: self.query,
selector: PhantomData,
}
}
pub fn one<C>(self, db: &C) -> Result<Option<(E::Model, F::Model)>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().one(db)
}
pub fn all<C>(self, db: &C) -> Result<Vec<(E::Model, F::Model)>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().all(db)
}
pub fn stream<'a: 'b, 'b, C>(
self,
db: &'a C,
) -> Result<impl Iterator<Item = Result<(E::Model, F::Model), DbErr>> + 'b, DbErr>
where
C: ConnectionTrait + StreamTrait,
{
self.into_model().stream(db)
}
pub fn stream_partial_model<'a: 'b, 'b, C, M, N>(
self,
db: &'a C,
) -> Result<impl Iterator<Item = Result<(M, N), DbErr>> + 'b, DbErr>
where
C: ConnectionTrait + StreamTrait,
M: PartialModelTrait + 'b,
N: PartialModelTrait + 'b,
{
self.into_partial_model().stream(db)
}
}
impl<S> Selector<S>
where
S: SelectorTrait,
{
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(S::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| S::from_raw_query_result(row))
.try_collect()
}
pub fn stream<'a: 'b, 'b, C>(self, db: &'a C) -> Result<PinBoxStream<'b, S::Item>, DbErr>
where
C: ConnectionTrait + StreamTrait,
S: 'b,
{
let stream = db.stream(&self.query)?;
#[cfg(not(feature = "sync"))]
{
Ok(Box::new(stream.and_then(|row| {
futures_util::future::ready(S::from_raw_query_result(row))
})))
}
#[cfg(feature = "sync")]
{
Ok(Box::new(
stream.map(|item| item.and_then(S::from_raw_query_result)),
))
}
}
}
impl<S> SelectorRaw<S>
where
S: SelectorTrait,
{
pub fn from_statement<M>(stmt: Statement) -> SelectorRaw<SelectModel<M>>
where
M: FromQueryResult,
{
SelectorRaw {
stmt,
selector: PhantomData,
}
}
pub fn into_model<M>(self) -> SelectorRaw<SelectModel<M>>
where
M: FromQueryResult,
{
SelectorRaw {
stmt: self.stmt,
selector: PhantomData,
}
}
#[cfg(feature = "with-json")]
pub fn into_json(self) -> SelectorRaw<SelectModel<JsonValue>> {
SelectorRaw {
stmt: self.stmt,
selector: PhantomData,
}
}
pub fn into_statement(self) -> Statement {
self.stmt
}
pub fn one<C>(self, db: &C) -> Result<Option<S::Item>, DbErr>
where
C: ConnectionTrait,
{
let row = db.query_one_raw(self.stmt)?;
match row {
Some(row) => Ok(Some(S::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_raw(self.stmt)?
.into_iter()
.map(|row| S::from_raw_query_result(row))
.try_collect()
}
pub fn stream<'a: 'b, 'b, C>(self, db: &'a C) -> Result<PinBoxStream<'b, S::Item>, DbErr>
where
C: ConnectionTrait + StreamTrait,
S: 'b,
{
let stream = db.stream_raw(self.stmt)?;
#[cfg(not(feature = "sync"))]
{
Ok(Box::new(stream.and_then(|row| {
futures_util::future::ready(S::from_raw_query_result(row))
})))
}
#[cfg(feature = "sync")]
{
Ok(Box::new(
stream.map(|item| item.and_then(S::from_raw_query_result)),
))
}
}
}