use std::{marker::PhantomData, rc::Rc};
use crate::{Expr, lower, value::DbTyp};
pub(crate) struct Cacher {
pub(crate) columns: Vec<Rc<lower::Expr>>,
}
impl Cacher {
pub(crate) fn new() -> Self {
Self {
columns: Vec::new(),
}
}
}
pub struct Cached<T> {
pub(crate) idx: usize,
_p: PhantomData<T>,
}
impl<T> Clone for Cached<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Cached<T> {}
impl Cacher {
pub(crate) fn cache_erased(&mut self, val: Rc<lower::Expr>) -> usize {
let idx = self.columns.len();
self.columns.push(val);
idx
}
}
#[derive(Clone, Copy)]
pub(crate) struct Row<'x> {
pub(crate) row: &'x rusqlite::Row<'x>,
pub(crate) fields: &'x [String],
}
impl<'x> Row<'x> {
pub(crate) fn new(row: &'x rusqlite::Row<'x>, fields: &'x [String]) -> Self {
Self { row, fields }
}
pub fn get<T: DbTyp>(&self, val: Cached<T>) -> T {
let idx = &self.fields[val.idx];
T::from_sql(self.row.get_ref_unwrap(idx.as_str())).unwrap()
}
}
pub(crate) trait Prepared {
type Out;
fn call(&mut self, row: Row<'_>) -> Self::Out;
}
pub struct Select<'columns, S, Out> {
pub(crate) inner: DynSelectImpl<Out>,
pub(crate) _p: PhantomData<&'columns ()>,
pub(crate) _p2: PhantomData<S>,
}
impl<'columns, S, Out: 'static> Select<'columns, S, Out> {
pub fn map<T>(self, f: impl 'static + FnMut(Out) -> T) -> Select<'columns, S, T> {
Select::new(MapImpl {
dummy: self.inner,
func: f,
})
}
}
pub struct DynSelectImpl<Out> {
inner: Box<dyn FnOnce(&mut Cacher) -> DynPrepared<Out>>,
}
impl<Out> SelectImpl for DynSelectImpl<Out> {
type Out = Out;
type Prepared = DynPrepared<Out>;
fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
(self.inner)(cacher)
}
}
pub struct DynPrepared<Out> {
inner: Box<dyn Prepared<Out = Out>>,
}
impl<Out> Prepared for DynPrepared<Out> {
type Out = Out;
fn call(&mut self, row: Row<'_>) -> Self::Out {
self.inner.call(row)
}
}
impl<S, Out> Select<'_, S, Out> {
pub(crate) fn new(val: impl 'static + SelectImpl<Out = Out>) -> Self {
Self {
inner: DynSelectImpl {
inner: Box::new(|cacher| DynPrepared {
inner: Box::new(val.prepare(cacher)),
}),
},
_p: PhantomData,
_p2: PhantomData,
}
}
}
impl<'columns, S, Out: 'static> IntoSelect<'columns, S> for Select<'columns, S, Out> {
type Out = Out;
fn into_select(self) -> Select<'columns, S, Self::Out> {
self
}
}
pub trait SelectImpl {
type Out;
#[doc(hidden)]
type Prepared: Prepared<Out = Self::Out>;
#[doc(hidden)]
fn prepare(self, cacher: &mut Cacher) -> Self::Prepared;
}
pub trait IntoSelect<'columns, S>: Sized {
type Out: 'static;
fn into_select(self) -> Select<'columns, S, Self::Out>;
}
pub struct MapImpl<D, F> {
dummy: D,
func: F,
}
impl<D, F, O> SelectImpl for MapImpl<D, F>
where
D: SelectImpl,
F: FnMut(D::Out) -> O,
{
type Out = O;
type Prepared = MapPrepared<D::Prepared, F>;
fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
MapPrepared {
inner: self.dummy.prepare(cacher),
map: self.func,
}
}
}
pub struct MapPrepared<X, M> {
inner: X,
map: M,
}
impl<X, M, Out> Prepared for MapPrepared<X, M>
where
X: Prepared,
M: FnMut(X::Out) -> Out,
{
type Out = Out;
fn call(&mut self, row: Row<'_>) -> Self::Out {
(self.map)(self.inner.call(row))
}
}
impl Prepared for () {
type Out = ();
fn call(&mut self, _row: Row<'_>) -> Self::Out {}
}
impl SelectImpl for () {
type Out = ();
type Prepared = ();
fn prepare(self, _cacher: &mut Cacher) -> Self::Prepared {}
}
impl<'columns, S> IntoSelect<'columns, S> for () {
type Out = ();
fn into_select(self) -> Select<'columns, S, Self::Out> {
Select::new(())
}
}
impl<T: DbTyp> Prepared for Cached<T> {
type Out = T;
fn call(&mut self, row: Row<'_>) -> Self::Out {
row.get(*self)
}
}
pub struct ColumnImpl<Out> {
pub(crate) expr: Rc<lower::Expr>,
pub(crate) _p: PhantomData<Out>,
}
impl<Out: DbTyp> SelectImpl for ColumnImpl<Out> {
type Out = Out;
type Prepared = Cached<Out>;
fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
Cached {
idx: cacher.cache_erased(self.expr),
_p: PhantomData,
}
}
}
impl<'columns, S, T> IntoSelect<'columns, S> for Expr<'columns, S, T>
where
T: DbTyp,
{
type Out = T;
fn into_select(self) -> Select<'columns, S, Self::Out> {
Select::new(ColumnImpl {
expr: self.inner,
_p: PhantomData,
})
}
}
impl<'columns, S, T> IntoSelect<'columns, S> for &T
where
T: IntoSelect<'columns, S> + Clone,
{
type Out = T::Out;
fn into_select(self) -> Select<'columns, S, Self::Out> {
T::clone(self).into_select()
}
}
impl<A, B> Prepared for (A, B)
where
A: Prepared,
B: Prepared,
{
type Out = (A::Out, B::Out);
fn call(&mut self, row: Row<'_>) -> Self::Out {
(self.0.call(row), self.1.call(row))
}
}
impl<A, B> SelectImpl for (A, B)
where
A: SelectImpl,
B: SelectImpl,
{
type Out = (A::Out, B::Out);
type Prepared = (A::Prepared, B::Prepared);
fn prepare(self, cacher: &mut Cacher) -> Self::Prepared {
let prepared_a = self.0.prepare(cacher);
let prepared_b = self.1.prepare(cacher);
(prepared_a, prepared_b)
}
}
impl<'columns, S, A, B> IntoSelect<'columns, S> for (A, B)
where
A: IntoSelect<'columns, S>,
B: IntoSelect<'columns, S>,
{
type Out = (A::Out, B::Out);
fn into_select(self) -> Select<'columns, S, Self::Out> {
Select::new((self.0.into_select().inner, self.1.into_select().inner))
}
}
#[cfg(test)]
#[allow(unused)]
mod tests {
use crate::IntoExpr;
use super::*;
struct User {
a: i64,
b: String,
}
struct UserSelect<A, B> {
a: A,
b: B,
}
impl<'columns, S, A, B> IntoSelect<'columns, S> for UserSelect<A, B>
where
A: IntoExpr<'columns, S, Typ = i64>,
B: IntoExpr<'columns, S, Typ = String>,
{
type Out = User;
fn into_select(self) -> Select<'columns, S, Self::Out> {
(self.a.into_expr(), self.b.into_expr())
.into_select()
.map((|(a, b)| User { a, b }) as fn((i64, String)) -> User)
.into_select()
}
}
}