use core::marker::PhantomData;
use crate::expr::{NonNull, Null, Nullability};
use crate::{Cons, Nil};
macro_rules! marker_impls {
($($name:ident<$($p:ident),+>),+ $(,)?) => {$(
impl<$($p),+> Clone for $name<$($p),+> {
fn clone(&self) -> Self {
*self
}
}
impl<$($p),+> Copy for $name<$($p),+> {}
impl<$($p),+> Default for $name<$($p),+> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<$($p),+> core::fmt::Debug for $name<$($p),+> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(stringify!($name))
}
}
)+};
}
marker_impls!(
AliasKey<Tag>,
OuterJoined<T>,
ScopeThere<Prev>,
TableKey<Name, Table>,
Scoped<Marker, Scope, Used>,
Src<T>,
Coalesce<A, B>,
At<Scope, S>,
Lateral<Kind>,
);
pub trait ScopeEntry {
type Key;
type Nullable: Nullability;
type Sources;
}
impl<T: ScopeEntry + ?Sized> ScopeEntry for &T {
type Key = T::Key;
type Nullable = T::Nullable;
type Sources = T::Sources;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct RawSource;
impl<V: crate::SQLParam> ScopeEntry for crate::SQL<'_, V> {
type Key = RawSource;
type Nullable = NonNull;
type Sources = ();
}
pub struct AliasKey<Tag>(PhantomData<Tag>);
impl<Tag> ScopeEntry for AliasKey<Tag> {
type Key = Self;
type Nullable = NonNull;
type Sources = ();
}
pub struct OuterJoined<T>(PhantomData<T>);
impl<T: ScopeEntry> ScopeEntry for OuterJoined<T> {
type Key = T::Key;
type Nullable = Null;
type Sources = ();
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ScopeHere;
pub struct ScopeThere<Prev>(PhantomData<Prev>);
#[derive(Debug, Clone, Copy, Default)]
pub struct ScopeFound;
#[diagnostic::on_unimplemented(
message = "`{Key}` is not in this query's FROM/JOIN scope",
label = "this expression reads a source that the query never joins",
note = "add the source with .from(...) or a .join(...) before using its columns"
)]
pub trait ScopeContains<Key, Witness> {
type Nullable: Nullability;
}
impl<Name, Table, Scope> ScopeContains<TableKey<Name, Table>, ScopeFound> for Scope
where
Scope: FindTable<Table>,
{
type Nullable = Scope::Nullable;
}
impl<Tag, Head, Tail> ScopeContains<AliasKey<Tag>, ScopeHere> for Cons<Head, Tail>
where
Head: ScopeEntry<Key = AliasKey<Tag>>,
{
type Nullable = Head::Nullable;
}
impl<Tag, Head, Tail, Witness> ScopeContains<AliasKey<Tag>, ScopeThere<Witness>>
for Cons<Head, Tail>
where
Tail: ScopeContains<AliasKey<Tag>, Witness>,
{
type Nullable = Tail::Nullable;
}
pub struct TableKey<Name, Table>(PhantomData<(Name, Table)>);
#[derive(Debug, Clone, Copy, Default)]
pub struct True;
#[derive(Debug, Clone, Copy, Default)]
pub struct False;
pub mod name {
use super::{False, True};
macro_rules! nibbles {
($($n:ident),+) => {
$(
#[doc(hidden)]
#[derive(Debug, Clone, Copy, Default)]
pub struct $n;
)+
};
}
nibbles!(
H0, H1, H2, H3, H4, H5, H6, H7, H8, H9, HA, HB, HC, HD, HE, HF
);
pub trait NibEq<Other> {
type Out;
}
macro_rules! nib_eq {
() => {};
($head:ident $(, $rest:ident)*) => {
impl NibEq<$head> for $head {
type Out = True;
}
$(
impl NibEq<$rest> for $head {
type Out = False;
}
impl NibEq<$head> for $rest {
type Out = False;
}
)*
nib_eq!($($rest),*);
};
}
nib_eq!(
H0, H1, H2, H3, H4, H5, H6, H7, H8, H9, HA, HB, HC, HD, HE, HF
);
}
#[doc(hidden)]
pub trait NameEq<Other> {
type Out;
}
impl NameEq<Nil> for Nil {
type Out = True;
}
impl<H, T> NameEq<Cons<H, T>> for Nil {
type Out = False;
}
impl<H, T> NameEq<Nil> for Cons<H, T> {
type Out = False;
}
impl<HA, TA, HB, TB> NameEq<Cons<HB, TB>> for Cons<HA, TA>
where
HA: name::NibEq<HB>,
HA::Out: NameEqRest<TA, TB>,
{
type Out = <HA::Out as NameEqRest<TA, TB>>::Out;
}
#[doc(hidden)]
pub trait NameEqRest<A, B> {
type Out;
}
impl<A: NameEq<B>, B> NameEqRest<A, B> for True {
type Out = A::Out;
}
impl<A, B> NameEqRest<A, B> for False {
type Out = False;
}
#[doc(hidden)]
pub trait TableName {
type Name;
}
impl<Name, Table> TableName for TableKey<Name, Table> {
type Name = Name;
}
#[doc(hidden)]
pub trait IsTable<Table> {
type Out;
}
impl<Table, Other, T> IsTable<Table> for TableKey<Other, T>
where
Table: ScopeEntry,
Table::Key: TableName,
Other: NameEq<<Table::Key as TableName>::Name>,
{
type Out = Other::Out;
}
impl<Table, Tag> IsTable<Table> for AliasKey<Tag> {
type Out = False;
}
impl<Table> IsTable<Table> for RawSource {
type Out = False;
}
#[doc(hidden)]
#[diagnostic::on_unimplemented(
message = "`{Table}` is not in this query's FROM/JOIN scope",
label = "this expression reads a table that the query never joins",
note = "add the table with .from(...) or a .join(...) before using its columns"
)]
pub trait FindTable<Table> {
type Nullable: Nullability;
}
impl<Table, Head, Tail> FindTable<Table> for Cons<Head, Tail>
where
Head: ScopeEntry,
Head::Key: IsTable<Table>,
<Head::Key as IsTable<Table>>::Out: FoundOr<Head::Nullable, Tail, Table>,
{
type Nullable =
<<Head::Key as IsTable<Table>>::Out as FoundOr<Head::Nullable, Tail, Table>>::Nullable;
}
#[doc(hidden)]
pub trait FoundOr<Nullable, Rest, Table> {
type Nullable: Nullability;
}
impl<N: Nullability, Rest, Table> FoundOr<N, Rest, Table> for True {
type Nullable = N;
}
impl<N, Rest, Table> FoundOr<N, Rest, Table> for False
where
Rest: FindTable<Table>,
{
type Nullable = Rest::Nullable;
}
pub struct Scoped<Marker, Scope, Used = ()>(PhantomData<(Marker, Scope, Used)>);
pub trait HasScope {
type Scope;
type Used;
type With<Sources>;
}
impl<Marker, Scope, Used> HasScope for Scoped<Marker, Scope, Used> {
type Scope = Scope;
type Used = Used;
type With<Sources> = Scoped<Marker, Scope, (Used, At<Scope, Sources>)>;
}
pub struct Src<T>(PhantomData<T>);
pub struct Coalesce<A, B>(PhantomData<(A, B)>);
pub type ScopeOnly<S> = Coalesce<S, NonNull>;
pub trait SourcesIn<Scope, Proof> {
type Nullable: Nullability;
}
impl<Scope> SourcesIn<Scope, ()> for () {
type Nullable = NonNull;
}
impl<Scope> SourcesIn<Scope, ()> for NonNull {
type Nullable = NonNull;
}
impl<Scope> SourcesIn<Scope, ()> for Null {
type Nullable = Null;
}
impl<Scope, T, Witness> SourcesIn<Scope, Witness> for Src<T>
where
T: ScopeEntry,
Scope: ScopeContains<T::Key, Witness>,
{
type Nullable = Scope::Nullable;
}
impl<Scope, A, B, ProofA, ProofB> SourcesIn<Scope, (ProofA, ProofB)> for (A, B)
where
A: SourcesIn<Scope, ProofA>,
B: SourcesIn<Scope, ProofB>,
{
type Nullable = <A::Nullable as Nullability>::Or<B::Nullable>;
}
impl<Scope, A, B, ProofA, ProofB> SourcesIn<Scope, (ProofA, ProofB)> for Coalesce<A, B>
where
A: SourcesIn<Scope, ProofA>,
B: SourcesIn<Scope, ProofB>,
{
type Nullable = <A::Nullable as Nullability>::And<B::Nullable>;
}
pub struct At<Scope, S>(PhantomData<(Scope, S)>);
impl<Outer, Scope, S, Proof> SourcesIn<Outer, Proof> for At<Scope, S>
where
Scope: crate::Concat<Outer>,
S: SourcesIn<<Scope as crate::Concat<Outer>>::Output, Proof>,
{
type Nullable = NonNull;
}
pub trait SelectSources {
type Sources;
}
impl SelectSources for crate::row::SelectStar {
type Sources = ();
}
impl SelectSources for crate::row::SelectExpr {
type Sources = ();
}
impl<R> SelectSources for crate::row::SelectAs<R> {
type Sources = ();
}
impl<Cols> SelectSources for crate::row::SelectCols<Cols>
where
Cols: crate::row::SelectedExpressionList,
Cols::Expressions: crate::expr::ExprSources,
{
type Sources = <Cols::Expressions as crate::expr::ExprSources>::Sources;
}
impl<M: SelectSources, Scope, Used> SelectSources for Scoped<M, Scope, Used> {
type Sources = (Used, At<Scope, M::Sources>);
}
pub trait SetOperand<Other> {
type Combined;
}
impl<M, Scope, Used, Other: SelectSources> SetOperand<Other> for Scoped<M, Scope, Used> {
type Combined = Scoped<M, Scope, (Used, Other::Sources)>;
}
impl<Other> SetOperand<Other> for crate::row::SelectStar {
type Combined = Self;
}
impl<Other> SetOperand<Other> for crate::row::SelectExpr {
type Combined = Self;
}
impl<Cols, Other> SetOperand<Other> for crate::row::SelectCols<Cols> {
type Combined = Self;
}
impl<R, Other> SetOperand<Other> for crate::row::SelectAs<R> {
type Combined = Self;
}
pub type Arg<N, S> = (N, S);
pub trait ListContains<T, Witness> {}
impl<Head, Tail> ListContains<Head, ScopeHere> for Cons<Head, Tail> {}
impl<Head, Tail, T, Witness> ListContains<T, ScopeThere<Witness>> for Cons<Head, Tail> where
Tail: ListContains<T, Witness>
{
}
pub trait ListIncludes<Required, Proof> {}
impl<List> ListIncludes<Nil, ()> for List {}
impl<List, Head, Tail, HeadProof, TailProof> ListIncludes<Cons<Head, Tail>, (HeadProof, TailProof)>
for List
where
List: ListContains<Head, HeadProof> + ListIncludes<Tail, TailProof>,
{
}
#[derive(Debug, Clone, Copy, Default)]
pub struct InnerJoin;
#[derive(Debug, Clone, Copy, Default)]
pub struct LeftJoin;
#[derive(Debug, Clone, Copy, Default)]
pub struct RightJoin;
#[derive(Debug, Clone, Copy, Default)]
pub struct FullJoin;
pub struct Lateral<Kind>(PhantomData<Kind>);
#[doc(hidden)]
pub trait OuterJoinScope {
type Out;
}
impl OuterJoinScope for Nil {
type Out = Self;
}
impl<Head, Tail: OuterJoinScope> OuterJoinScope for Cons<Head, Tail> {
type Out = Cons<OuterJoined<Head>, Tail::Out>;
}
#[doc(hidden)]
pub trait JoinRow<Row, Joined, Kind> {
type Row;
}
pub trait JoinStep<Row, Joined, Kind, On = ()> {
type Marker;
type Row;
}
type Joined<M, NewScope, Used, Free, On> = Scoped<M, NewScope, ((Used, Free), At<NewScope, On>)>;
impl<M, Scope, Used, Row, J, On> JoinStep<Row, J, InnerJoin, On> for Scoped<M, Scope, Used>
where
M: JoinRow<Row, J, InnerJoin>,
J: ScopeEntry,
{
type Marker = Joined<M, Cons<J, Scope>, Used, J::Sources, On>;
type Row = M::Row;
}
impl<M, Scope, Used, Row, J, On> JoinStep<Row, J, LeftJoin, On> for Scoped<M, Scope, Used>
where
M: JoinRow<Row, J, LeftJoin>,
J: ScopeEntry,
{
type Marker = Joined<M, Cons<OuterJoined<J>, Scope>, Used, J::Sources, On>;
type Row = M::Row;
}
impl<M, Scope, Used, Row, J, On> JoinStep<Row, J, RightJoin, On> for Scoped<M, Scope, Used>
where
M: JoinRow<Row, J, RightJoin>,
J: ScopeEntry,
Scope: OuterJoinScope,
{
type Marker = Joined<M, Cons<J, Scope::Out>, Used, J::Sources, On>;
type Row = M::Row;
}
impl<M, Scope, Used, Row, J, On> JoinStep<Row, J, FullJoin, On> for Scoped<M, Scope, Used>
where
M: JoinRow<Row, J, FullJoin>,
J: ScopeEntry,
Scope: OuterJoinScope,
{
type Marker = Joined<M, Cons<OuterJoined<J>, Scope::Out>, Used, J::Sources, On>;
type Row = M::Row;
}
impl<M, Scope, Used, Row, J, On> JoinStep<Row, J, Lateral<InnerJoin>, On> for Scoped<M, Scope, Used>
where
M: JoinRow<Row, J, InnerJoin>,
J: ScopeEntry,
{
type Marker = Joined<M, Cons<J, Scope>, Used, (), (On, J::Sources)>;
type Row = M::Row;
}
impl<M, Scope, Used, Row, J, On> JoinStep<Row, J, Lateral<LeftJoin>, On> for Scoped<M, Scope, Used>
where
M: JoinRow<Row, J, LeftJoin>,
J: ScopeEntry,
{
type Marker = Joined<M, Cons<OuterJoined<J>, Scope>, Used, (), (On, J::Sources)>;
type Row = M::Row;
}
pub type FromMarker<M, Source> = Scoped<M, Cons<Source, Nil>, <Source as ScopeEntry>::Sources>;