use super::{AggStatus, PkGroup, Scoped, SelectAs, SelectCols, SelectExpr, SelectStar};
use crate::error::DrizzleError;
use crate::expr::{AllAgg, AllScalar, MixedAgg};
use crate::{Cons, ExecutableState};
mod sealed {
pub trait Sealed {}
impl Sealed for super::ExactlyOne {}
impl Sealed for super::ZeroOrMore {}
}
pub trait RowCount: sealed::Sealed {
type Get<R>;
fn first<R>(row: Option<R>) -> Result<Self::Get<R>, DrizzleError>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ExactlyOne;
impl RowCount for ExactlyOne {
type Get<R> = R;
fn first<R>(row: Option<R>) -> Result<R, DrizzleError> {
row.ok_or(DrizzleError::NotFound)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ZeroOrMore;
impl RowCount for ZeroOrMore {
type Get<R> = Option<R>;
fn first<R>(row: Option<R>) -> Result<Option<R>, DrizzleError> {
Ok(row)
}
}
pub trait QueryRows<Grouped, State> {
type Rows: RowCount;
}
impl<Mk, Grouped, State> QueryRows<Grouped, State> for Mk
where
Mk: SelectionRows,
Grouped: GroupingRows,
State: ExecutableState,
{
type Rows = State::Rows<Grouped::Rows<Mk::Rows>>;
}
pub type GetRow<Mk, Grouped, State, R> =
<<Mk as QueryRows<Grouped, State>>::Rows as RowCount>::Get<R>;
pub trait SelectionRows {
type Rows: RowCount;
}
impl<M: SelectionRows, Scope, Used> SelectionRows for Scoped<M, Scope, Used> {
type Rows = M::Rows;
}
impl SelectionRows for SelectStar {
type Rows = ZeroOrMore;
}
impl SelectionRows for SelectExpr {
type Rows = ZeroOrMore;
}
impl<R> SelectionRows for SelectAs<R> {
type Rows = ZeroOrMore;
}
impl<Cols> SelectionRows for SelectCols<Cols>
where
Cols: AggStatus,
Cols::Status: StatusRows,
{
type Rows = <Cols::Status as StatusRows>::Rows;
}
pub trait GroupingRows {
type Rows<R: RowCount>: RowCount;
}
impl GroupingRows for () {
type Rows<R: RowCount> = R;
}
impl<Head, Tail> GroupingRows for Cons<Head, Tail> {
type Rows<R: RowCount> = ZeroOrMore;
}
impl<Table> GroupingRows for PkGroup<Table> {
type Rows<R: RowCount> = ZeroOrMore;
}
pub trait StatusRows {
type Rows: RowCount;
}
impl StatusRows for AllAgg {
type Rows = ExactlyOne;
}
impl StatusRows for AllScalar {
type Rows = ZeroOrMore;
}
impl StatusRows for MixedAgg {
type Rows = ZeroOrMore;
}
impl<V: crate::SQLParam> crate::expr::HasAggStatus for crate::sql::SQL<'_, V> {
type Status = AllScalar;
}
#[doc(hidden)]
pub fn first_row<Mk, Grouped, State, R>(
row: Option<R>,
) -> Result<GetRow<Mk, Grouped, State, R>, DrizzleError>
where
Mk: QueryRows<Grouped, State>,
{
<Mk::Rows as RowCount>::first(row)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::builder::{SelectFromSet, SelectOrderSet, SelectSetOpSet, SelectWhereSet};
fn rows_of<S: ExecutableState>() -> &'static str {
core::any::type_name::<S::Rows<ExactlyOne>>()
}
#[test]
fn only_clauses_that_keep_every_row_keep_an_aggregate_s_one_row() {
assert!(rows_of::<SelectFromSet>().ends_with("ExactlyOne"));
assert!(rows_of::<SelectWhereSet>().ends_with("ExactlyOne"));
assert!(rows_of::<SelectSetOpSet>().ends_with("ZeroOrMore"));
assert!(rows_of::<SelectOrderSet>().ends_with("ZeroOrMore"));
}
}