use core::marker::PhantomData;
use crate::SQLParam;
use crate::relation::RelationDef;
use super::builder::{
AllColumns, Clauses, HasLimit, HasOffset, HasOrderBy, HasWhere, IntoColumnSelection, NoLimit,
NoOrderBy, NoWhere, PartialColumns, QueryTable,
};
pub struct RelationHandle<V: SQLParam, R: RelationDef, Nested = (), Cols = AllColumns, Cl = Clauses>
{
pub(crate) where_clause: String,
pub(crate) where_params: Vec<V>,
pub(crate) order_by_clause: String,
pub(crate) limit: Option<u32>,
pub(crate) offset: Option<u32>,
pub(crate) nested: Nested,
pub(crate) cols: Cols,
pub(crate) _marker: PhantomData<(R, Cl)>,
}
impl<V: SQLParam, R: RelationDef> RelationHandle<V, R> {
#[must_use]
pub const fn new() -> Self {
Self {
where_clause: String::new(),
where_params: Vec::new(),
order_by_clause: String::new(),
limit: None,
offset: None,
nested: (),
cols: AllColumns,
_marker: PhantomData,
}
}
}
impl<V: SQLParam, R: RelationDef> Default for RelationHandle<V, R> {
fn default() -> Self {
Self::new()
}
}
impl<V: SQLParam, R: RelationDef, Nested, Cols, Cl> RelationHandle<V, R, Nested, Cols, Cl> {
#[allow(clippy::type_complexity)]
pub fn with<NR, NN, NC, NCl>(
self,
handle: RelationHandle<V, NR, NN, NC, NCl>,
) -> RelationHandle<V, R, (RelationHandle<V, NR, NN, NC, NCl>, Nested), Cols, Cl>
where
NR: RelationDef<Source = R::Target> + 'static,
{
RelationHandle {
where_clause: self.where_clause,
where_params: self.where_params,
order_by_clause: self.order_by_clause,
limit: self.limit,
offset: self.offset,
nested: (handle, self.nested),
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<V: SQLParam, R: RelationDef, Nested, Cols, Ord, Lim>
RelationHandle<V, R, Nested, Cols, Clauses<NoWhere, Ord, Lim>>
{
pub fn r#where<'a, E>(
self,
condition: E,
) -> RelationHandle<V, R, Nested, Cols, Clauses<HasWhere, Ord, Lim>>
where
E: crate::expr::Expr<'a, V>,
E::SQLType: crate::types::BooleanLike,
V: 'a,
{
let sql = condition.to_sql();
let (text, params) = sql.build();
RelationHandle {
where_clause: text,
where_params: params.into_iter().cloned().collect(),
order_by_clause: self.order_by_clause,
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<V: SQLParam, R: RelationDef, Nested, Cols, W, Lim>
RelationHandle<V, R, Nested, Cols, Clauses<W, NoOrderBy, Lim>>
{
pub fn order_by<'a, E>(
self,
expr: E,
) -> RelationHandle<V, R, Nested, Cols, Clauses<W, HasOrderBy, Lim>>
where
E: crate::traits::ToSQL<'a, V>,
V: 'a,
{
let sql = expr.to_sql();
let (text, _) = sql.build();
RelationHandle {
where_clause: self.where_clause,
where_params: self.where_params,
order_by_clause: text,
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<V: SQLParam, R: RelationDef, Nested, Cols, W, Ord>
RelationHandle<V, R, Nested, Cols, Clauses<W, Ord, NoLimit>>
{
pub fn limit(self, n: u32) -> RelationHandle<V, R, Nested, Cols, Clauses<W, Ord, HasLimit>> {
RelationHandle {
where_clause: self.where_clause,
where_params: self.where_params,
order_by_clause: self.order_by_clause,
limit: Some(n),
offset: self.offset,
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
pub fn first(self) -> RelationHandle<V, R, Nested, Cols, Clauses<W, Ord, HasLimit>> {
self.limit(1)
}
}
impl<V: SQLParam, R: RelationDef, Nested, Cols, W, Ord>
RelationHandle<V, R, Nested, Cols, Clauses<W, Ord, HasLimit>>
{
pub fn offset(self, n: u32) -> RelationHandle<V, R, Nested, Cols, Clauses<W, Ord, HasOffset>> {
RelationHandle {
where_clause: self.where_clause,
where_params: self.where_params,
order_by_clause: self.order_by_clause,
limit: self.limit,
offset: Some(n),
nested: self.nested,
cols: self.cols,
_marker: PhantomData,
}
}
}
impl<V: SQLParam, R: RelationDef, Nested, Cl> RelationHandle<V, R, Nested, AllColumns, Cl>
where
R::Target: QueryTable,
{
pub fn columns<S: IntoColumnSelection>(
self,
selector: S,
) -> RelationHandle<V, R, Nested, PartialColumns, Cl> {
RelationHandle {
where_clause: self.where_clause,
where_params: self.where_params,
order_by_clause: self.order_by_clause,
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: PartialColumns {
columns: selector.into_column_names(),
},
_marker: PhantomData,
}
}
pub fn omit<S: IntoColumnSelection>(
self,
selector: S,
) -> RelationHandle<V, R, Nested, PartialColumns, Cl> {
let omitted = selector.into_column_names();
let columns = <R::Target as QueryTable>::COLUMN_NAMES
.iter()
.copied()
.filter(|c| !omitted.contains(c))
.collect();
RelationHandle {
where_clause: self.where_clause,
where_params: self.where_params,
order_by_clause: self.order_by_clause,
limit: self.limit,
offset: self.offset,
nested: self.nested,
cols: PartialColumns { columns },
_marker: PhantomData,
}
}
}