qraft-core 0.1.2

Core type system, query model, decoding, and SQL lowering primitives for qraft.
Documentation
use std::marker::PhantomData;

use crate::{
    Query,
    builder::{delete::Delete, update::UpdateFrom},
    query::{LowerProject, Table, WithSelect, select},
};

/// A lowered common table expression attached to a statement.
#[derive(Debug, Clone)]
pub struct Cte {
    pub(crate) name: &'static str,
    pub(crate) columns: Vec<&'static str>,
    pub(crate) query: Query,
    pub(crate) recursive: bool,
}

/// A typed CTE definition before recursive mode has been selected.
#[derive(Debug, Clone)]
pub struct CteDefinition<T> {
    name: &'static str,
    columns: &'static [&'static str],
    query: Query,
    marker: PhantomData<T>,
}

/// Converts one or more typed CTE definitions into lowered CTEs.
pub trait IntoCtes {
    #[doc(hidden)]
    fn into_ctes(self, recursive: bool) -> Vec<Cte>;
}

/// A top-level `with (...)` wrapper that can start statements.
#[derive(Debug, Clone)]
pub struct WithClause {
    ctes: Vec<Cte>,
}

impl<T> CteDefinition<T> {
    /// Creates a typed CTE definition from a derived schema module.
    pub fn new<Q>(name: &'static str, columns: &'static [&'static str], query: Q) -> Self
    where
        Q: Into<Query>,
    {
        Self {
            name,
            columns,
            query: query.into(),
            marker: PhantomData,
        }
    }

    fn into_cte(self, recursive: bool) -> Cte {
        Cte {
            name: self.name,
            columns: self.columns.to_vec(),
            query: self.query,
            recursive,
        }
    }
}

impl<T> IntoCtes for CteDefinition<T> {
    fn into_ctes(self, recursive: bool) -> Vec<Cte> {
        vec![self.into_cte(recursive)]
    }
}

impl IntoCtes for Cte {
    fn into_ctes(mut self, recursive: bool) -> Vec<Cte> {
        self.recursive |= recursive;
        vec![self]
    }
}

impl IntoCtes for Vec<Cte> {
    fn into_ctes(mut self, recursive: bool) -> Vec<Cte> {
        if recursive {
            for cte in &mut self {
                cte.recursive = true;
            }
        }
        self
    }
}

impl<T> IntoCtes for Vec<CteDefinition<T>> {
    fn into_ctes(self, recursive: bool) -> Vec<Cte> {
        self.into_iter()
            .map(|cte| cte.into_cte(recursive))
            .collect()
    }
}

macro_rules! impl_into_ctes_tuple {
    ($($T:ident),+) => {
        impl<$($T,)+> IntoCtes for ($($T,)+)
        where
            $($T: IntoCtes,)+
        {
            fn into_ctes(self, recursive: bool) -> Vec<Cte> {
                #[allow(non_snake_case)]
                let ($($T,)+) = self;
                let mut out = Vec::new();
                $(
                    out.extend($T.into_ctes(recursive));
                )+
                out
            }
        }
    };
}

crate::impl_for_all_tuples!(impl_into_ctes_tuple);

/// Starts a `with (...)` wrapper from one or more typed CTE definitions.
pub fn with<C>(ctes: C) -> WithClause
where
    C: IntoCtes,
{
    WithClause {
        ctes: ctes.into_ctes(false),
    }
}

/// Starts a `with recursive (...)` wrapper from one or more typed CTE definitions.
pub fn with_recursive<C>(ctes: C) -> WithClause
where
    C: IntoCtes,
{
    WithClause {
        ctes: ctes.into_ctes(true),
    }
}

impl WithClause {
    /// Starts a select statement with the attached CTEs.
    pub fn select<P>(self, project: P) -> WithSelect<P>
    where
        P: LowerProject,
    {
        let mut ctes = self.ctes.into_iter();
        let first = ctes.next().expect("with() requires at least one CTE");
        let mut stmt = select(project).with(first);
        for cte in ctes {
            stmt = stmt.with(cte);
        }
        stmt
    }

    /// Starts an update statement with the attached CTEs.
    pub fn update<M, T>(self, table: T) -> UpdateFrom<M>
    where
        T: crate::builder::update::UpdateTable<M>,
    {
        let mut stmt = crate::builder::update::update(table);
        for cte in self.ctes {
            stmt = stmt.with(cte);
        }
        stmt
    }

    /// Starts a delete statement with the attached CTEs.
    pub fn delete<T>(self, table: Table<T>) -> Delete<T> {
        let mut stmt = crate::builder::delete::delete_from(table);
        for cte in self.ctes {
            stmt = stmt.with(cte);
        }
        stmt
    }
}