use std::future::Future;
use std::marker::PhantomData;
use futures_lite::StreamExt;
use tokio::net::TcpStream;
use tokio_util::compat::Compat;
use crate::structs::into_result::IntoResult;
use crate::structs::query_core::{Executable, QueryCore};
use crate::structs::ssql_marker::SsqlMarker;
use crate::structs::JoinArg;
use crate::{ColExpr, FilterExpr, RowStream, SsqlResult};
pub trait CoreVisitor<'a> {
fn core_mut(&mut self) -> &mut QueryCore<'a>;
fn core_ref(&self) -> &QueryCore<'a>;
}
pub trait QueryAble<'a>: Send + Sync + CoreVisitor<'a>
where
Self::Ret: IntoResult + Send + Sync + 'static,
{
#[doc(hidden)]
type NxtModel<NxtType: SsqlMarker>;
#[doc(hidden)]
type Ret;
fn all(
&self,
conn: &mut tiberius::Client<Compat<TcpStream>>,
) -> impl Future<Output = SsqlResult<Vec<Self::Ret>>> + Send {
async move {
let mut stream = self.core_ref().execute(conn).await?.into_row_stream();
let mut ret = vec![];
while let Some(row) = stream.try_next().await? {
ret.push(Self::Ret::to_struct(&row));
}
Ok(ret)
}
}
fn stream<'b>(
&self,
conn: &'b mut tiberius::Client<Compat<TcpStream>>,
) -> impl Future<Output = SsqlResult<RowStream<'b, Self::Ret>>> + Send {
async move {
let stream = self.core_ref().execute(conn).await?;
Ok(RowStream::new(stream, Self::Ret::to_struct))
}
}
fn one(
&self,
conn: &mut tiberius::Client<Compat<TcpStream>>,
) -> impl Future<Output = SsqlResult<Option<Self::Ret>>> + Send {
async move {
let row = self.core_ref().execute(conn).await?.into_row().await?;
match row {
None => Ok(None),
Some(row) => Ok(Some(Self::Ret::to_struct(&row))),
}
}
}
#[cfg(feature = "serde")]
fn json(
&self,
conn: &mut tiberius::Client<Compat<TcpStream>>,
) -> impl Future<Output = SsqlResult<Vec<<<Self as QueryAble<'a>>::Ret as IntoResult>::Js>>> + Send
{
async move {
let mut stream = self.core_ref().execute(conn).await?.into_row_stream();
let mut ret = vec![];
while let Some(row) = stream.try_next().await? {
ret.push(Self::Ret::to_json(&row))
}
Ok(ret)
}
}
#[cfg(feature = "polars")]
fn df(
&self,
conn: &mut tiberius::Client<Compat<TcpStream>>,
) -> impl Future<Output = SsqlResult<<Self::Ret as IntoResult>::Df>> + Send {
async move { Self::Ret::df(self.core_ref().execute(conn).await?).await }
}
fn join<NxtType>(self, join_args: JoinArg) -> Self::NxtModel<NxtType>
where
NxtType: SsqlMarker;
fn left_join<NxtType>(self) -> Self::NxtModel<NxtType>
where
NxtType: SsqlMarker,
Self: Sized,
{
self.join::<NxtType>(JoinArg::Left)
}
fn right_join<NxtType>(self) -> Self::NxtModel<NxtType>
where
NxtType: SsqlMarker,
Self: Sized,
{
self.join::<NxtType>(JoinArg::Right)
}
fn inner_join<NxtType>(self) -> Self::NxtModel<NxtType>
where
NxtType: SsqlMarker,
Self: Sized,
{
self.join::<NxtType>(JoinArg::Inner)
}
fn outer_join<NxtType>(self) -> Self::NxtModel<NxtType>
where
NxtType: SsqlMarker,
Self: Sized,
{
self.join::<NxtType>(JoinArg::Outer)
}
fn filter(mut self, filter_expr: FilterExpr<'a>) -> SsqlResult<Self>
where
Self: Sized,
{
self.core_mut().filter(filter_expr)?;
Ok(self)
}
fn order_by_asc(mut self, col_expr: ColExpr) -> SsqlResult<Self>
where
Self: Sized,
{
self.core_mut().order_by(col_expr, true)?;
Ok(self)
}
fn order_by_desc(mut self, col_expr: ColExpr) -> SsqlResult<Self>
where
Self: Sized,
{
self.core_mut().order_by(col_expr, false)?;
Ok(self)
}
}
pub struct QueryBuilderI<'a, Ta>
where
Ta: SsqlMarker,
{
core: QueryCore<'a>,
ta: PhantomData<Ta>,
}
impl<'a, T> QueryBuilderI<'a, T>
where
T: SsqlMarker,
{
pub fn new(fields: (&'static str, Vec<&'static str>), func: fn(&str) -> &'static str) -> Self {
let core = QueryCore::new(fields, func);
Self {
core,
ta: Default::default(),
}
}
}
pub struct QueryBuilderII<'a, Ta, Tb>
where
Ta: SsqlMarker,
Tb: SsqlMarker,
{
core: QueryCore<'a>,
ta: PhantomData<Ta>,
tb: PhantomData<Tb>,
}
pub struct QueryBuilderIII<'a, Ta, Tb, Tc>
where
Ta: SsqlMarker,
Tb: SsqlMarker,
Tc: SsqlMarker,
{
core: QueryCore<'a>,
ta: PhantomData<Ta>,
tb: PhantomData<Tb>,
tc: PhantomData<Tc>,
}
pub struct QueryBuilderIV<'a, Ta, Tb, Tc, Td>
where
Ta: SsqlMarker,
Tb: SsqlMarker,
Tc: SsqlMarker,
Td: SsqlMarker,
{
core: QueryCore<'a>,
ta: PhantomData<Ta>,
tb: PhantomData<Tb>,
tc: PhantomData<Tc>,
td: PhantomData<Td>,
}
pub struct QueryBuilderV<'a, Ta, Tb, Tc, Td, Te>
where
Ta: SsqlMarker,
Tb: SsqlMarker,
Tc: SsqlMarker,
Td: SsqlMarker,
Te: SsqlMarker,
{
core: QueryCore<'a>,
ta: PhantomData<Ta>,
tb: PhantomData<Tb>,
tc: PhantomData<Tc>,
td: PhantomData<Td>,
te: PhantomData<Te>,
}
impl<'a, Ta> QueryAble<'a> for QueryBuilderI<'a, Ta>
where
Ta: SsqlMarker + Send + Sync + 'static,
{
type NxtModel<NxtType: SsqlMarker> = QueryBuilderII<'a, Ta, NxtType>;
type Ret = Ta;
fn join<NxtType>(self, join_args: JoinArg) -> Self::NxtModel<NxtType>
where
NxtType: SsqlMarker,
{
QueryBuilderII {
core: self.core.join::<NxtType>(join_args),
ta: Default::default(),
tb: Default::default(),
}
}
}
impl_queryable!(QueryBuilderII, QueryBuilderIII, [Ta, Tb], [ta, tb, tc]);
impl_queryable!(
QueryBuilderIII,
QueryBuilderIV,
[Ta, Tb, Tc],
[ta, tb, tc, td]
);
impl_queryable!(
QueryBuilderIV,
QueryBuilderV,
[Ta, Tb, Tc, Td],
[ta, tb, tc, td, te]
);
impl<'a, Ta, Tb, Tc, Td, Te> QueryAble<'a> for QueryBuilderV<'a, Ta, Tb, Tc, Td, Te>
where
Ta: SsqlMarker + Send + Sync + 'static,
Tb: SsqlMarker + Send + Sync + 'static,
Tc: SsqlMarker + Send + Sync + 'static,
Td: SsqlMarker + Send + Sync + 'static,
Te: SsqlMarker + Send + Sync + 'static,
{
type NxtModel<NxtType: SsqlMarker> = PhantomData<NxtType>;
type Ret = (Ta, Tb, Tc, Td, Te);
fn join<NxtType>(self, _join_args: JoinArg) -> Self::NxtModel<NxtType>
where
NxtType: SsqlMarker,
{
unimplemented!()
}
}
impl<'a, Ta> CoreVisitor<'a> for QueryBuilderI<'a, Ta>
where
Ta: SsqlMarker,
{
fn core_mut(&mut self) -> &mut QueryCore<'a> {
&mut self.core
}
fn core_ref(&self) -> &QueryCore<'a> {
&self.core
}
}
impl_corevisitor!(QueryBuilderII, [Ta, Tb]);
impl_corevisitor!(QueryBuilderIII, [Ta, Tb, Tc]);
impl_corevisitor!(QueryBuilderIV, [Ta, Tb, Tc, Td]);
impl_corevisitor!(QueryBuilderV, [Ta, Tb, Tc, Td, Te]);