use std::fmt::Display;
use std::marker::PhantomData;
use std::sync::Arc;
use tokio_postgres::types::ToSql;
use crate::entity::slice_query_value_iter;
use crate::prelude::{BoxedSql, ColumnType, DatabaseConnection, Entity, EntityColumn, QueryCondition, UntypedColumn};
use crate::result_mapping::ResultMapping;
pub struct SelectQueryType;
pub struct DeleteQueryType;
#[derive(Debug, Clone)]
pub enum OrderDirection {
ASC,
DESC,
}
impl Display for OrderDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
OrderDirection::ASC => "ASC",
OrderDirection::DESC => "DESC",
})
}
}
pub struct Query<T: Entity, R: ResultMapping, QT> {
base_query: BoxedSql,
condition: Option<QueryCondition<T>>,
group_by: Vec<BoxedSql>,
order: Vec<(BoxedSql, OrderDirection)>,
phantom: PhantomData<(R, QT)>,
}
impl<T: Entity, R: ResultMapping, QT> Query<T, R, QT> {
pub fn new(base_query: BoxedSql) -> Query<T, R, QT> {
Self {
base_query,
condition: None,
group_by: vec![],
order: vec![],
phantom: PhantomData,
}
}
pub fn condition(mut self, condition: QueryCondition<T>) -> Query<T, R, QT> {
self.condition = Some(condition);
self
}
fn get_raw_query(self) -> (String, Vec<Arc<Box<dyn ToSql + Send + Sync>>>) {
let (mut query, mut values, mut index) = self.base_query.resolve(1);
if self.condition.is_some() {
let (condition_query, condition_values, next_index) =
self.condition.unwrap().resolve(index);
index = next_index;
values = condition_values;
query.push_str(" WHERE ");
query.push_str(&*condition_query);
}
if !self.group_by.is_empty() {
query.push_str(" GROUP BY ");
let mut grouped_by = vec![];
for x in self.group_by {
grouped_by.push(x.sql);
}
query.push_str(&*grouped_by.join(","));
}
if !self.order.is_empty() {
query.push_str(" ORDER BY ");
let mut orders = vec![];
for (order_name, order_dir) in self.order {
let (order_query, order_values, _) = order_name.resolve(index);
values.extend(order_values);
orders.push(format!("{} {}", order_query, order_dir.to_string()));
}
query.push_str(&*orders.join(","));
}
(query, values)
}
}
impl<T: Entity, R: ResultMapping> Query<T, R, SelectQueryType> {
pub fn add_order(
mut self,
order: &(dyn UntypedColumn<T>),
order_direction: OrderDirection,
) -> Query<T, R, SelectQueryType> {
self.order.push((order.get_sql(), order_direction));
self
}
pub fn order(
mut self,
order: &(dyn UntypedColumn<T>),
order_direction: OrderDirection,
) -> Query<T, R, SelectQueryType> {
self.order = vec![(order.get_sql(), order_direction)];
self
}
pub fn add_group_by<U: ColumnType>(
mut self,
group_by: &EntityColumn<U ,T>,
) -> Query<T, R, SelectQueryType> {
self.group_by.push(group_by.get_sql());
self
}
pub fn group_by<U: ColumnType>(
mut self,
group_by: &EntityColumn<U ,T>,
) -> Query<T, R, SelectQueryType> {
self.group_by = vec![group_by.get_sql()];
self
}
pub async fn fetch(self, connection: &impl DatabaseConnection) -> crate::Result<Vec<R>> {
let (query, values) = self.get_raw_query();
let rows = connection
.query_many(
&*query,
slice_query_value_iter(values.as_slice())
.collect::<Vec<&(dyn ToSql + Sync)>>()
.as_slice(),
)
.await?;
Ok(rows.into_iter().map(|r| R::from_row(r)).collect::<Vec<R>>())
}
pub async fn fetch_single(self, connection: &impl DatabaseConnection) -> crate::Result<R> {
let (query, values) = self.get_raw_query();
let row = connection
.query_single(
&*query,
slice_query_value_iter(values.as_slice())
.collect::<Vec<&(dyn ToSql + Sync)>>()
.as_slice(),
)
.await?;
Ok(R::from_row(row))
}
}
impl<T: Entity, R: ResultMapping> Query<T, R, DeleteQueryType> {
pub async fn execute(self, connection: &impl DatabaseConnection) -> crate::Result<()> {
let (query, values) = self.get_raw_query();
connection
.execute_query(
&*query,
slice_query_value_iter(values.as_slice())
.collect::<Vec<&(dyn ToSql + Sync)>>()
.as_slice(),
)
.await?;
Ok(())
}
}