use super::{Delete, Expr, IntoStatement, List, Statement, Value};
use crate::{
Executor, Result,
schema::{Load, Model},
};
use std::{fmt, marker::PhantomData};
use toasty_core::stmt::{self, Offset, Returning};
pub struct Query<T> {
pub(crate) untyped: stmt::Query,
_p: PhantomData<T>,
}
impl<T> Query<T> {
pub fn unit() -> Self {
Self {
untyped: stmt::Query::unit(),
_p: PhantomData,
}
}
pub(crate) const fn from_untyped(untyped: stmt::Query) -> Self {
Self {
untyped,
_p: PhantomData,
}
}
pub fn from_expr(expr: Expr<T>) -> Self {
match expr.untyped {
stmt::Expr::Stmt(expr) => match *expr.stmt {
stmt::Statement::Query(stmt) => Self::from_untyped(stmt),
stmt => todo!("stmt={stmt:#?}"),
},
expr => Self::from_untyped(stmt::Query::values(expr)),
}
}
pub fn and(mut self, filter: Expr<bool>) -> Self {
self.untyped.add_filter(filter.untyped);
self
}
pub fn include(&mut self, path: impl Into<stmt::Path>) -> &mut Self {
self.untyped.include(path.into());
self
}
pub fn order_by(&mut self, order_by: impl Into<stmt::OrderBy>) -> &mut Self {
self.untyped.order_by = Some(order_by.into());
self
}
pub fn limit(&mut self, n: usize) -> &mut Self {
self.untyped.limit = Some(stmt::Limit {
limit: stmt::Value::from(n as i64).into(),
offset: None,
});
self
}
pub fn offset(&mut self, n: usize) -> &mut Self {
self.untyped.limit = match self.untyped.limit.take() {
Some(limit) => Some(stmt::Limit {
limit: limit.limit,
offset: Some(Offset::Count(stmt::Expr::Value(Value::from(n)))),
}),
None => panic!("limit required for offset"),
};
self
}
pub fn delete(self) -> Delete<()> {
Delete::from_untyped(self.untyped.delete())
}
pub fn to_list(mut self) -> Query<List<T>> {
assert!(self.untyped.single, "not a single query");
self.untyped.single = false;
Query {
untyped: self.untyped,
_p: PhantomData,
}
}
}
impl<T> Query<List<T>> {
pub fn first(mut self) -> Query<Option<T>> {
set_first(&mut self.untyped);
Query {
untyped: self.untyped,
_p: PhantomData,
}
}
pub fn one(mut self) -> Query<T> {
set_first(&mut self.untyped);
Query {
untyped: self.untyped,
_p: PhantomData,
}
}
}
fn set_first(query: &mut stmt::Query) {
assert!(!query.single, "query is single");
query.single = true;
}
impl<T: Load> Query<T> {
pub async fn exec(self, executor: &mut dyn Executor) -> Result<T::Output> {
executor.exec(self.into_statement()).await
}
}
impl<M: Model> Query<List<M>> {
pub fn filter(expr: Expr<bool>) -> Self {
let mut query = stmt::Query::new_select(M::id(), expr.untyped);
query.single = false;
Self::from_untyped(query)
}
pub fn count(mut self) -> Query<u64> {
*self.untyped.returning_mut_unwrap() = Returning::Expr(stmt::Expr::count_star());
self.untyped.single = true;
Query::from_untyped(self.untyped)
}
pub fn all() -> Self {
let filter = stmt::Expr::Value(Value::from_bool(true));
let mut query = stmt::Query::new_select(M::id(), filter);
query.single = false;
Self::from_untyped(query)
}
}
impl<T> IntoStatement for Query<T> {
type Returning = T;
fn into_statement(self) -> Statement<T> {
Statement::from_untyped_stmt(self.untyped.into())
}
}
impl<T> IntoStatement for &Query<T> {
type Returning = T;
fn into_statement(self) -> Statement<T> {
Statement::from_untyped_stmt(self.clone().untyped.into())
}
}
impl<T> Clone for Query<T> {
fn clone(&self) -> Self {
Self {
untyped: self.untyped.clone(),
_p: PhantomData,
}
}
}
impl<T> fmt::Debug for Query<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.untyped.fmt(fmt)
}
}